From 0d982f008d25f0fd944539a7595bf5b153958188 Mon Sep 17 00:00:00 2001 From: Marcin Kolasinski Date: Fri, 3 Jul 2026 12:32:35 +0200 Subject: [PATCH 1/7] feat: validate ADDON Jira ticket in PR title Add a step to the validate-pr-title job that checks for ADDON-XXXXX (5-6 digit number) in the PR title, failing with a clear error message if missing. Co-Authored-By: Claude --- .github/workflows/reusable-build-test-release.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/reusable-build-test-release.yml b/.github/workflows/reusable-build-test-release.yml index 6b5c14b6..e6488e3f 100644 --- a/.github/workflows/reusable-build-test-release.yml +++ b/.github/workflows/reusable-build-test-release.yml @@ -373,6 +373,13 @@ jobs: validateSingleCommit: true env: GITHUB_TOKEN: ${{ github.token }} + - name: Validate ADDON Jira ticket in PR title + run: | + PR_TITLE="${{ github.event.pull_request.title }}" + if [[ ! "$PR_TITLE" =~ ADDON-[0-9]{5,6} ]]; then + echo "::error::PR title must contain a Jira ticket in the format ADDON-XXXXX (5-6 digits). Got: $PR_TITLE" + exit 1 + fi meta: runs-on: ubuntu-latest From e10389df3380e1d9f9fcef97ebd85aad1db0b847 Mon Sep 17 00:00:00 2001 From: Marcin Kolasinski Date: Wed, 8 Jul 2026 19:05:53 +0200 Subject: [PATCH 2/7] fix: harden ADDON Jira ticket title validation - Pass the PR title through env: instead of interpolating it into the run: script text, so a crafted title can't trigger command substitution on the runner. - Anchor the ticket regex so a longer digit run (e.g. ADDON-1234567) no longer satisfies the check via its 5-6 digit prefix. --- .github/workflows/reusable-build-test-release.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/reusable-build-test-release.yml b/.github/workflows/reusable-build-test-release.yml index e6488e3f..0f7ad726 100644 --- a/.github/workflows/reusable-build-test-release.yml +++ b/.github/workflows/reusable-build-test-release.yml @@ -374,9 +374,10 @@ jobs: env: GITHUB_TOKEN: ${{ github.token }} - name: Validate ADDON Jira ticket in PR title + env: + PR_TITLE: ${{ github.event.pull_request.title }} run: | - PR_TITLE="${{ github.event.pull_request.title }}" - if [[ ! "$PR_TITLE" =~ ADDON-[0-9]{5,6} ]]; then + if [[ ! "$PR_TITLE" =~ ADDON-[0-9]{5,6}([^0-9]|$) ]]; then echo "::error::PR title must contain a Jira ticket in the format ADDON-XXXXX (5-6 digits). Got: $PR_TITLE" exit 1 fi From 645bfb73905365580d6a74022a992dad2ffd7bfd Mon Sep 17 00:00:00 2001 From: Marcin Kolasinski Date: Wed, 8 Jul 2026 23:08:13 +0200 Subject: [PATCH 3/7] feat: comment on Jira ticket when referenced in a push commit Adds a comment-on-jira job that posts a link back to the commit (and originating PR, if found) on any ADDON-XXXXX ticket referenced in a commit pushed to main/develop, giving reporters visibility into when their fix landed. Uses the Jira REST API directly via curl rather than a third-party comment action (atlassian/gajira-comment has a known RCE, CVE-2020-14189). Requires new JIRA_BASE_URL/JIRA_USER_EMAIL/JIRA_API_TOKEN secrets; consuming repos need these added before bumping to the next tag. --- .../workflows/reusable-build-test-release.yml | 62 +++++++++++++++++++ README.md | 18 ++++++ 2 files changed, 80 insertions(+) diff --git a/.github/workflows/reusable-build-test-release.yml b/.github/workflows/reusable-build-test-release.yml index 0f7ad726..0e158561 100644 --- a/.github/workflows/reusable-build-test-release.yml +++ b/.github/workflows/reusable-build-test-release.yml @@ -129,6 +129,15 @@ on: GSSA_AWS_SECRET_ACCESS_KEY: description: GSSA AWS secret access key required: true + JIRA_BASE_URL: + description: Jira base URL, e.g. https://mycompany.atlassian.net + required: true + JIRA_USER_EMAIL: + description: Email of the Jira user used to post ticket comments + required: true + JIRA_API_TOKEN: + description: API token for the Jira user used to post ticket comments + required: true permissions: contents: read packages: read @@ -382,6 +391,59 @@ jobs: exit 1 fi + comment-on-jira: + name: Comment on Jira ticket + if: ${{ github.event_name == 'push' && (github.ref_name == 'main' || github.ref_name == 'develop') }} + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: Comment on referenced ADDON ticket(s) + env: + GH_TOKEN: ${{ github.token }} + COMMITS: ${{ toJson(github.event.commits) }} + REPO: ${{ github.repository }} + BRANCH: ${{ github.ref_name }} + JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }} + JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }} + JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }} + run: | + SEEN="" + echo "$COMMITS" | jq -c '.[]' | while read -r commit; do + TITLE=$(jq -r '.message' <<<"$commit" | head -n1) + SHA=$(jq -r '.id' <<<"$commit") + if [[ ! "$TITLE" =~ (ADDON-[0-9]{5,6})([^0-9]|$) ]]; then + continue + fi + TICKET="${BASH_REMATCH[1]}" + if [[ " $SEEN " == *" $TICKET "* ]]; then + continue + fi + SEEN="$SEEN $TICKET" + + COMMIT_URL="https://github.com/$REPO/commit/$SHA" + PR_URL=$(gh api "repos/$REPO/commits/$SHA/pulls" --jq '.[0].html_url // ""' || true) + BODY=$(jq -n \ + --arg branch "$BRANCH" --arg repo "$REPO" \ + --arg commit_url "$COMMIT_URL" --arg sha "${SHA:0:7}" --arg pr_url "$PR_URL" \ + '{body: {type: "doc", version: 1, content: [{type: "paragraph", content: ( + [{type: "text", text: ("Pushed to " + $branch + " in " + $repo + ": ")}, + {type: "text", text: $sha, marks: [{type: "link", attrs: {href: $commit_url}}]}] + + (if $pr_url != "" then + [{type: "text", text: " (via "}, + {type: "text", text: "PR", marks: [{type: "link", attrs: {href: $pr_url}}]}, + {type: "text", text: ")"}] + else [] end) + )}]}}') + + echo "Commenting on $TICKET for commit ${SHA:0:7}" + curl --fail --silent --show-error \ + -u "$JIRA_USER_EMAIL:$JIRA_API_TOKEN" \ + -H "Content-Type: application/json" \ + -X POST "$JIRA_BASE_URL/rest/api/3/issue/$TICKET/comment" \ + -d "$BODY" || echo "::warning::Failed to comment on $TICKET for commit ${SHA:0:7}" + done + meta: runs-on: ubuntu-latest outputs: diff --git a/README.md b/README.md index 35bd0c86..03f5af46 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ * [[Job] check-docs-changes](#job-check-docs-changes) * [[Job] setup-workflow](#job-setup-workflow) * [[Job] validate-pr-title](#job-validate-pr-title) + * [[Job] comment-on-jira](#job-comment-on-jira) * [[Job] meta](#job-meta) * [[Job] fossa-scan](#job-fossa-scan) * [[Job] fossa-test](#job-fossa-test) @@ -285,6 +286,23 @@ gitGraph - No additional artifacts. +## [Job] comment-on-jira + +**Description:** + +- Runs only on push events to `main` or `develop`. +- Scans the commit messages in the push for an `ADDON-XXXXX` Jira ticket reference (5-6 digits) and posts a comment on each referenced ticket linking back to the commit (and originating PR, if found). +- Requires the `JIRA_BASE_URL`, `JIRA_USER_EMAIL`, and `JIRA_API_TOKEN` secrets to be configured; the Jira user needs comment permission on the referenced tickets. + +**Pass/fail behaviour:** + +- Never fails the workflow: a failed Jira API call only logs a warning. + +**Artifacts:** + +- No additional artifacts. + + ## [Job] meta **Description:** From 44fe3e7e0e3839c25745e2366f6276e7e1ff05a8 Mon Sep 17 00:00:00 2001 From: Marcin Kolasinski Date: Wed, 8 Jul 2026 23:21:38 +0200 Subject: [PATCH 4/7] refactor: align Jira secrets with org convention (ATLASSIAN_EMAIL/TOKEN) addonfactory-docs-on-github-integration and issue-exploder already use ATLASSIAN_EMAIL/ATLASSIAN_TOKEN (or JIRA_LOGIN/JIRA_API_TOKEN) against a hardcoded splunk.atlassian.net rather than a configurable base URL. Match that instead of introducing new JIRA_* secret names. --- .../workflows/reusable-build-test-release.yml | 19 ++++++++----------- README.md | 2 +- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/.github/workflows/reusable-build-test-release.yml b/.github/workflows/reusable-build-test-release.yml index 0e158561..585154e3 100644 --- a/.github/workflows/reusable-build-test-release.yml +++ b/.github/workflows/reusable-build-test-release.yml @@ -129,14 +129,11 @@ on: GSSA_AWS_SECRET_ACCESS_KEY: description: GSSA AWS secret access key required: true - JIRA_BASE_URL: - description: Jira base URL, e.g. https://mycompany.atlassian.net + ATLASSIAN_EMAIL: + description: Email of the Atlassian user used to post Jira ticket comments required: true - JIRA_USER_EMAIL: - description: Email of the Jira user used to post ticket comments - required: true - JIRA_API_TOKEN: - description: API token for the Jira user used to post ticket comments + ATLASSIAN_TOKEN: + description: API token for the Atlassian user used to post Jira ticket comments required: true permissions: contents: read @@ -404,9 +401,9 @@ jobs: COMMITS: ${{ toJson(github.event.commits) }} REPO: ${{ github.repository }} BRANCH: ${{ github.ref_name }} - JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }} - JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }} - JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }} + JIRA_BASE_URL: https://splunk.atlassian.net + ATLASSIAN_EMAIL: ${{ secrets.ATLASSIAN_EMAIL }} + ATLASSIAN_TOKEN: ${{ secrets.ATLASSIAN_TOKEN }} run: | SEEN="" echo "$COMMITS" | jq -c '.[]' | while read -r commit; do @@ -438,7 +435,7 @@ jobs: echo "Commenting on $TICKET for commit ${SHA:0:7}" curl --fail --silent --show-error \ - -u "$JIRA_USER_EMAIL:$JIRA_API_TOKEN" \ + -u "$ATLASSIAN_EMAIL:$ATLASSIAN_TOKEN" \ -H "Content-Type: application/json" \ -X POST "$JIRA_BASE_URL/rest/api/3/issue/$TICKET/comment" \ -d "$BODY" || echo "::warning::Failed to comment on $TICKET for commit ${SHA:0:7}" diff --git a/README.md b/README.md index 03f5af46..ec44ddc2 100644 --- a/README.md +++ b/README.md @@ -292,7 +292,7 @@ gitGraph - Runs only on push events to `main` or `develop`. - Scans the commit messages in the push for an `ADDON-XXXXX` Jira ticket reference (5-6 digits) and posts a comment on each referenced ticket linking back to the commit (and originating PR, if found). -- Requires the `JIRA_BASE_URL`, `JIRA_USER_EMAIL`, and `JIRA_API_TOKEN` secrets to be configured; the Jira user needs comment permission on the referenced tickets. +- Requires the `ATLASSIAN_EMAIL` and `ATLASSIAN_TOKEN` secrets to be configured; the Atlassian user needs comment permission on the referenced tickets in `https://splunk.atlassian.net`. **Pass/fail behaviour:** From 1c8ba82716a35a283633cbb56f00bad409dbecc4 Mon Sep 17 00:00:00 2001 From: Marcin Kolasinski Date: Thu, 9 Jul 2026 00:16:34 +0200 Subject: [PATCH 5/7] fix: grant pull-requests:read and stop leaking gh api errors into Jira comments Live test against test-addonfactory-repo (push to main, ADDON-88759) showed the PR lookup 403ing (job only had contents:read) and, because gh api's stderr/stdout still populated PR_URL before the `|| true` swallowed the exit code, the raw JSON error body got embedded as the "PR" link href in the posted Jira comment. --- .github/workflows/reusable-build-test-release.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/reusable-build-test-release.yml b/.github/workflows/reusable-build-test-release.yml index 585154e3..b4d24f2e 100644 --- a/.github/workflows/reusable-build-test-release.yml +++ b/.github/workflows/reusable-build-test-release.yml @@ -394,6 +394,7 @@ jobs: runs-on: ubuntu-latest permissions: contents: read + pull-requests: read steps: - name: Comment on referenced ADDON ticket(s) env: @@ -419,7 +420,9 @@ jobs: SEEN="$SEEN $TICKET" COMMIT_URL="https://github.com/$REPO/commit/$SHA" - PR_URL=$(gh api "repos/$REPO/commits/$SHA/pulls" --jq '.[0].html_url // ""' || true) + if ! PR_URL=$(gh api "repos/$REPO/commits/$SHA/pulls" --jq '.[0].html_url // ""' 2>/dev/null); then + PR_URL="" + fi BODY=$(jq -n \ --arg branch "$BRANCH" --arg repo "$REPO" \ --arg commit_url "$COMMIT_URL" --arg sha "${SHA:0:7}" --arg pr_url "$PR_URL" \ From 80f33e73b0cfb7f796b9b94e88d4c72bfaf45441 Mon Sep 17 00:00:00 2001 From: Marcin Kolasinski Date: Thu, 9 Jul 2026 00:32:49 +0200 Subject: [PATCH 6/7] feat: match GitLab-style wording for Jira comment-on-push messages Reword the posted comment to "{author} mentioned this issue in a commit of {repo} on branch {branch}:" followed by the commit/PR links, matching the phrasing already used by the existing GitLab->Jira integration (srv-ssc-gitlab) so both integrations read consistently on a ticket. --- .../workflows/reusable-build-test-release.yml | 25 +++++++++++-------- README.md | 2 +- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/.github/workflows/reusable-build-test-release.yml b/.github/workflows/reusable-build-test-release.yml index b4d24f2e..2bfaa6ca 100644 --- a/.github/workflows/reusable-build-test-release.yml +++ b/.github/workflows/reusable-build-test-release.yml @@ -410,6 +410,7 @@ jobs: echo "$COMMITS" | jq -c '.[]' | while read -r commit; do TITLE=$(jq -r '.message' <<<"$commit" | head -n1) SHA=$(jq -r '.id' <<<"$commit") + AUTHOR=$(jq -r '.author.username // .author.name' <<<"$commit") if [[ ! "$TITLE" =~ (ADDON-[0-9]{5,6})([^0-9]|$) ]]; then continue fi @@ -424,17 +425,21 @@ jobs: PR_URL="" fi BODY=$(jq -n \ - --arg branch "$BRANCH" --arg repo "$REPO" \ + --arg author "$AUTHOR" --arg branch "$BRANCH" --arg repo "$REPO" \ --arg commit_url "$COMMIT_URL" --arg sha "${SHA:0:7}" --arg pr_url "$PR_URL" \ - '{body: {type: "doc", version: 1, content: [{type: "paragraph", content: ( - [{type: "text", text: ("Pushed to " + $branch + " in " + $repo + ": ")}, - {type: "text", text: $sha, marks: [{type: "link", attrs: {href: $commit_url}}]}] - + (if $pr_url != "" then - [{type: "text", text: " (via "}, - {type: "text", text: "PR", marks: [{type: "link", attrs: {href: $pr_url}}]}, - {type: "text", text: ")"}] - else [] end) - )}]}}') + '{body: {type: "doc", version: 1, content: [ + {type: "paragraph", content: [ + {type: "text", text: ($author + " mentioned this issue in a commit of " + $repo + " on branch " + $branch + ":")} + ]}, + {type: "paragraph", content: ( + [{type: "text", text: $sha, marks: [{type: "link", attrs: {href: $commit_url}}]}] + + (if $pr_url != "" then + [{type: "text", text: " (via "}, + {type: "text", text: "PR", marks: [{type: "link", attrs: {href: $pr_url}}]}, + {type: "text", text: ")"}] + else [] end) + )} + ]}}') echo "Commenting on $TICKET for commit ${SHA:0:7}" curl --fail --silent --show-error \ diff --git a/README.md b/README.md index ec44ddc2..8057a91f 100644 --- a/README.md +++ b/README.md @@ -291,7 +291,7 @@ gitGraph **Description:** - Runs only on push events to `main` or `develop`. -- Scans the commit messages in the push for an `ADDON-XXXXX` Jira ticket reference (5-6 digits) and posts a comment on each referenced ticket linking back to the commit (and originating PR, if found). +- Scans the commit messages in the push for an `ADDON-XXXXX` Jira ticket reference (5-6 digits) and posts a comment on each referenced ticket (e.g. "*author* mentioned this issue in a commit of *repo* on branch *branch*:") linking back to the commit (and originating PR, if found). - Requires the `ATLASSIAN_EMAIL` and `ATLASSIAN_TOKEN` secrets to be configured; the Atlassian user needs comment permission on the referenced tickets in `https://splunk.atlassian.net`. **Pass/fail behaviour:** From 680bdd2cdc0ee38ae0ff5639b0cfd8a8c2446c0e Mon Sep 17 00:00:00 2001 From: Marcin Kolasinski Date: Wed, 15 Jul 2026 09:36:40 +0200 Subject: [PATCH 7/7] fix: comment on every ADDON ticket referenced in a commit message The Jira comment job only matched the first line of the commit message and the first regex match, so pushes referencing multiple tickets (or a ticket outside the first line) only got a comment on one. --- .../workflows/reusable-build-test-release.yml | 68 ++++++++++--------- 1 file changed, 35 insertions(+), 33 deletions(-) diff --git a/.github/workflows/reusable-build-test-release.yml b/.github/workflows/reusable-build-test-release.yml index 2bfaa6ca..815f8e88 100644 --- a/.github/workflows/reusable-build-test-release.yml +++ b/.github/workflows/reusable-build-test-release.yml @@ -408,45 +408,47 @@ jobs: run: | SEEN="" echo "$COMMITS" | jq -c '.[]' | while read -r commit; do - TITLE=$(jq -r '.message' <<<"$commit" | head -n1) + MESSAGE=$(jq -r '.message' <<<"$commit") SHA=$(jq -r '.id' <<<"$commit") AUTHOR=$(jq -r '.author.username // .author.name' <<<"$commit") - if [[ ! "$TITLE" =~ (ADDON-[0-9]{5,6})([^0-9]|$) ]]; then + TICKETS=$(grep -oP 'ADDON-[0-9]{5,6}(?![0-9])' <<<"$MESSAGE" | sort -u) + if [[ -z "$TICKETS" ]]; then continue fi - TICKET="${BASH_REMATCH[1]}" - if [[ " $SEEN " == *" $TICKET "* ]]; then - continue - fi - SEEN="$SEEN $TICKET" + while read -r TICKET; do + if [[ " $SEEN " == *" $TICKET "* ]]; then + continue + fi + SEEN="$SEEN $TICKET" - COMMIT_URL="https://github.com/$REPO/commit/$SHA" - if ! PR_URL=$(gh api "repos/$REPO/commits/$SHA/pulls" --jq '.[0].html_url // ""' 2>/dev/null); then - PR_URL="" - fi - BODY=$(jq -n \ - --arg author "$AUTHOR" --arg branch "$BRANCH" --arg repo "$REPO" \ - --arg commit_url "$COMMIT_URL" --arg sha "${SHA:0:7}" --arg pr_url "$PR_URL" \ - '{body: {type: "doc", version: 1, content: [ - {type: "paragraph", content: [ - {type: "text", text: ($author + " mentioned this issue in a commit of " + $repo + " on branch " + $branch + ":")} - ]}, - {type: "paragraph", content: ( - [{type: "text", text: $sha, marks: [{type: "link", attrs: {href: $commit_url}}]}] - + (if $pr_url != "" then - [{type: "text", text: " (via "}, - {type: "text", text: "PR", marks: [{type: "link", attrs: {href: $pr_url}}]}, - {type: "text", text: ")"}] - else [] end) - )} - ]}}') + COMMIT_URL="https://github.com/$REPO/commit/$SHA" + if ! PR_URL=$(gh api "repos/$REPO/commits/$SHA/pulls" --jq '.[0].html_url // ""' 2>/dev/null); then + PR_URL="" + fi + BODY=$(jq -n \ + --arg author "$AUTHOR" --arg branch "$BRANCH" --arg repo "$REPO" \ + --arg commit_url "$COMMIT_URL" --arg sha "${SHA:0:7}" --arg pr_url "$PR_URL" \ + '{body: {type: "doc", version: 1, content: [ + {type: "paragraph", content: [ + {type: "text", text: ($author + " mentioned this issue in a commit of " + $repo + " on branch " + $branch + ":")} + ]}, + {type: "paragraph", content: ( + [{type: "text", text: $sha, marks: [{type: "link", attrs: {href: $commit_url}}]}] + + (if $pr_url != "" then + [{type: "text", text: " (via "}, + {type: "text", text: "PR", marks: [{type: "link", attrs: {href: $pr_url}}]}, + {type: "text", text: ")"}] + else [] end) + )} + ]}}') - echo "Commenting on $TICKET for commit ${SHA:0:7}" - curl --fail --silent --show-error \ - -u "$ATLASSIAN_EMAIL:$ATLASSIAN_TOKEN" \ - -H "Content-Type: application/json" \ - -X POST "$JIRA_BASE_URL/rest/api/3/issue/$TICKET/comment" \ - -d "$BODY" || echo "::warning::Failed to comment on $TICKET for commit ${SHA:0:7}" + echo "Commenting on $TICKET for commit ${SHA:0:7}" + curl --fail --silent --show-error \ + -u "$ATLASSIAN_EMAIL:$ATLASSIAN_TOKEN" \ + -H "Content-Type: application/json" \ + -X POST "$JIRA_BASE_URL/rest/api/3/issue/$TICKET/comment" \ + -d "$BODY" || echo "::warning::Failed to comment on $TICKET for commit ${SHA:0:7}" + done <<<"$TICKETS" done meta: