ランブック

ターミナルから Jira プロジェクトの整理を自動化する

解決済みの古い課題をアーカイブし、分類用のラベルを付け、CSV の整理レポートを生成します。これらをすべて 1 つのスクリプトから実行できます。

この処理の内容

このランブックは、経過日数とステータスをもとに放置された解決済みの Jira 課題を探し、見つかったものすべての CSV レポートを生成したうえで、必要に応じてラベル付けやコンポーネントの更新を一括で行います。プロジェクトを定期的に整理するためのもので、毎月実行すればバックログをきれいに保てます。

前提条件

クイックスタート

# Preview issues resolved more than 180 days ago
./project-cleanup.sh --project PROJ --days 180 --dry-run

# Label old issues as archived
./project-cleanup.sh --project PROJ --days 180 --label "archived"

# Update component for all Done issues
./project-cleanup.sh --project PROJ --status Done --component "Legacy"

ランブックスクリプト全文

project-cleanup.sh

#!/bin/bash
# Jira Project Cleanup Script
#
# Performs bulk cleanup operations on Jira issues:
#   - Archive old resolved issues
#   - Update labels for categorization
#   - Update components or fix versions
#
# Usage:
#   # Dry run to preview changes
#   ./project-cleanup.sh --project PROJ --days 180 --dry-run
#
#   # Add archived label to old issues
#   ./project-cleanup.sh --project PROJ --days 180 --label archived
#
#   # Bulk update component
#   ./project-cleanup.sh --project PROJ --status Done --component Legacy
#
# Requirements:
#   - atlassian-cli installed and configured

set -euo pipefail

# Configuration
PROJECT=""
DAYS_OLD=""
STATUS=""
LABEL=""
COMPONENT=""
PROFILE="default"
DRY_RUN=true
CONCURRENCY=4

# Colors
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
NC='\033[0m'

log() {
    echo -e "${GREEN}[INFO]${NC} $*"
}

warn() {
    echo -e "${YELLOW}[WARN]${NC} $*"
}

error() {
    echo -e "${RED}[ERROR]${NC} $*" >&2
}

# Parse arguments
parse_args() {
    while [[ $# -gt 0 ]]; do
        case $1 in
            --project)
                PROJECT="$2"
                shift 2
                ;;
            --days)
                DAYS_OLD="$2"
                shift 2
                ;;
            --status)
                STATUS="$2"
                shift 2
                ;;
            --label)
                LABEL="$2"
                DRY_RUN=false
                shift 2
                ;;
            --component)
                COMPONENT="$2"
                DRY_RUN=false
                shift 2
                ;;
            --profile)
                PROFILE="$2"
                shift 2
                ;;
            --dry-run)
                DRY_RUN=true
                shift
                ;;
            *)
                error "Unknown option: $1"
                exit 1
                ;;
        esac
    done

    if [ -z "$PROJECT" ]; then
        error "Project key required (--project PROJECT)"
        exit 1
    fi
}

# Build JQL query
build_jql() {
    local jql="project = $PROJECT"

    if [ -n "$DAYS_OLD" ]; then
        local cutoff_date
        cutoff_date=$(date -u -d "$DAYS_OLD days ago" +%Y-%m-%d 2>/dev/null || \
                      date -u -v-${DAYS_OLD}d +%Y-%m-%d)  # macOS fallback
        jql="$jql AND resolved < -${DAYS_OLD}d"
    fi

    if [ -n "$STATUS" ]; then
        jql="$jql AND status = \"$STATUS\""
    fi

    echo "$jql"
}

# Preview affected issues
preview_issues() {
    local jql="$1"

    log "Finding issues matching criteria..."
    log "JQL: $jql"

    local issues
    issues=$(atlassian-cli jira issue search \
        --profile "$PROFILE" \
        --jql "$jql" \
        --output json 2>/dev/null || echo "[]")

    local count
    count=$(echo "$issues" | jq '. | length')

    if [ "$count" -eq 0 ]; then
        warn "No issues found matching criteria"
        return 1
    fi

    log "Found $count issues to process"

    # Show sample issues
    echo ""
    echo "Sample issues (first 10):"
    echo "$issues" | jq -r '.[:10][] | "  - \(.key): \(.fields.summary) (\(.fields.status.name))"'
    echo ""

    return 0
}

# Add label to issues
add_label() {
    local jql="$1"
    local label="$2"

    if [ "$DRY_RUN" = "true" ]; then
        warn "[DRY-RUN] Would add label '$label' to matching issues"
        return
    fi

    log "Adding label '$label' to issues..."

    atlassian-cli jira bulk label \
        --profile "$PROFILE" \
        --jql "$jql" \
        --labels "$label" \
        --concurrency "$CONCURRENCY"

    log "Labels added successfully"
}

# Update component
update_component() {
    local jql="$1"
    local component="$2"

    if [ "$DRY_RUN" = "true" ]; then
        warn "[DRY-RUN] Would update component to '$component' for matching issues"
        return
    fi

    warn "Bulk component update requires custom implementation"
    warn "Use: atlassian-cli jira issue update <key> --component \"$component\""
}

# Generate cleanup report
generate_report() {
    local jql="$1"

    log "Generating cleanup report..."

    local issues
    issues=$(atlassian-cli jira issue search \
        --profile "$PROFILE" \
        --jql "$jql" \
        --output json)

    local report_file="cleanup_report_$(date +%Y%m%d_%H%M%S).csv"

    echo "Issue Key,Summary,Status,Created,Resolved,Labels" > "$report_file"

    echo "$issues" | jq -r '.[] |
        [
            .key,
            (.fields.summary | gsub("\"";"\"\"") ),
            .fields.status.name,
            .fields.created,
            (.fields.resolutiondate // "N/A"),
            ((.fields.labels // []) | join(";"))
        ] | @csv' >> "$report_file"

    log "Report saved: $report_file"
}

# Main execution
main() {
    parse_args "$@"

    log "Jira Project Cleanup"
    log "Project: $PROJECT | Profile: $PROFILE"

    if [ "$DRY_RUN" = "true" ]; then
        warn "DRY-RUN MODE: No changes will be made"
    fi

    local jql
    jql=$(build_jql)

    if ! preview_issues "$jql"; then
        exit 0
    fi

    # Generate report
    generate_report "$jql"

    # Execute operations
    if [ -n "$LABEL" ]; then
        add_label "$jql" "$LABEL"
    fi

    if [ -n "$COMPONENT" ]; then
        update_component "$jql" "$COMPONENT"
    fi

    log "Cleanup complete"
}

main "$@"

仕組み

1

JQL を動的に組み立てる。 スクリプトは、指定したフラグから JQL クエリを組み立てます。--project でプロジェクトキーを指定し、--days で解決から N 日以上経過した課題に絞り込み、--status でステータスの条件を追加します。これらは 1 つのクエリにまとめられます。

2

一致する課題をプレビューする。 変更を加える前に、スクリプトが Jira を検索し、一致した最初の 10 件を課題キー、要約、現在のステータスとあわせて表示します。一致する課題がなければ、そのまま正常に終了します。

3

CSV レポートを生成する。 実行のたびにタイムスタンプ付きの CSV ファイル(cleanup_report_YYYYMMDD_HHMMSS.csv)が作成され、課題キー、要約、ステータス、作成日、解決日、ラベルが記録されます。これが監査証跡になります。

4

ラベルを一括で付ける。 --label を指定すると、スクリプトは CLI の bulk label コマンドを使って、一致したすべての課題に同時にラベルを付けます。よくある使い方は、古い課題に archived を付けることです。

5

コンポーネントの更新。 --component フラグはコンポーネントの付け替えを対象とします。コンポーネントの一括更新には課題ごとの API 呼び出しが必要なため、スクリプトは自分のワークフローに組み込むためのコマンドのひな形を提示します。

関連ランブック

コピーしました