Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
210 changes: 210 additions & 0 deletions .github/workflows/create-github-issue-from-jira-branch.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
name: Create GitHub Issue from Jira Branch

on:
create:

permissions:
contents: read
issues: write

jobs:
extract-jira-key:
if: github.event.ref_type == 'branch'
runs-on: ubuntu-latest
outputs:
jira_key: ${{ steps.extract.outputs.jira_key }}
branch_name: ${{ steps.extract.outputs.branch_name }}
steps:
- name: Extract Jira key from branch name
id: extract
shell: bash
run: |
BRANCH_NAME="${{ github.event.ref }}"
if [[ "$BRANCH_NAME" =~ ([A-Z][A-Z0-9]+-[0-9]+) ]]; then
echo "jira_key=${BASH_REMATCH[1]}" >> "$GITHUB_OUTPUT"
echo "branch_name=$BRANCH_NAME" >> "$GITHUB_OUTPUT"
else
echo "No Jira key found in branch: $BRANCH_NAME"
echo "jira_key=" >> "$GITHUB_OUTPUT"
echo "branch_name=$BRANCH_NAME" >> "$GITHUB_OUTPUT"
fi

create-github-issue:
needs: extract-jira-key
if: needs.extract-jira-key.outputs.jira_key != ''
runs-on: ubuntu-latest
concurrency:
group: jira-gh-issue-${{ github.repository }}-${{ needs.extract-jira-key.outputs.jira_key }}
cancel-in-progress: false

steps:
- name: Fetch Jira issue summary and description
id: jira
env:
JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }}
JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}
JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }}
JIRA_KEY: ${{ needs.extract-jira-key.outputs.jira_key }}
shell: bash
run: |
set -euo pipefail

RESPONSE=$(curl -sS --fail \
--connect-timeout 10 \
--max-time 30 \
--retry 3 \
--retry-delay 2 \
--retry-all-errors \
-u "${JIRA_USER_EMAIL}:${JIRA_API_TOKEN}" \
-H "Accept: application/json" \
"${JIRA_BASE_URL}/rest/api/3/issue/${JIRA_KEY}?fields=summary,description,parent&expand=renderedFields")
Comment thread
coderabbitai[bot] marked this conversation as resolved.

SUMMARY=$(echo "$RESPONSE" | jq -r '.fields.summary // empty')
DESCRIPTION_HTML=$(echo "$RESPONSE" | jq -r '.renderedFields.description // ""')
PARENT_KEY=$(echo "$RESPONSE" | jq -r '.fields.parent.key // ""')

if [[ -z "$SUMMARY" ]]; then
echo "Jira issue summary is empty for key: $JIRA_KEY"
exit 1
fi

DELIMITER="EOF_$(date +%s)_$RANDOM"
{
echo "summary=$SUMMARY"
echo "parent_key=$PARENT_KEY"
echo "description_html<<$DELIMITER"
echo "$DESCRIPTION_HTML"
echo "$DELIMITER"
} >> "$GITHUB_OUTPUT"
Comment thread
coderabbitai[bot] marked this conversation as resolved.

- name: Check existing GitHub issue for Jira key
id: dedup
uses: actions/github-script@v7
env:
JIRA_KEY: ${{ needs.extract-jira-key.outputs.jira_key }}
with:
script: |
const jiraKey = process.env.JIRA_KEY;
const repo = `${context.repo.owner}/${context.repo.repo}`;
const marker = `JIRA_KEY: ${jiraKey}`;
const q = `repo:${repo} "${jiraKey}"`;
const result = await github.rest.search.issuesAndPullRequests({
q,
per_page: 20,
});
Comment thread
coderabbitai[bot] marked this conversation as resolved.

const existing = result.data.items.find((item) =>
!item.pull_request &&
(item.title.startsWith(`[${jiraKey}]`) || item.body?.includes(marker))
);
if (existing) {
core.info(`Issue already exists: #${existing.number}`);
core.setOutput('exists', 'true');
core.setOutput('number', String(existing.number));
} else {
core.setOutput('exists', 'false');
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

- name: Create GitHub issue
if: steps.dedup.outputs.exists != 'true'
id: create_issue
uses: actions/github-script@v7
env:
JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }}
JIRA_KEY: ${{ needs.extract-jira-key.outputs.jira_key }}
JIRA_SUMMARY: ${{ steps.jira.outputs.summary }}
JIRA_PARENT_KEY: ${{ steps.jira.outputs.parent_key }}
JIRA_DESCRIPTION_HTML: ${{ steps.jira.outputs.description_html }}
BRANCH_NAME: ${{ needs.extract-jira-key.outputs.branch_name }}
with:
script: |
const jiraKey = process.env.JIRA_KEY;
const title = `[${jiraKey}] ${process.env.JIRA_SUMMARY}`;
const summaryForBranch = (process.env.JIRA_SUMMARY || '')
.trim()
.replace(/\s+/g, '-')
.replace(/[^a-zA-Z0-9/_-]/g, '-')
.replace(/-+/g, '-')
.replace(/^-|-$/g, '');
const recommendedBranch = `${jiraKey}-${summaryForBranch || 'task'}`;
const parentKey = process.env.JIRA_PARENT_KEY || 'none';
const actualBranch = process.env.BRANCH_NAME || recommendedBranch;
const body = [
`JIRA_KEY: ${jiraKey}`,
'',
'### Parent Ticket Number',
parentKey,
'',
'### Branch',
`\`${actualBranch}\``,
'',
'### Description',
process.env.JIRA_DESCRIPTION_HTML || '(no description)',
'',
].join('\n');

const created = await github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title,
body,
});

core.setOutput('number', String(created.data.number));

- name: Comment GitHub issue link back to Jira
if: steps.dedup.outputs.exists != 'true'
env:
JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }}
JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}
JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }}
JIRA_KEY: ${{ needs.extract-jira-key.outputs.jira_key }}
GH_ISSUE_NUMBER: ${{ steps.create_issue.outputs.number }}
GH_REPO: ${{ github.repository }}
GH_SERVER_URL: ${{ github.server_url }}
shell: bash
run: |
set -euo pipefail

GH_ISSUE_URL="${GH_SERVER_URL}/${GH_REPO}/issues/${GH_ISSUE_NUMBER}"

COMMENT_JSON=$(jq -n \
--arg url "$GH_ISSUE_URL" \
--arg no "$GH_ISSUE_NUMBER" \
'{
body: {
type: "doc",
version: 1,
content: [
{
type: "paragraph",
content: [
{ type: "text", text: "GitHub issue created: " },
{
type: "text",
text: ("#" + $no),
marks: [
{
type: "link",
attrs: { href: $url }
}
]
}
]
}
]
}
}')

curl -sS --fail \
--connect-timeout 10 \
--max-time 30 \
--retry 3 \
--retry-delay 2 \
--retry-all-errors \
-u "${JIRA_USER_EMAIL}:${JIRA_API_TOKEN}" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-X POST \
--data "$COMMENT_JSON" \
"${JIRA_BASE_URL}/rest/api/3/issue/${JIRA_KEY}/comment" > /dev/null
102 changes: 0 additions & 102 deletions .github/workflows/create-jira-issue.yml

This file was deleted.