Skip to content

chore(ci): Add workflow to sync JIRA ticket with GitHub issue#6391

Draft
Yaminyam wants to merge 3 commits into
mainfrom
ci/ticket-issue-sync
Draft

chore(ci): Add workflow to sync JIRA ticket with GitHub issue#6391
Yaminyam wants to merge 3 commits into
mainfrom
ci/ticket-issue-sync

Conversation

@Yaminyam
Copy link
Copy Markdown
Member

Introduces a GitHub Actions workflow that extracts a JIRA ticket number from the PR title, fetches the corresponding GitHub issue number from JIRA, and updates the PR body with a resolves clause to link the PR to the GitHub issue. This automates cross-referencing between JIRA tickets and GitHub issues for improved traceability.

resolves #NNN (BA-MMM)

Checklist: (if applicable)

  • Milestone metadata specifying the target backport version
  • Mention to the original issue
  • Installer updates including:
    • Fixtures for db schema changes
    • New mandatory config options
  • Update of end-to-end CLI integration tests in ai.backend.test
  • API server-client counterparts (e.g., manager API -> client SDK)
  • Test case(s) to:
    • Demonstrate the difference of before/after
    • Demonstrate the flow of abstract/conceptual models with a concrete implementation
  • Documentation
    • Contents in the docs directory
    • docstrings in public interfaces and type annotations

Introduces a GitHub Actions workflow that extracts a JIRA ticket number from the PR title, fetches the corresponding GitHub issue number from JIRA, and updates the PR body with a resolves clause to link the PR to the GitHub issue. This automates cross-referencing between JIRA tickets and GitHub issues for improved traceability.
Copilot AI review requested due to automatic review settings October 24, 2025 07:05
@Yaminyam Yaminyam changed the title Add workflow to sync JIRA ticket with GitHub issue chore(ci): Add workflow to sync JIRA ticket with GitHub issue Oct 24, 2025
@github-actions github-actions Bot added the size:L 100~500 LoC label Oct 24, 2025
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR introduces a GitHub Actions workflow that automates the synchronization between JIRA tickets and GitHub issues. When a PR is opened or synchronized, the workflow extracts the JIRA ticket number from the PR title, queries the JIRA API to retrieve the associated GitHub issue URL, and updates the PR body with a resolves clause linking to that issue.

Key changes:

  • Adds automated JIRA ticket extraction from PR titles using regex pattern matching
  • Implements JIRA API integration to fetch GitHub issue URLs from ticket metadata
  • Automatically updates PR bodies with resolves clauses for cross-referencing

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.


# 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)
Copy link

Copilot AI Oct 24, 2025

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 format customfield_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.

Suggested change
GITHUB_ISSUE_URL=$(echo "$JIRA_RESPONSE" | jq -r '.fields | to_entries[] | select(.key == "GitHub Issue URL") | .value' 2>/dev/null | head -1)
# Replace customfield_12345 with your actual custom field ID for "GitHub Issue URL"
GITHUB_ISSUE_URL=$(echo "$JIRA_RESPONSE" | jq -r '.fields["customfield_12345"]' 2>/dev/null)

Copilot uses AI. Check for mistakes.
echo "Generated resolves clause: $RESOLVES_CLAUSE"

# Replace existing resolves clause or add new one
if echo "$PR_BODY" | grep -qi "resolves #.*("; then
Copy link

Copilot AI Oct 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The regex pattern resolves #.*( is missing proper escaping for the parenthesis. The ( character should be escaped as \( in grep, otherwise it's treated as a literal character to match. The correct pattern should be resolves #.*\(.

Suggested change
if echo "$PR_BODY" | grep -qi "resolves #.*("; then
if echo "$PR_BODY" | grep -qi "resolves #.*\("; then

Copilot uses AI. Check for mistakes.
# Replace existing resolves clause or add new one
if echo "$PR_BODY" | grep -qi "resolves #.*("; then
# Replace existing resolves clause
NEW_BODY=$(echo "$PR_BODY" | sed -E "s/resolves #[0-9]+ \([^)]+\)/$RESOLVES_CLAUSE/gi")
Copy link

Copilot AI Oct 24, 2025

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.

Suggested change
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")

Copilot uses AI. Check for mistakes.
Comment on lines +9 to +114
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
# Replace existing resolves clause
NEW_BODY=$(echo "$PR_BODY" | sed -E "s/resolves #[0-9]+ \([^)]+\)/$RESOLVES_CLAUSE/gi")
echo "Replaced existing resolves clause"
else
# Add new resolves clause
NEW_BODY=$(printf "%s\n\n%s" "$PR_BODY" "$RESOLVES_CLAUSE")
echo "Added new resolves clause"
fi

# Update PR body
curl -s -X PATCH \
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
-H "Accept: application/vnd.github.v3+json" \
-H "Content-Type: application/json" \
"https://api.github.com/repos/${{ github.repository_owner }}/${{ github.event.repository.name }}/pulls/${{ github.event.pull_request.number }}" \
-d "{\"body\": $(echo "$NEW_BODY" | jq -R -s .)}"

echo "✅ Added resolves clause: $RESOLVES_CLAUSE"

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}

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: write

indented to match other job keys. No imports or additional methods are required, since this is purely a workflow configuration change.

Suggested changeset 1
.github/workflows/ticket-issue-number-sync.yml

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/.github/workflows/ticket-issue-number-sync.yml b/.github/workflows/ticket-issue-number-sync.yml
--- a/.github/workflows/ticket-issue-number-sync.yml
+++ b/.github/workflows/ticket-issue-number-sync.yml
@@ -6,6 +6,9 @@
 
 jobs:
   ticket-issue-number-sync:
+    permissions:
+      contents: read
+      pull-requests: write
     runs-on: ubuntu-latest
     steps:
     - name: Checkout code
EOF
@@ -6,6 +6,9 @@

jobs:
ticket-issue-number-sync:
permissions:
contents: read
pull-requests: write
runs-on: ubuntu-latest
steps:
- name: Checkout code
Copilot is powered by AI and may make mistakes. Always verify output.
@Yaminyam Yaminyam marked this pull request as draft October 24, 2025 07:17
@HyeockJinKim HyeockJinKim force-pushed the main branch 4 times, most recently from 1a10632 to 2d8c9ea Compare November 23, 2025 14:45
@HyeockJinKim HyeockJinKim force-pushed the main branch 2 times, most recently from 9552aac to 4af738e Compare December 31, 2025 15:41
Fix several issues in the ticket-issue-number-sync workflow:

1. GitHub Issue URL field detection:
   - Search for any field containing GitHub issue URLs using regex
   - Support customfield_* format (actual JIRA custom field format)
   - Previously assumed field key would be "GitHub Issue URL" literally

2. GitHub API repository reference:
   - Use github.repository instead of github.repository_owner/github.event.repository.name
   - github.event.repository.name doesn't exist in the context

3. sed regex improvements:
   - Fix case-insensitive matching: use [Rr]esolves instead of /gi flag
   - Add optional space in regex: "resolves #[0-9]+ ?\(" to handle spacing variations
   - Remove invalid sed flags

4. Better error messages:
   - Add emojis (✅/❌) for clarity
   - Show available JIRA fields when GitHub Issue URL not found
   - More detailed format expectations in error messages

5. UX improvements:
   - Add resolves clause at the beginning of PR body instead of end
   - Better visibility for automated changes

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L 100~500 LoC

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants