-
Notifications
You must be signed in to change notification settings - Fork 175
chore(ci): Add workflow to sync JIRA ticket with GitHub issue #6391
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,114 @@ | ||||||||||||||||||||||||||||||||
| name: ticket-issue-number-sync | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| on: | ||||||||||||||||||||||||||||||||
| pull_request: | ||||||||||||||||||||||||||||||||
| types: [opened, synchronize] | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| jobs: | ||||||||||||||||||||||||||||||||
| ticket-issue-number-sync: | ||||||||||||||||||||||||||||||||
| runs-on: ubuntu-latest | ||||||||||||||||||||||||||||||||
| steps: | ||||||||||||||||||||||||||||||||
| - name: Checkout code | ||||||||||||||||||||||||||||||||
| uses: actions/checkout@v4 | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| - name: Extract JIRA ticket number from PR title | ||||||||||||||||||||||||||||||||
| id: extract_ticket | ||||||||||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||||||||||
| # Extract JIRA ticket number from PR title (e.g., "fix(BA-1234): aaaa" -> "BA-1234") | ||||||||||||||||||||||||||||||||
| TITLE="${{ github.event.pull_request.title }}" | ||||||||||||||||||||||||||||||||
| echo "PR Title: $TITLE" | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| # Extract ticket number using regex pattern BA-XXXX format | ||||||||||||||||||||||||||||||||
| TICKET_NUMBER=$(echo "$TITLE" | grep -oE 'BA-[0-9]+' | head -1) | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| if [ -n "$TICKET_NUMBER" ]; then | ||||||||||||||||||||||||||||||||
| echo "Extracted ticket: $TICKET_NUMBER" | ||||||||||||||||||||||||||||||||
| echo "ticket_number=$TICKET_NUMBER" >> $GITHUB_OUTPUT | ||||||||||||||||||||||||||||||||
| echo "has_ticket=true" >> $GITHUB_OUTPUT | ||||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||||
| echo "No JIRA ticket number found in PR title" | ||||||||||||||||||||||||||||||||
| echo "has_ticket=false" >> $GITHUB_OUTPUT | ||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| - name: Get GitHub Issue URL from JIRA | ||||||||||||||||||||||||||||||||
| if: steps.extract_ticket.outputs.has_ticket == 'true' | ||||||||||||||||||||||||||||||||
| id: get_jira_issue | ||||||||||||||||||||||||||||||||
| env: | ||||||||||||||||||||||||||||||||
| JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }} | ||||||||||||||||||||||||||||||||
| JIRA_USERNAME: ${{ secrets.JIRA_USERNAME }} | ||||||||||||||||||||||||||||||||
| JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }} | ||||||||||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||||||||||
| TICKET_NUMBER="${{ steps.extract_ticket.outputs.ticket_number }}" | ||||||||||||||||||||||||||||||||
| echo "Fetching GitHub Issue URL for JIRA ticket: $TICKET_NUMBER" | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| # Get ticket information through JIRA API | ||||||||||||||||||||||||||||||||
| JIRA_RESPONSE=$(curl -s -u "$JIRA_USERNAME:$JIRA_API_TOKEN" \ | ||||||||||||||||||||||||||||||||
| -H "Accept: application/json" \ | ||||||||||||||||||||||||||||||||
| "$JIRA_BASE_URL/rest/api/3/issue/$TICKET_NUMBER") | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| echo "JIRA API Response: $JIRA_RESPONSE" | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| # Extract issue number from GitHub Issue URL field | ||||||||||||||||||||||||||||||||
| # Find field that exactly matches "GitHub Issue URL" in JIRA | ||||||||||||||||||||||||||||||||
| GITHUB_ISSUE_URL=$(echo "$JIRA_RESPONSE" | jq -r '.fields | to_entries[] | select(.key == "GitHub Issue URL") | .value' 2>/dev/null | head -1) | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| # Fail immediately if GitHub Issue URL is not found | ||||||||||||||||||||||||||||||||
| if [ -z "$GITHUB_ISSUE_URL" ] || [ "$GITHUB_ISSUE_URL" = "null" ]; then | ||||||||||||||||||||||||||||||||
| echo "No GitHub Issue URL found in JIRA ticket" | ||||||||||||||||||||||||||||||||
| exit 1 | ||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| echo "Found GitHub Issue URL: $GITHUB_ISSUE_URL" | ||||||||||||||||||||||||||||||||
| # Extract GitHub issue number (e.g., https://github.com/owner/repo/issues/123 -> 123) | ||||||||||||||||||||||||||||||||
| GITHUB_ISSUE_NUMBER=$(echo "$GITHUB_ISSUE_URL" | grep -oE '/issues/[0-9]+' | grep -oE '[0-9]+' | head -1) | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| if [ -n "$GITHUB_ISSUE_NUMBER" ]; then | ||||||||||||||||||||||||||||||||
| echo "Extracted GitHub Issue Number: $GITHUB_ISSUE_NUMBER" | ||||||||||||||||||||||||||||||||
| echo "github_issue_number=$GITHUB_ISSUE_NUMBER" >> $GITHUB_OUTPUT | ||||||||||||||||||||||||||||||||
| echo "has_github_issue=true" >> $GITHUB_OUTPUT | ||||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||||
| echo "❌ Could not extract issue number from GitHub Issue URL: $GITHUB_ISSUE_URL" | ||||||||||||||||||||||||||||||||
| echo "Please verify the GitHub Issue URL format is correct." | ||||||||||||||||||||||||||||||||
| exit 1 | ||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| - name: Update PR body with resolves clause | ||||||||||||||||||||||||||||||||
| if: steps.extract_ticket.outputs.has_ticket == 'true' && steps.get_jira_issue.outputs.has_github_issue == 'true' | ||||||||||||||||||||||||||||||||
| env: | ||||||||||||||||||||||||||||||||
| TICKET_NUMBER: ${{ steps.extract_ticket.outputs.ticket_number }} | ||||||||||||||||||||||||||||||||
| GITHUB_ISSUE_NUMBER: ${{ steps.get_jira_issue.outputs.github_issue_number }} | ||||||||||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||||||||||
| echo "Using GitHub issue number: $GITHUB_ISSUE_NUMBER from JIRA" | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| # Get current PR body | ||||||||||||||||||||||||||||||||
| PR_BODY=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ | ||||||||||||||||||||||||||||||||
| -H "Accept: application/vnd.github.v3+json" \ | ||||||||||||||||||||||||||||||||
| "https://api.github.com/repos/${{ github.repository_owner }}/${{ github.event.repository.name }}/pulls/${{ github.event.pull_request.number }}" | \ | ||||||||||||||||||||||||||||||||
| jq -r '.body // ""') | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| echo "Current PR body: $PR_BODY" | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| # Generate resolves clause | ||||||||||||||||||||||||||||||||
| RESOLVES_CLAUSE="resolves #$GITHUB_ISSUE_NUMBER ($TICKET_NUMBER)" | ||||||||||||||||||||||||||||||||
| echo "Generated resolves clause: $RESOLVES_CLAUSE" | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| # Replace existing resolves clause or add new one | ||||||||||||||||||||||||||||||||
| if echo "$PR_BODY" | grep -qi "resolves #.*("; then | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
| if echo "$PR_BODY" | grep -qi "resolves #.*("; then | |
| if echo "$PR_BODY" | grep -qi "resolves #.*\("; then |
Copilot
AI
Oct 24, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The sed command uses the gi flags, but g (global) and i (case-insensitive) flags are not both available in the same sed command syntax. The correct approach in GNU sed is to use I (uppercase) for case-insensitive matching with the -E flag, so this should be s/resolves #[0-9]+ \([^)]+\)/$RESOLVES_CLAUSE/gI.
| NEW_BODY=$(echo "$PR_BODY" | sed -E "s/resolves #[0-9]+ \([^)]+\)/$RESOLVES_CLAUSE/gi") | |
| NEW_BODY=$(echo "$PR_BODY" | sed -E "s/resolves #[0-9]+ \([^)]+\)/$RESOLVES_CLAUSE/gI") |
Check warning
Code scanning / CodeQL
Workflow does not contain permissions Medium
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 4 months ago
To fix this, add an explicit permissions block so the GITHUB_TOKEN used by this workflow follows the principle of least privilege. This workflow needs to read PR metadata and update the PR body via the GitHub API, which maps to pull-requests: write. It does not need to push commits or modify repository contents, so contents can be limited to read. The cleanest fix is to add a job-level permissions block under the ticket-issue-number-sync job, above runs-on. This keeps the scope tight and avoids affecting other workflows.
Concretely, in .github/workflows/ticket-issue-number-sync.yml, under jobs:, inside the ticket-issue-number-sync: job, insert:
permissions:
contents: read
pull-requests: writeindented to match other job keys. No imports or additional methods are required, since this is purely a workflow configuration change.
-
Copy modified lines R9-R11
| @@ -6,6 +6,9 @@ | ||
|
|
||
| jobs: | ||
| ticket-issue-number-sync: | ||
| permissions: | ||
| contents: read | ||
| pull-requests: write | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Checkout code |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The jq query uses
.key == "GitHub Issue URL"which attempts to match field keys as literal strings, but JIRA custom field keys are typically in the formatcustomfield_XXXXX, not human-readable names. This query will likely fail to find the field. You should either use the actual custom field ID (e.g.,customfield_10001) or query by field name using.fields["GitHub Issue URL"]if the field name is mapped correctly.