From 4b4e616cb9e598915bcde5b4fce23b2b0f52cf67 Mon Sep 17 00:00:00 2001 From: Patrick Dodgen Date: Thu, 18 Dec 2025 14:21:06 -0700 Subject: [PATCH 1/9] feat: force Jira linking to Github changes --- .github/workflows/jira-ticket-check.yaml | 323 +++++++++++++++++++++++ 1 file changed, 323 insertions(+) create mode 100644 .github/workflows/jira-ticket-check.yaml diff --git a/.github/workflows/jira-ticket-check.yaml b/.github/workflows/jira-ticket-check.yaml new file mode 100644 index 0000000..72d9c30 --- /dev/null +++ b/.github/workflows/jira-ticket-check.yaml @@ -0,0 +1,323 @@ +name: Jira Ticket Check +on: + pull_request: + types: [opened, synchronize, reopened, edited] + workflow_call: + inputs: + jira-project-pattern: + required: false + type: string + description: 'Regex pattern for Jira project keys (e.g., "DEVEX|DEV|PROJ")' + default: '[A-Z]+' + check-commits: + required: false + type: boolean + description: 'Whether to check commit messages for Jira tickets' + default: true + fail-on-missing: + required: false + type: boolean + description: 'Whether to fail the workflow if no Jira ticket is found' + default: true + verify-ticket-exists: + required: false + type: boolean + description: 'Whether to verify the ticket exists in Jira via API' + default: true + jira-instance: + required: false + type: string + description: 'Jira instance URL (e.g., revolutionparts.atlassian.net)' + default: 'revolutionparts.atlassian.net' + secrets: + jira-email: + required: false + description: 'Jira account email for API authentication' + jira-api-token: + required: false + description: 'Jira API token for authentication' + +jobs: + check-jira-ticket: + name: Verify Jira Ticket Link + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Extract Jira ticket from branch name + id: branch-check + run: | + BRANCH_NAME="${{ github.head_ref }}" + JIRA_PROJECT_PATTERN="${{ inputs.jira-project-pattern }}" + if [ -z "$JIRA_PROJECT_PATTERN" ]; then + JIRA_PROJECT_PATTERN="[A-Z]+" + fi + JIRA_PATTERN="${JIRA_PROJECT_PATTERN}-[0-9]+" + + if [ -z "$BRANCH_NAME" ]; then + echo "ticket=" >> $GITHUB_OUTPUT + echo "found_in=" >> $GITHUB_OUTPUT + exit 0 + fi + + if echo "$BRANCH_NAME" | grep -qiE "$JIRA_PATTERN"; then + TICKET=$(echo "$BRANCH_NAME" | grep -oiE "$JIRA_PATTERN" | head -1) + echo "ticket=$TICKET" >> $GITHUB_OUTPUT + echo "found_in=branch" >> $GITHUB_OUTPUT + echo "✅ Found Jira ticket in branch name: $TICKET" + exit 0 + fi + + echo "ticket=" >> $GITHUB_OUTPUT + echo "found_in=" >> $GITHUB_OUTPUT + + - name: Extract Jira ticket from PR title + id: pr-title-check + if: steps.branch-check.outputs.ticket == '' + run: | + PR_TITLE="${{ github.event.pull_request.title }}" + JIRA_PROJECT_PATTERN="${{ inputs.jira-project-pattern }}" + if [ -z "$JIRA_PROJECT_PATTERN" ]; then + JIRA_PROJECT_PATTERN="[A-Z]+" + fi + JIRA_PATTERN="${JIRA_PROJECT_PATTERN}-[0-9]+" + + if [ -z "$PR_TITLE" ]; then + echo "ticket=" >> $GITHUB_OUTPUT + echo "found_in=" >> $GITHUB_OUTPUT + exit 0 + fi + + if echo "$PR_TITLE" | grep -qiE "$JIRA_PATTERN"; then + TICKET=$(echo "$PR_TITLE" | grep -oiE "$JIRA_PATTERN" | head -1) + echo "ticket=$TICKET" >> $GITHUB_OUTPUT + echo "found_in=pr_title" >> $GITHUB_OUTPUT + echo "✅ Found Jira ticket in PR title: $TICKET" + exit 0 + fi + + echo "ticket=" >> $GITHUB_OUTPUT + echo "found_in=" >> $GITHUB_OUTPUT + + - name: Extract Jira ticket from PR description + id: pr-description-check + if: steps.pr-title-check.outputs.ticket == '' + run: | + PR_BODY="${{ github.event.pull_request.body }}" + JIRA_PROJECT_PATTERN="${{ inputs.jira-project-pattern }}" + if [ -z "$JIRA_PROJECT_PATTERN" ]; then + JIRA_PROJECT_PATTERN="[A-Z]+" + fi + JIRA_PATTERN="${JIRA_PROJECT_PATTERN}-[0-9]+" + + if [ -z "$PR_BODY" ]; then + echo "ticket=" >> $GITHUB_OUTPUT + echo "found_in=" >> $GITHUB_OUTPUT + exit 0 + fi + + # Check for ticket in PR body/description + if echo "$PR_BODY" | grep -qiE "$JIRA_PATTERN"; then + TICKET=$(echo "$PR_BODY" | grep -oiE "$JIRA_PATTERN" | head -1) + echo "ticket=$TICKET" >> $GITHUB_OUTPUT + echo "found_in=pr_description" >> $GITHUB_OUTPUT + echo "✅ Found Jira ticket in PR description: $TICKET" + exit 0 + fi + + echo "ticket=" >> $GITHUB_OUTPUT + echo "found_in=" >> $GITHUB_OUTPUT + + - name: Extract Jira ticket from commits + id: commit-check + if: steps.pr-description-check.outputs.ticket == '' + run: | + CHECK_COMMITS="${{ inputs.check-commits }}" + if [ "$CHECK_COMMITS" != "true" ]; then + echo "ticket=" >> $GITHUB_OUTPUT + echo "found_in=" >> $GITHUB_OUTPUT + exit 0 + fi + + JIRA_PROJECT_PATTERN="${{ inputs.jira-project-pattern }}" + if [ -z "$JIRA_PROJECT_PATTERN" ]; then + JIRA_PROJECT_PATTERN="[A-Z]+" + fi + JIRA_PATTERN="${JIRA_PROJECT_PATTERN}-[0-9]+" + BASE_SHA="${{ github.event.pull_request.base.sha }}" + HEAD_SHA="${{ github.event.pull_request.head.sha }}" + + if [ -z "$BASE_SHA" ] || [ -z "$HEAD_SHA" ]; then + echo "ticket=" >> $GITHUB_OUTPUT + echo "found_in=" >> $GITHUB_OUTPUT + exit 0 + fi + + # Get all commit messages between base and head + COMMIT_MESSAGES=$(git log --pretty=format:"%s" ${BASE_SHA}..${HEAD_SHA} 2>/dev/null || echo "") + + if [ -n "$COMMIT_MESSAGES" ] && echo "$COMMIT_MESSAGES" | grep -qiE "$JIRA_PATTERN"; then + TICKET=$(echo "$COMMIT_MESSAGES" | grep -oiE "$JIRA_PATTERN" | head -1) + echo "ticket=$TICKET" >> $GITHUB_OUTPUT + echo "found_in=commits" >> $GITHUB_OUTPUT + echo "✅ Found Jira ticket in commit messages: $TICKET" + exit 0 + fi + + echo "ticket=" >> $GITHUB_OUTPUT + echo "found_in=" >> $GITHUB_OUTPUT + + - name: Determine final ticket + id: final-ticket + run: | + if [ -n "${{ steps.branch-check.outputs.ticket }}" ]; then + echo "ticket=${{ steps.branch-check.outputs.ticket }}" >> $GITHUB_OUTPUT + echo "found_in=${{ steps.branch-check.outputs.found_in }}" >> $GITHUB_OUTPUT + elif [ -n "${{ steps.pr-title-check.outputs.ticket }}" ]; then + echo "ticket=${{ steps.pr-title-check.outputs.ticket }}" >> $GITHUB_OUTPUT + echo "found_in=${{ steps.pr-title-check.outputs.found_in }}" >> $GITHUB_OUTPUT + elif [ -n "${{ steps.pr-description-check.outputs.ticket }}" ]; then + echo "ticket=${{ steps.pr-description-check.outputs.ticket }}" >> $GITHUB_OUTPUT + echo "found_in=${{ steps.pr-description-check.outputs.found_in }}" >> $GITHUB_OUTPUT + elif [ -n "${{ steps.commit-check.outputs.ticket }}" ]; then + echo "ticket=${{ steps.commit-check.outputs.ticket }}" >> $GITHUB_OUTPUT + echo "found_in=${{ steps.commit-check.outputs.found_in }}" >> $GITHUB_OUTPUT + else + echo "ticket=" >> $GITHUB_OUTPUT + echo "found_in=" >> $GITHUB_OUTPUT + fi + + - name: Fail if no Jira ticket found + if: steps.final-ticket.outputs.ticket == '' + run: | + FAIL_ON_MISSING="${{ inputs.fail-on-missing }}" + if [ "$FAIL_ON_MISSING" != "true" ] && [ -n "$FAIL_ON_MISSING" ]; then + echo "⚠️ No Jira ticket found, but fail-on-missing is disabled" + exit 0 + fi + + # Default to true if not specified (for direct PR triggers) + if [ -z "$FAIL_ON_MISSING" ]; then + FAIL_ON_MISSING="true" + fi + + if [ "$FAIL_ON_MISSING" != "true" ]; then + exit 0 + fi + + echo "❌ No Jira ticket found in branch name, PR title, PR description" + CHECK_COMMITS="${{ inputs.check-commits }}" + if [ "$CHECK_COMMITS" == "true" ]; then + echo " or commit messages." + else + echo "." + fi + echo "" + echo "Please ensure one of the following contains a Jira ticket key (e.g., DEVEX-600, DEV-123):" + echo " - Branch name: ${{ github.head_ref }}" + echo " - PR title: ${{ github.event.pull_request.title }}" + echo " - PR description" + if [ "$CHECK_COMMITS" == "true" ]; then + echo " - Commit messages" + fi + echo "" + echo "Jira ticket format: [PROJECT-KEY]-[NUMBER] (e.g., DEVEX-600)" + exit 1 + + - name: Verify ticket exists in Jira + id: verify-ticket + if: steps.final-ticket.outputs.ticket != '' + run: | + VERIFY_TICKET="${{ inputs.verify-ticket-exists }}" + if [ "$VERIFY_TICKET" != "true" ]; then + echo "⚠️ Ticket verification is disabled, skipping API check" + echo "exists=true" >> $GITHUB_OUTPUT + exit 0 + fi + + JIRA_EMAIL="${{ secrets.jira-email }}" + JIRA_TOKEN="${{ secrets.jira-api-token }}" + + if [ -z "$JIRA_EMAIL" ] || [ -z "$JIRA_TOKEN" ]; then + echo "⚠️ Jira credentials not provided, skipping ticket verification" + echo "⚠️ To enable verification, provide jira-email and jira-api-token secrets" + echo "exists=true" >> $GITHUB_OUTPUT + exit 0 + fi + + TICKET="${{ steps.final-ticket.outputs.ticket }}" + JIRA_INSTANCE="${{ inputs.jira-instance }}" + if [ -z "$JIRA_INSTANCE" ]; then + JIRA_INSTANCE="revolutionparts.atlassian.net" + fi + + # Create base64 encoded credentials + AUTH_STRING=$(echo -n "${JIRA_EMAIL}:${JIRA_TOKEN}" | base64) + + echo "🔍 Verifying ticket $TICKET exists in Jira..." + + # Make API request to check if ticket exists + RESPONSE=$(curl -s -w "\n%{http_code}" \ + -X GET \ + -H "Authorization: Basic ${AUTH_STRING}" \ + -H "Accept: application/json" \ + "https://${JIRA_INSTANCE}/rest/api/3/issue/${TICKET}") + + HTTP_CODE=$(echo "$RESPONSE" | tail -n1) + BODY=$(echo "$RESPONSE" | sed '$d') + + if [ "$HTTP_CODE" = "200" ]; then + echo "✅ Ticket $TICKET exists in Jira" + echo "exists=true" >> $GITHUB_OUTPUT + # Extract ticket title if available + TITLE=$(echo "$BODY" | grep -o '"summary":"[^"]*' | head -1 | cut -d'"' -f4 || echo "") + if [ -n "$TITLE" ]; then + echo " Title: $TITLE" + fi + elif [ "$HTTP_CODE" = "404" ]; then + echo "❌ Ticket $TICKET does not exist in Jira" + echo "exists=false" >> $GITHUB_OUTPUT + exit 1 + elif [ "$HTTP_CODE" = "401" ] || [ "$HTTP_CODE" = "403" ]; then + echo "⚠️ Authentication failed or insufficient permissions to verify ticket" + echo "⚠️ HTTP Status: $HTTP_CODE" + echo "⚠️ Continuing without verification..." + echo "exists=true" >> $GITHUB_OUTPUT + else + echo "⚠️ Unexpected response from Jira API" + echo "⚠️ HTTP Status: $HTTP_CODE" + echo "⚠️ Response: $BODY" + echo "⚠️ Continuing without verification..." + echo "exists=true" >> $GITHUB_OUTPUT + fi + + - name: Fail if ticket does not exist + if: steps.final-ticket.outputs.ticket != '' && steps.verify-ticket.outputs.exists == 'false' + run: | + TICKET="${{ steps.final-ticket.outputs.ticket }}" + JIRA_INSTANCE="${{ inputs.jira-instance }}" + if [ -z "$JIRA_INSTANCE" ]; then + JIRA_INSTANCE="revolutionparts.atlassian.net" + fi + echo "❌ Jira ticket $TICKET does not exist in Jira" + echo "" + echo "The ticket key was found in your PR, but it does not exist in Jira." + echo "Please ensure you're using a valid Jira ticket key." + echo "" + echo "Link: https://${JIRA_INSTANCE}/browse/$TICKET" + exit 1 + + - name: Success message + if: steps.final-ticket.outputs.ticket != '' && (steps.verify-ticket.outputs.exists == 'true' || steps.verify-ticket.outputs.exists == '') + run: | + TICKET="${{ steps.final-ticket.outputs.ticket }}" + JIRA_INSTANCE="${{ inputs.jira-instance }}" + if [ -z "$JIRA_INSTANCE" ]; then + JIRA_INSTANCE="revolutionparts.atlassian.net" + fi + echo "✅ Jira ticket found: $TICKET" + echo " Found in: ${{ steps.final-ticket.outputs.found_in }}" + echo " Link: https://${JIRA_INSTANCE}/browse/$TICKET" From e1c9e20210030956aebfd177d6fbf408e42a4e3c Mon Sep 17 00:00:00 2001 From: Patrick Dodgen Date: Thu, 18 Dec 2025 14:25:26 -0700 Subject: [PATCH 2/9] fix: check to verify against Jira --- .github/workflows/jira-ticket-check.yaml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/jira-ticket-check.yaml b/.github/workflows/jira-ticket-check.yaml index 72d9c30..3abbcec 100644 --- a/.github/workflows/jira-ticket-check.yaml +++ b/.github/workflows/jira-ticket-check.yaml @@ -232,7 +232,12 @@ jobs: if: steps.final-ticket.outputs.ticket != '' run: | VERIFY_TICKET="${{ inputs.verify-ticket-exists }}" - if [ "$VERIFY_TICKET" != "true" ]; then + # Default to true if not specified (for direct PR triggers or when default is used) + if [ -z "$VERIFY_TICKET" ]; then + VERIFY_TICKET="true" + fi + + if [ "$VERIFY_TICKET" = "false" ]; then echo "⚠️ Ticket verification is disabled, skipping API check" echo "exists=true" >> $GITHUB_OUTPUT exit 0 From 987f607c7305990c8d683999f8e5575f6f365faa Mon Sep 17 00:00:00 2001 From: Patrick Dodgen Date: Thu, 18 Dec 2025 14:35:56 -0700 Subject: [PATCH 3/9] fix: not checking PR description as that doesn't get linked back to Jira --- .github/workflows/jira-ticket-check.yaml | 31 +----------------------- 1 file changed, 1 insertion(+), 30 deletions(-) diff --git a/.github/workflows/jira-ticket-check.yaml b/.github/workflows/jira-ticket-check.yaml index 3abbcec..0408a39 100644 --- a/.github/workflows/jira-ticket-check.yaml +++ b/.github/workflows/jira-ticket-check.yaml @@ -102,38 +102,9 @@ jobs: echo "ticket=" >> $GITHUB_OUTPUT echo "found_in=" >> $GITHUB_OUTPUT - - name: Extract Jira ticket from PR description - id: pr-description-check - if: steps.pr-title-check.outputs.ticket == '' - run: | - PR_BODY="${{ github.event.pull_request.body }}" - JIRA_PROJECT_PATTERN="${{ inputs.jira-project-pattern }}" - if [ -z "$JIRA_PROJECT_PATTERN" ]; then - JIRA_PROJECT_PATTERN="[A-Z]+" - fi - JIRA_PATTERN="${JIRA_PROJECT_PATTERN}-[0-9]+" - - if [ -z "$PR_BODY" ]; then - echo "ticket=" >> $GITHUB_OUTPUT - echo "found_in=" >> $GITHUB_OUTPUT - exit 0 - fi - - # Check for ticket in PR body/description - if echo "$PR_BODY" | grep -qiE "$JIRA_PATTERN"; then - TICKET=$(echo "$PR_BODY" | grep -oiE "$JIRA_PATTERN" | head -1) - echo "ticket=$TICKET" >> $GITHUB_OUTPUT - echo "found_in=pr_description" >> $GITHUB_OUTPUT - echo "✅ Found Jira ticket in PR description: $TICKET" - exit 0 - fi - - echo "ticket=" >> $GITHUB_OUTPUT - echo "found_in=" >> $GITHUB_OUTPUT - - name: Extract Jira ticket from commits id: commit-check - if: steps.pr-description-check.outputs.ticket == '' + if: steps.pr-title-check.outputs.ticket == '' run: | CHECK_COMMITS="${{ inputs.check-commits }}" if [ "$CHECK_COMMITS" != "true" ]; then From 73a7b252917d3503311824bcfaec6354d9a4fd2f Mon Sep 17 00:00:00 2001 From: Patrick Dodgen Date: Thu, 18 Dec 2025 16:11:44 -0700 Subject: [PATCH 4/9] fix: updating for PR description, ticket case insensitivity, and invalid statuses --- .github/workflows/jira-ticket-check.yaml | 98 +++++++++++++++++++++++- 1 file changed, 95 insertions(+), 3 deletions(-) diff --git a/.github/workflows/jira-ticket-check.yaml b/.github/workflows/jira-ticket-check.yaml index 0408a39..902cf28 100644 --- a/.github/workflows/jira-ticket-check.yaml +++ b/.github/workflows/jira-ticket-check.yaml @@ -53,7 +53,11 @@ jobs: BRANCH_NAME="${{ github.head_ref }}" JIRA_PROJECT_PATTERN="${{ inputs.jira-project-pattern }}" if [ -z "$JIRA_PROJECT_PATTERN" ]; then - JIRA_PROJECT_PATTERN="[A-Z]+" + JIRA_PROJECT_PATTERN="[A-Za-z]+" + else + # Make pattern case-insensitive by adding both cases if it's a simple pattern + # For complex patterns, rely on grep -i flag + JIRA_PROJECT_PATTERN=$(echo "$JIRA_PROJECT_PATTERN" | sed 's/\[A-Z\]\+/[A-Za-z]+/g' | sed 's/\[A-Z\]/[A-Za-z]/g') fi JIRA_PATTERN="${JIRA_PROJECT_PATTERN}-[0-9]+" @@ -63,8 +67,11 @@ jobs: exit 0 fi + # Case-insensitive search if echo "$BRANCH_NAME" | grep -qiE "$JIRA_PATTERN"; then TICKET=$(echo "$BRANCH_NAME" | grep -oiE "$JIRA_PATTERN" | head -1) + # Normalize to uppercase for Jira API (Jira ticket keys are uppercase) + TICKET=$(echo "$TICKET" | tr '[:lower:]' '[:upper:]') echo "ticket=$TICKET" >> $GITHUB_OUTPUT echo "found_in=branch" >> $GITHUB_OUTPUT echo "✅ Found Jira ticket in branch name: $TICKET" @@ -81,7 +88,11 @@ jobs: PR_TITLE="${{ github.event.pull_request.title }}" JIRA_PROJECT_PATTERN="${{ inputs.jira-project-pattern }}" if [ -z "$JIRA_PROJECT_PATTERN" ]; then - JIRA_PROJECT_PATTERN="[A-Z]+" + JIRA_PROJECT_PATTERN="[A-Za-z]+" + else + # Make pattern case-insensitive by adding both cases if it's a simple pattern + # For complex patterns, rely on grep -i flag + JIRA_PROJECT_PATTERN=$(echo "$JIRA_PROJECT_PATTERN" | sed 's/\[A-Z\]\+/[A-Za-z]+/g' | sed 's/\[A-Z\]/[A-Za-z]/g') fi JIRA_PATTERN="${JIRA_PROJECT_PATTERN}-[0-9]+" @@ -91,8 +102,11 @@ jobs: exit 0 fi + # Case-insensitive search if echo "$PR_TITLE" | grep -qiE "$JIRA_PATTERN"; then TICKET=$(echo "$PR_TITLE" | grep -oiE "$JIRA_PATTERN" | head -1) + # Normalize to uppercase for Jira API (Jira ticket keys are uppercase) + TICKET=$(echo "$TICKET" | tr '[:lower:]' '[:upper:]') echo "ticket=$TICKET" >> $GITHUB_OUTPUT echo "found_in=pr_title" >> $GITHUB_OUTPUT echo "✅ Found Jira ticket in PR title: $TICKET" @@ -102,9 +116,44 @@ jobs: echo "ticket=" >> $GITHUB_OUTPUT echo "found_in=" >> $GITHUB_OUTPUT + - name: Extract Jira ticket from PR description + id: pr-description-check + if: steps.pr-title-check.outputs.ticket == '' + run: | + PR_BODY="${{ github.event.pull_request.body }}" + JIRA_PROJECT_PATTERN="${{ inputs.jira-project-pattern }}" + if [ -z "$JIRA_PROJECT_PATTERN" ]; then + JIRA_PROJECT_PATTERN="[A-Za-z]+" + else + # Make pattern case-insensitive by adding both cases if it's a simple pattern + # For complex patterns, rely on grep -i flag + JIRA_PROJECT_PATTERN=$(echo "$JIRA_PROJECT_PATTERN" | sed 's/\[A-Z\]\+/[A-Za-z]+/g' | sed 's/\[A-Z\]/[A-Za-z]/g') + fi + JIRA_PATTERN="${JIRA_PROJECT_PATTERN}-[0-9]+" + + if [ -z "$PR_BODY" ]; then + echo "ticket=" >> $GITHUB_OUTPUT + echo "found_in=" >> $GITHUB_OUTPUT + exit 0 + fi + + # Case-insensitive search + if echo "$PR_BODY" | grep -qiE "$JIRA_PATTERN"; then + TICKET=$(echo "$PR_BODY" | grep -oiE "$JIRA_PATTERN" | head -1) + # Normalize to uppercase for Jira API (Jira ticket keys are uppercase) + TICKET=$(echo "$TICKET" | tr '[:lower:]' '[:upper:]') + echo "ticket=$TICKET" >> $GITHUB_OUTPUT + echo "found_in=pr_description" >> $GITHUB_OUTPUT + echo "✅ Found Jira ticket in PR description: $TICKET" + exit 0 + fi + + echo "ticket=" >> $GITHUB_OUTPUT + echo "found_in=" >> $GITHUB_OUTPUT + - name: Extract Jira ticket from commits id: commit-check - if: steps.pr-title-check.outputs.ticket == '' + if: steps.pr-description-check.outputs.ticket == '' run: | CHECK_COMMITS="${{ inputs.check-commits }}" if [ "$CHECK_COMMITS" != "true" ]; then @@ -130,8 +179,11 @@ jobs: # Get all commit messages between base and head COMMIT_MESSAGES=$(git log --pretty=format:"%s" ${BASE_SHA}..${HEAD_SHA} 2>/dev/null || echo "") + # Case-insensitive search if [ -n "$COMMIT_MESSAGES" ] && echo "$COMMIT_MESSAGES" | grep -qiE "$JIRA_PATTERN"; then TICKET=$(echo "$COMMIT_MESSAGES" | grep -oiE "$JIRA_PATTERN" | head -1) + # Normalize to uppercase for Jira API (Jira ticket keys are uppercase) + TICKET=$(echo "$TICKET" | tr '[:lower:]' '[:upper:]') echo "ticket=$TICKET" >> $GITHUB_OUTPUT echo "found_in=commits" >> $GITHUB_OUTPUT echo "✅ Found Jira ticket in commit messages: $TICKET" @@ -248,11 +300,51 @@ jobs: if [ "$HTTP_CODE" = "200" ]; then echo "✅ Ticket $TICKET exists in Jira" echo "exists=true" >> $GITHUB_OUTPUT + # Extract ticket title if available TITLE=$(echo "$BODY" | grep -o '"summary":"[^"]*' | head -1 | cut -d'"' -f4 || echo "") + + # Extract status from Jira API response (status is nested: fields.status.name) + # Try using jq first (if available), then fall back to grep/sed + if command -v jq >/dev/null 2>&1; then + STATUS=$(echo "$BODY" | jq -r '.fields.status.name // empty' 2>/dev/null || echo "") + fi + + if [ -z "$STATUS" ]; then + # Fallback: extract status using grep/sed pattern matching + # Look for "status" object followed by "name" field + STATUS=$(echo "$BODY" | grep -o '"status"[^}]*"name":"[^"]*' | sed 's/.*"name":"\([^"]*\)".*/\1/' | head -1 || echo "") + fi + + if [ -z "$STATUS" ]; then + # Alternative: look for any "name" field that appears after "status" in the JSON + STATUS=$(echo "$BODY" | sed -n 's/.*"status"[^}]*"name":"\([^"]*\)".*/\1/p' | head -1 || echo "") + fi + + # Normalize status to lowercase for comparison + STATUS_LOWER=$(echo "$STATUS" | tr '[:upper:]' '[:lower:]') + + # Check if status is in the list of invalid statuses + INVALID_STATUSES="backlog|open|closed|done|ready to release" + if echo "$STATUS_LOWER" | grep -qiE "($INVALID_STATUSES)"; then + echo "❌ Ticket $TICKET is in an invalid status: $STATUS" + echo "" + echo "Tickets in the following statuses cannot have work logged against them:" + echo " - Backlog" + echo " - Open" + echo " - Closed/Done" + echo " - Ready to Release" + echo "" + echo "Please move the ticket to an active status before creating a PR." + exit 1 + fi + if [ -n "$TITLE" ]; then echo " Title: $TITLE" fi + if [ -n "$STATUS" ]; then + echo " Status: $STATUS" + fi elif [ "$HTTP_CODE" = "404" ]; then echo "❌ Ticket $TICKET does not exist in Jira" echo "exists=false" >> $GITHUB_OUTPUT From 30a7532b46fcd85b442748dcbb9d0940bc84533a Mon Sep 17 00:00:00 2001 From: Patrick Dodgen Date: Fri, 19 Dec 2025 15:39:43 -0700 Subject: [PATCH 5/9] fix: fetching only status --- .github/workflows/jira-ticket-check.yaml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/jira-ticket-check.yaml b/.github/workflows/jira-ticket-check.yaml index 902cf28..a569a13 100644 --- a/.github/workflows/jira-ticket-check.yaml +++ b/.github/workflows/jira-ticket-check.yaml @@ -288,22 +288,22 @@ jobs: echo "🔍 Verifying ticket $TICKET exists in Jira..." # Make API request to check if ticket exists - RESPONSE=$(curl -s -w "\n%{http_code}" \ + # Write body to stdout and HTTP code to stderr, then capture both + BODY=$(curl -s \ -X GET \ -H "Authorization: Basic ${AUTH_STRING}" \ -H "Accept: application/json" \ - "https://${JIRA_INSTANCE}/rest/api/3/issue/${TICKET}") + -w "\n%{http_code}" \ + "https://${JIRA_INSTANCE}/rest/api/3/issue/${TICKET}" 2>/dev/null) - HTTP_CODE=$(echo "$RESPONSE" | tail -n1) - BODY=$(echo "$RESPONSE" | sed '$d') + # Extract HTTP code (last line) and body (everything else) + HTTP_CODE=$(echo "$BODY" | tail -n1) + BODY=$(echo "$BODY" | head -n -1) if [ "$HTTP_CODE" = "200" ]; then echo "✅ Ticket $TICKET exists in Jira" echo "exists=true" >> $GITHUB_OUTPUT - # Extract ticket title if available - TITLE=$(echo "$BODY" | grep -o '"summary":"[^"]*' | head -1 | cut -d'"' -f4 || echo "") - # Extract status from Jira API response (status is nested: fields.status.name) # Try using jq first (if available), then fall back to grep/sed if command -v jq >/dev/null 2>&1; then From c2fad66ca03f54f3387871af1fc0602ce7d88ff6 Mon Sep 17 00:00:00 2001 From: Patrick Dodgen Date: Fri, 19 Dec 2025 15:40:20 -0700 Subject: [PATCH 6/9] fix: simpler handling of cURL response --- .github/workflows/jira-ticket-check.yaml | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/.github/workflows/jira-ticket-check.yaml b/.github/workflows/jira-ticket-check.yaml index a569a13..cdb3bec 100644 --- a/.github/workflows/jira-ticket-check.yaml +++ b/.github/workflows/jira-ticket-check.yaml @@ -288,17 +288,19 @@ jobs: echo "🔍 Verifying ticket $TICKET exists in Jira..." # Make API request to check if ticket exists - # Write body to stdout and HTTP code to stderr, then capture both - BODY=$(curl -s \ + # Use a unique delimiter to separate body from HTTP code + TEMP_FILE=$(mktemp) + HTTP_CODE=$(curl -s \ -X GET \ -H "Authorization: Basic ${AUTH_STRING}" \ -H "Accept: application/json" \ - -w "\n%{http_code}" \ - "https://${JIRA_INSTANCE}/rest/api/3/issue/${TICKET}" 2>/dev/null) + -w "%{http_code}" \ + -o "$TEMP_FILE" \ + "https://${JIRA_INSTANCE}/rest/api/3/issue/${TICKET}") - # Extract HTTP code (last line) and body (everything else) - HTTP_CODE=$(echo "$BODY" | tail -n1) - BODY=$(echo "$BODY" | head -n -1) + # Read the body from the temp file (this is clean JSON) + BODY=$(cat "$TEMP_FILE") + rm -f "$TEMP_FILE" if [ "$HTTP_CODE" = "200" ]; then echo "✅ Ticket $TICKET exists in Jira" From 4400164a720f25740588ec1a3972091c82d6b2b6 Mon Sep 17 00:00:00 2001 From: Patrick Dodgen Date: Fri, 19 Dec 2025 15:40:55 -0700 Subject: [PATCH 7/9] chore: better naming for secrets --- .github/workflows/jira-ticket-check.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/jira-ticket-check.yaml b/.github/workflows/jira-ticket-check.yaml index cdb3bec..6b78f99 100644 --- a/.github/workflows/jira-ticket-check.yaml +++ b/.github/workflows/jira-ticket-check.yaml @@ -30,10 +30,10 @@ on: description: 'Jira instance URL (e.g., revolutionparts.atlassian.net)' default: 'revolutionparts.atlassian.net' secrets: - jira-email: + JIRA_EMAIL: required: false description: 'Jira account email for API authentication' - jira-api-token: + JIRA_API_TOKEN: required: false description: 'Jira API token for authentication' From 2eea56221d85785a2c5a471cda6a1045fd40ac9b Mon Sep 17 00:00:00 2001 From: Patrick Dodgen Date: Fri, 19 Dec 2025 15:43:44 -0700 Subject: [PATCH 8/9] chore: better naming of secrets --- .github/workflows/jira-ticket-check.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/jira-ticket-check.yaml b/.github/workflows/jira-ticket-check.yaml index 6b78f99..6f07072 100644 --- a/.github/workflows/jira-ticket-check.yaml +++ b/.github/workflows/jira-ticket-check.yaml @@ -30,10 +30,10 @@ on: description: 'Jira instance URL (e.g., revolutionparts.atlassian.net)' default: 'revolutionparts.atlassian.net' secrets: - JIRA_EMAIL: + JIRA_GITHUB_USER_EMAIL: required: false description: 'Jira account email for API authentication' - JIRA_API_TOKEN: + JIRA_GITHUB_USER_API_TOKEN: required: false description: 'Jira API token for authentication' From 53362e96f48bb53e4edbff8feb6b6186aadf1b19 Mon Sep 17 00:00:00 2001 From: Patrick Dodgen Date: Fri, 19 Dec 2025 15:49:30 -0700 Subject: [PATCH 9/9] chore: update secret usage --- .github/workflows/jira-ticket-check.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/jira-ticket-check.yaml b/.github/workflows/jira-ticket-check.yaml index 6f07072..5919550 100644 --- a/.github/workflows/jira-ticket-check.yaml +++ b/.github/workflows/jira-ticket-check.yaml @@ -266,8 +266,8 @@ jobs: exit 0 fi - JIRA_EMAIL="${{ secrets.jira-email }}" - JIRA_TOKEN="${{ secrets.jira-api-token }}" + JIRA_EMAIL="${{ secrets.JIRA_GITHUB_USER_EMAIL }}" + JIRA_TOKEN="${{ secrets.JIRA_GITHUB_USER_API_TOKEN }}" if [ -z "$JIRA_EMAIL" ] || [ -z "$JIRA_TOKEN" ]; then echo "⚠️ Jira credentials not provided, skipping ticket verification"