Runbook

How to Bulk Transition Jira Issues via CLI

Move hundreds of Jira issues through workflow states in a single command. Preview changes with dry-run mode before committing.

What This Does

This runbook transitions all Jira issues matching a JQL query to a new workflow state. It supports dry-run previews so you can verify which issues will be affected before making changes. Concurrency is configurable, and optional comments can be added to each transitioned issue.

Prerequisites

Quick Start

# Preview which issues will be transitioned
./bulk-transition.sh --jql "project = DEV AND status = 'In Progress'" \
  --transition "Done" --dry-run

# Execute the transition against production
./bulk-transition.sh --jql "project = DEV AND status = 'In Progress'" \
  --transition "Done" --profile prod --execute

Full Runbook Script

bulk-transition.sh

#!/bin/bash
# Jira Bulk Transition Script
#
# Bulk transition issues matching a JQL query through workflow states.
# Supports dry-run mode and progress tracking.
#
# Usage:
#   # Dry run (preview affected issues)
#   ./bulk-transition.sh --jql "project = PROJ AND status = 'In Progress'" \
#                        --transition "Done" --dry-run
#
#   # Execute transition
#   ./bulk-transition.sh --jql "project = PROJ AND status = 'In Progress'" \
#                        --transition "Done" --profile prod
#
# Requirements:
#   - atlassian-cli installed and configured

set -euo pipefail

# Configuration
JQL=""
TRANSITION=""
PROFILE="default"
DRY_RUN=true
CONCURRENCY=4
COMMENT=""

# 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
            --jql)
                JQL="$2"
                shift 2
                ;;
            --transition)
                TRANSITION="$2"
                shift 2
                ;;
            --profile)
                PROFILE="$2"
                shift 2
                ;;
            --comment)
                COMMENT="$2"
                shift 2
                ;;
            --concurrency)
                CONCURRENCY="$2"
                shift 2
                ;;
            --execute)
                DRY_RUN=false
                shift
                ;;
            --dry-run)
                DRY_RUN=true
                shift
                ;;
            *)
                error "Unknown option: $1"
                exit 1
                ;;
        esac
    done

    if [ -z "$JQL" ] || [ -z "$TRANSITION" ]; then
        error "Both --jql and --transition are required"
        exit 1
    fi
}

# Preview affected issues
preview_issues() {
    log "Finding issues matching JQL..."
    log "Query: $JQL"

    local issues
    issues=$(atlassian-cli jira 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 transition"

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

    return 0
}

# Execute bulk transition
execute_transition() {
    if [ "$DRY_RUN" = "true" ]; then
        warn "[DRY-RUN] Would transition issues to: $TRANSITION"
        return
    fi

    log "Executing bulk transition to: $TRANSITION"

    local args=(
        "jira" "bulk" "transition"
        "--profile" "$PROFILE"
        "--jql" "$JQL"
        "--transition" "$TRANSITION"
        "--concurrency" "$CONCURRENCY"
    )

    if [ -n "$COMMENT" ]; then
        args+=("--comment" "$COMMENT")
    fi

    atlassian-cli "${args[@]}"

    log "Transition complete"
}

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

    log "Jira Bulk Transition"
    log "Transition: $TRANSITION | Profile: $PROFILE"

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

    if ! preview_issues; then
        exit 0
    fi

    if [ "$DRY_RUN" = "false" ]; then
        warn "This will transition matching issues to: $TRANSITION"
        read -rp "Type 'YES' to confirm: " confirm

        if [ "$confirm" != "YES" ]; then
            log "Transition cancelled"
            exit 0
        fi
    fi

    execute_transition

    log "Bulk transition complete"
}

main "$@"

How It Works

1

Parse arguments. The script reads command-line flags for the JQL query, target transition state, profile, concurrency level, and optional comment. Both --jql and --transition are required.

2

Preview matching issues. A jira search call finds all issues matching your JQL. The first 10 results are displayed with their key, summary, and current status so you can verify the query is correct.

3

Dry-run gate. By default the script runs in dry-run mode and stops after preview. Pass --execute to enable actual transitions, which triggers a confirmation prompt requiring you to type YES.

4

Bulk transition. The CLI's bulk transition command processes all matched issues concurrently (default 4 parallel requests). It handles rate-limiting and retries automatically.

5

Optional comment. Use --comment to add a note to every transitioned issue, useful for audit trails or linking to a ticket that triggered the bulk change.

Related Runbooks

Copied!