Defect tickets linked to quality gate failures in CI pipelines do not auto-transition from Open to In Progress when Jenkins reruns

Our Jira 9 defect-tracking project is linked to Jenkins pipelines that run SonarQube quality gate checks. When a quality gate fails, we create a defect ticket with the issue key extracted from the Git branch name (e.g., bugfix/DEF-456-memory-leak).

The problem: when we rerun the Jenkins pipeline to verify fixes, the defect ticket should automatically transition from “Open” to “In Progress” status, but it stays in “Open”. We’re using Jenkins scripted pipeline with calls to Jira’s REST transition endpoint:

sh "curl -X POST ${JIRA_URL}/rest/api/2/issue/${ISSUE_KEY}/transitions \
     -H 'Content-Type: application/json' \
     -d '{transition: {id: 21}}'"

The curl command executes without errors, but the defect workflow doesn’t update. We’re debating whether to use Jira Automation rules to listen to Jenkins events instead of direct API calls from CI, but we’re not sure which approach is more reliable for parsing issue keys from branches and triggering transitions based on build status.

Are you extracting the issue key correctly from the branch name? Jenkins environment variables can be tricky-if you’re using ${GIT_BRANCH}, it might include the remote prefix like origin/bugfix/DEF-456-memory-leak. Use a regex to strip everything except the issue key. Also, SonarQube quality gate status is available as a webhook event-consider using that to trigger Jira transitions instead of relying on Jenkins to make the API call.

I recommend using Jira Automation instead of direct API calls from Jenkins. Create an automation rule that triggers on “Build status changed” (requires Jenkins integration with Jira), filters for builds on branches matching DEF-*, and transitions the issue. This way, you don’t have to manage authentication tokens in Jenkins, and the automation handles edge cases like issue key parsing failures or invalid transitions.

We use a similar setup and found that calling Jira’s REST API directly from Jenkins is fragile. Instead, we implemented a hybrid approach:

Jenkins pipeline:

  1. Extract issue key from branch name using regex
  2. Pass issue key and build status to a webhook endpoint
  3. Let Jira Automation handle the transition logic

This decouples Jenkins from Jira workflow details and makes it easier to adjust transition rules without modifying pipelines.

Your curl command is missing authentication headers. Jira’s REST API requires either Basic Auth or a bearer token. Without proper auth, the API returns a 401 or 403, which your shell script might be ignoring. Add authentication to your curl command and check the HTTP response code to catch errors. Also verify that the transition ID 21 is correct for your workflow-transition IDs are workflow-specific and might differ between projects.

Here’s a comprehensive solution that addresses all the issues:

Problem 1: Missing authentication in curl command Your curl command needs proper Jira authentication. Use a Jenkins credential to store a Jira API token:

withCredentials([string(credentialsId: 'jira-api-token', variable: 'JIRA_TOKEN')]) {
    sh """
        curl -X POST ${JIRA_URL}/rest/api/2/issue/${ISSUE_KEY}/transitions \
        -H 'Authorization: Bearer ${JIRA_TOKEN}' \
        -H 'Content-Type: application/json' \
        -d '{"transition": {"id": "21"}}'
    """
}

Problem 2: Incorrect transition ID Transition IDs are workflow-specific. To find the correct ID, query Jira’s transitions endpoint:

GET ${JIRA_URL}/rest/api/2/issue/${ISSUE_KEY}/transitions

This returns available transitions for the issue. Look for the transition named “Start Progress” or “In Progress” and use its ID.

Problem 3: Issue key extraction from branch name Use a robust regex to extract the issue key from Git branch names:

def branchName = env.GIT_BRANCH.replaceAll('origin/', '')
def matcher = (branchName =~ /DEF-\d+/)
if (matcher.find()) {
    def issueKey = matcher.group(0)
    echo "Found issue key: ${issueKey}"
} else {
    echo "No issue key found in branch name"
    return
}

This handles branches like origin/bugfix/DEF-456-memory-leak and extracts DEF-456.

Problem 4: SonarQube quality gate integration Instead of transitioning on every Jenkins rerun, transition based on SonarQube quality gate status:

stage('SonarQube Analysis') {
    steps {
        withSonarQubeEnv('SonarQube') {
            sh 'mvn sonar:sonar'
        }
    }
}

stage('Quality Gate') {
    steps {
        timeout(time: 5, unit: 'MINUTES') {
            waitForQualityGate abortPipeline: false
        }
        script {
            def qg = waitForQualityGate()
            if (qg.status == 'OK') {
                transitionJiraIssue(issueKey, '21')  // In Progress
            } else {
                transitionJiraIssue(issueKey, '31')  // Failed
            }
        }
    }
}

Problem 5: Jira Automation vs direct API For reliability, use a combination:

  • Direct API from Jenkins: For immediate feedback during pipeline execution
  • Jira Automation: As a fallback to handle missed transitions or complex workflow rules

Create a Jira Automation rule:

  • Trigger: “Build status changed”
  • Condition: {{issue.key}} matches branch name AND build status is “IN_PROGRESS”
  • Action: Transition issue to “In Progress”

This ensures transitions happen even if Jenkins API calls fail.

Complete Jenkins pipeline function:

def transitionJiraIssue(issueKey, transitionId) {
    withCredentials([string(credentialsId: 'jira-api-token', variable: 'JIRA_TOKEN')]) {
        def response = sh(
            script: """
                curl -s -w "\n%{http_code}" -X POST \
                ${JIRA_URL}/rest/api/2/issue/${issueKey}/transitions \
                -H 'Authorization: Bearer ${JIRA_TOKEN}' \
                -H 'Content-Type: application/json' \
                -d '{"transition": {"id": "${transitionId}"}}'
            """,
            returnStdout: true
        ).trim()

        def lines = response.split(' ')
        def httpCode = lines[-1]

        if (httpCode == '204') {
            echo "Issue ${issueKey} transitioned successfully"
        } else {
            error "Failed to transition issue ${issueKey}. HTTP code: ${httpCode}"
        }
    }
}

Key points:

  1. Always check HTTP response codes to catch API failures
  2. Use Jenkins credentials for secure token storage
  3. Extract issue keys with robust regex patterns
  4. Verify transition IDs for your specific workflow
  5. Handle SonarQube quality gate status explicitly
  6. Implement both API calls and Jira Automation for redundancy

Permissions checklist:

  • Jenkins service account needs “Transition Issues” permission in Jira
  • Workflow transition must not have conditions that block automated transitions (e.g., required fields)
  • API token must have appropriate scopes (read/write issues)

This approach has been stable for us across 100+ microservices with SonarQube quality gates integrated into Jira defect tracking.