chore(release): Add backport PR and Jira audit workflow#19901
chore(release): Add backport PR and Jira audit workflow#19901
Conversation
Automated workflow to audit backport PRs and validate Jira metadata: - Fetches OPEN backport PRs using GitHub CLI - Resolves rhacs-bot and dependabot PRs to real authors - For dependabot PRs, finds who added the backport label - Validates ROX-XXXXX Jira references - Checks Jira fixVersion and affectedVersion fields - Finds orphaned Jira issues without PRs - Generates markdown report with Slack user IDs Example output for release-4.10 (16 open PRs): - 9 PRs missing Jira references - 3 Jira issues with incomplete metadata - All mapped to real authors with Slack IDs for mentions Runtime: ~1-2 minutes for typical branch Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Hey - I've found 1 issue, and left some high level feedback:
- In
find_orphaned_issues, thesummaryvariable is computed but never used; consider removing it or including the summary in the orphaned-issue report to avoid dead code. - The Jira
curlcalls (get_jira_issueandfind_orphaned_issues) swallow errors by defaulting to{}/{"issues":[]}; consider logging HTTP failures or surfacing non-2xx statuses so that misconfigured credentials or network issues are visible in CI. - In
generate_report,get-slack-user-id.shis invoked once per PR/author, which may become slow for many PRs; consider caching Slack IDs per GitHub login in an associative array to avoid redundant lookups.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- In `find_orphaned_issues`, the `summary` variable is computed but never used; consider removing it or including the summary in the orphaned-issue report to avoid dead code.
- The Jira `curl` calls (`get_jira_issue` and `find_orphaned_issues`) swallow errors by defaulting to `{}`/`{"issues":[]}`; consider logging HTTP failures or surfacing non-2xx statuses so that misconfigured credentials or network issues are visible in CI.
- In `generate_report`, `get-slack-user-id.sh` is invoked once per PR/author, which may become slow for many PRs; consider caching Slack IDs per GitHub login in an associative array to avoid redundant lookups.
## Individual Comments
### Comment 1
<location path="scripts/ci/audit-backport-prs.sh" line_range="435-436" />
<code_context>
+ local fix_versions
+ local affected_versions
+
+ fix_versions=$(echo "$issue_data" | jq -r '[.fields.fixVersions[].name] | join(", ")')
+ affected_versions=$(echo "$issue_data" | jq -r '[.fields.versions[].name] | join(", ")')
+
+ local has_fix_version="✓"
</code_context>
<issue_to_address>
**issue (bug_risk):** Handle missing or null Jira `fixVersions`/`versions` arrays to avoid jq errors.
These jq expressions will fail with "Cannot iterate over null" if `fields.fixVersions` or `fields.versions` is missing or null, and `set -e` will terminate the script. To make this robust, default to an empty array:
```bash
fix_versions=$(echo "$issue_data" | jq -r '[(.fields.fixVersions // [])[].name] | join(", ")')
affected_versions=$(echo "$issue_data" | jq -r '[(.fields.versions // [])[].name] | join(", ")')
```
This preserves your downstream checks while avoiding hard failures on incomplete Jira data.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| fix_versions=$(echo "$issue_data" | jq -r '[.fields.fixVersions[].name] | join(", ")') | ||
| affected_versions=$(echo "$issue_data" | jq -r '[.fields.versions[].name] | join(", ")') |
There was a problem hiding this comment.
issue (bug_risk): Handle missing or null Jira fixVersions/versions arrays to avoid jq errors.
These jq expressions will fail with "Cannot iterate over null" if fields.fixVersions or fields.versions is missing or null, and set -e will terminate the script. To make this robust, default to an empty array:
fix_versions=$(echo "$issue_data" | jq -r '[(.fields.fixVersions // [])[].name] | join(", ")')
affected_versions=$(echo "$issue_data" | jq -r '[(.fields.versions // [])[].name] | join(", ")')This preserves your downstream checks while avoiding hard failures on incomplete Jira data.
The paths filter was preventing the workflow from running on PRs. Now it will run on every PR to audit backport status. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
The report will now be visible in the workflow summary page, making it easy to see backport issues without downloading artifacts. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
📝 WalkthroughWalkthroughAdds a new GitHub Actions workflow and an executable CI script that audit open backport pull requests, cross-check them against Jira issues and release versions, and uploads a generated markdown report as a workflow artifact. Changes
Sequence Diagram(s)sequenceDiagram
participant GHA as GitHub Actions
participant Script as Audit Script
participant GHApi as GitHub API
participant JiraApi as Jira API
participant Artifact as Artifact Store
GHA->>Script: Run script with release_branches & credentials
Script->>GHApi: Fetch open backport PRs (up to 1000)
Script->>Script: Group PRs by baseRefName (target branch)
loop Per PR
Script->>GHApi: Query PR/issue events to resolve author (handle bots)
Script->>Script: Extract ROX Jira keys from PR title
end
loop Per Jira key
Script->>JiraApi: Validate issue & fetch metadata
Script->>Script: Store issue JSON
end
loop Per target branch
Script->>JiraApi: Query issues with fixVersion = expected version
Script->>Script: Compute orphaned issues not referenced by PRs
Script->>Script: Append branch summary to report
end
Script->>Artifact: Upload backport-audit-report.md
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes 🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
scripts/ci/audit-backport-prs.sh (1)
262-272: Silent failure on JIRA API errors may mask authentication issues.The
2>/dev/null || echo "{}"pattern silently swallows all curl errors including authentication failures. While the script will log warnings for unfetchable issues, a misconfiguredJIRA_TOKENwould result in all issues appearing unfetchable without a clear root cause message.Consider logging the curl error before falling back:
Proposed improvement
get_jira_issue() { local issue_key="$1" local response + local curl_err - response=$(curl --fail -sSL \ + if ! response=$(curl --fail -sSL \ -u "${JIRA_USER}:${JIRA_TOKEN}" \ "https://${JIRA_BASE_URL}/rest/api/3/issue/${issue_key}?fields=fixVersions,versions,summary,status" \ - 2>/dev/null || echo "{}") + 2>&1); then + warn "Failed to fetch Jira issue $issue_key: $response" + response="{}" + fi echo "$response" }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@scripts/ci/audit-backport-prs.sh` around lines 262 - 272, The get_jira_issue function currently swallows curl errors via "2>/dev/null || echo \"{}\"", hiding authentication or network failures; update get_jira_issue to capture curl's stderr and exit status (e.g., into local variables like curl_err and curl_rc) and if curl fails (non-zero rc) write a clear warning to stderr including the JIRA_USER/JIRA_BASE_URL context and the captured curl_err before returning the fallback "{}", so authentication or other API errors are visible when JIRA_TOKEN/credentials are misconfigured.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@scripts/ci/audit-backport-prs.sh`:
- Around line 199-232: The script incorrectly checks for app/dependabot when
detecting Dependabot authors, so the special handling never runs; update the
string comparisons in the two locations that test the author variables (the if
conditions that compare "$real_author" and "$author") to use "dependabot[bot]"
instead of "app/dependabot" so the gh API login value matches and the
backport-label lookup (the label_adder logic) executes as intended.
---
Nitpick comments:
In `@scripts/ci/audit-backport-prs.sh`:
- Around line 262-272: The get_jira_issue function currently swallows curl
errors via "2>/dev/null || echo \"{}\"", hiding authentication or network
failures; update get_jira_issue to capture curl's stderr and exit status (e.g.,
into local variables like curl_err and curl_rc) and if curl fails (non-zero rc)
write a clear warning to stderr including the JIRA_USER/JIRA_BASE_URL context
and the captured curl_err before returning the fallback "{}", so authentication
or other API errors are visible when JIRA_TOKEN/credentials are misconfigured.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Central YAML (base), Organization UI (inherited)
Review profile: CHILL
Plan: Pro
Run ID: 6f848c09-4e2e-4dfd-aec0-2ea05fce08d8
📒 Files selected for processing (2)
.github/workflows/audit-backport-prs.ymlscripts/ci/audit-backport-prs.sh
| if [[ "$real_author" == "app/dependabot" ]]; then | ||
| local label_adder | ||
| label_adder=$(gh api "repos/stackrox/stackrox/issues/${original_pr}/events" \ | ||
| --jq '.[] | select(.event == "labeled" and (.label.name | contains("backport"))) | .actor.login' \ | ||
| 2>/dev/null | head -1 || echo "") | ||
|
|
||
| if [[ -n "$label_adder" && "$label_adder" != "github-actions[bot]" ]]; then | ||
| author="$label_adder" | ||
| info " PR #$pr_number: rhacs-bot → @app/dependabot → @$label_adder (from #$original_pr, added backport label)" | ||
| else | ||
| author="$real_author" | ||
| info " PR #$pr_number: rhacs-bot → @$real_author (from #$original_pr)" | ||
| fi | ||
| else | ||
| author="$real_author" | ||
| info " PR #$pr_number: rhacs-bot → @$real_author (from #$original_pr)" | ||
| fi | ||
| else | ||
| warn " PR #$pr_number: Could not extract original PR from rhacs-bot backport" | ||
| fi | ||
| fi | ||
|
|
||
| # Check if author is dependabot directly (not via rhacs-bot) - find who added the backport label | ||
| if [[ "$author" == "app/dependabot" ]]; then | ||
| local label_adder | ||
| label_adder=$(gh api "repos/stackrox/stackrox/issues/${pr_number}/events" \ | ||
| --jq '.[] | select(.event == "labeled" and (.label.name | contains("backport"))) | .actor.login' \ | ||
| 2>/dev/null | head -1 || echo "") | ||
|
|
||
| if [[ -n "$label_adder" && "$label_adder" != "github-actions[bot]" ]]; then | ||
| author="$label_adder" | ||
| info " PR #$pr_number: app/dependabot → @$label_adder (added backport label)" | ||
| fi | ||
| fi |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check how gh CLI returns dependabot author in PR JSON
# Look for any existing dependabot PRs in the repo to see the format
gh pr list --repo stackrox/stackrox --author "app/dependabot" --limit 1 --json author --jq '.[].author.login' 2>/dev/null || echo "No dependabot PRs found"
gh pr list --repo stackrox/stackrox --author "dependabot[bot]" --limit 1 --json author --jq '.[].author.login' 2>/dev/null || echo "No dependabot[bot] PRs found"
gh pr list --repo stackrox/stackrox --author "dependabot" --limit 1 --json author --jq '.[].author.login' 2>/dev/null || echo "No dependabot PRs found"Repository: stackrox/stackrox
Length of output: 137
🌐 Web query:
GitHub API dependabot author format login field REST API
💡 Result:
In GitHub’s REST API, Dependabot shows up as a bot user account whose login is:
dependabot[bot](including the literal"[bot]"suffix) — e.g. in PR/webhook payloads likepull_request.user.login, and in API responses that include a GitHub “user” object for the author/committer. [1], [2]
So if you’re matching “Dependabot-authored” activity via REST, compare against dependabot[bot] (and optionally also check user.type == "Bot" when present). [1], [2]
Sources:
[1] GitHub Docs (Dependabot + Actions example checking github.event.pull_request.user.login == 'dependabot[bot]') (docs.github.com)
[2] dependabot/fetch-metadata examples checking github.event.pull_request.user.login == 'dependabot[bot]' (github.com)
Citations:
- 1: https://docs.github.com/en/enterprise-server%403.14/code-security/dependabot/working-with-dependabot/automating-dependabot-with-github-actions?utm_source=openai
- 2: https://github.com/dependabot/fetch-metadata?utm_source=openai
Fix incorrect Dependabot author string format.
Lines 199 and 222 check for "app/dependabot", but GitHub's REST API returns "dependabot[bot]" (with the literal [bot] suffix) as the author login. The current checks will never match actual Dependabot PRs, causing the special Dependabot handling logic to be skipped entirely.
Replace "app/dependabot" with "dependabot[bot]" on both lines.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@scripts/ci/audit-backport-prs.sh` around lines 199 - 232, The script
incorrectly checks for app/dependabot when detecting Dependabot authors, so the
special handling never runs; update the string comparisons in the two locations
that test the author variables (the if conditions that compare "$real_author"
and "$author") to use "dependabot[bot]" instead of "app/dependabot" so the gh
API login value matches and the backport-label lookup (the label_adder logic)
executes as intended.
Only show releases that have PRs or orphaned Jira issues. This keeps the report focused on actionable items. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Changed from: - PR #19875 - ROX-33451 - https://redhat.atlassian.net/browse/ROX-33451 To clickable markdown links: - [#19875](#19875) - [ROX-33451](https://redhat.atlassian.net/browse/ROX-33451) This makes the report cleaner and more readable in GitHub. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #19901 +/- ##
==========================================
+ Coverage 49.57% 49.58% +0.01%
==========================================
Files 2766 2766
Lines 208540 208535 -5
==========================================
+ Hits 103390 103409 +19
+ Misses 97474 97448 -26
- Partials 7676 7678 +2
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
🚀 Build Images ReadyImages are ready for commit fd61a69. To use with deploy scripts: export MAIN_IMAGE_TAG=4.11.x-617-gfd61a6950e |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.github/workflows/audit-backport-prs.yml:
- Around line 13-16: Add an explicit minimal permissions block for the audit job
so the GITHUB_TOKEN scope is limited; update the jobs.audit definition to
include a permissions map (e.g., set GITHUB_TOKEN to only the least privileges
needed such as contents: read and any other specific scopes the audit steps
require) instead of relying on repo defaults, ensuring the permissions are
placed under the audit job definition (jobs.audit).
- Around line 3-6: The workflow triggers pull_request for all PRs but the
'audit' job (which uses the JIRA_TOKEN secret) fails for forked PRs where that
secret is unavailable; fix by gating the job or the specific step that uses
JIRA_TOKEN with an if-condition (e.g., if:
github.event.pull_request.head.repo.full_name == github.repository) or
alternatively remove pull_request from the triggers and restrict to
workflow_dispatch/schedule; apply this change to the 'audit' job or the step
that references JIRA_TOKEN to prevent runs on forks.
- Line 17: Replace the mutable action tags with pinned commit SHAs: locate the
uses entries referencing actions/checkout@v4 and actions/upload-artifact@v4 and
change them to their respective full commit SHA pins (e.g.,
actions/checkout@<full-sha> and actions/upload-artifact@<full-sha>) so the
workflow uses immutable references; obtain the correct full-length SHAs from the
upstream repositories' tags/releases and update both occurrences (checkout and
upload-artifact) accordingly.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Central YAML (base), Organization UI (inherited)
Review profile: CHILL
Plan: Pro
Run ID: 6f6565f0-c2e2-4cb8-a367-488027b90a6e
📒 Files selected for processing (1)
.github/workflows/audit-backport-prs.yml
| on: | ||
| pull_request: | ||
| types: [opened, synchronize] | ||
| workflow_dispatch: |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Read-only reconnaissance: find whether this workflow is required and where it fails.
# 1) Show branch protection required checks
gh api repos/{owner}/{repo}/branches/master/protection --jq '.required_status_checks.checks // []'
# 2) List recent runs and conclusions for this workflow
gh run list --workflow "Audit Backport PRs" --limit 30 --json databaseId,event,conclusion,headBranch,displayTitle,createdAtRepository: stackrox/stackrox
Length of output: 422
🏁 Script executed:
cat -n .github/workflows/audit-backport-prs.ymlRepository: stackrox/stackrox
Length of output: 1340
Guard pull_request trigger against fork PR failures due to unavailable JIRA_TOKEN secret.
The audit job runs on all pull requests (line 4), including forks, but requires JIRA_TOKEN (line 24) which is unavailable on forked PRs. This will cause workflow failures. Gate the step with if: github.event.pull_request.head.repo.full_name == github.repository or restrict the trigger to workflow_dispatch and scheduled runs only.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/audit-backport-prs.yml around lines 3 - 6, The workflow
triggers pull_request for all PRs but the 'audit' job (which uses the JIRA_TOKEN
secret) fails for forked PRs where that secret is unavailable; fix by gating the
job or the specific step that uses JIRA_TOKEN with an if-condition (e.g., if:
github.event.pull_request.head.repo.full_name == github.repository) or
alternatively remove pull_request from the triggers and restrict to
workflow_dispatch/schedule; apply this change to the 'audit' job or the step
that references JIRA_TOKEN to prevent runs on forks.
| jobs: | ||
| audit: | ||
| runs-on: ubuntu-latest | ||
| steps: |
There was a problem hiding this comment.
Set explicit minimal GITHUB_TOKEN permissions for this job.
No permissions block is defined, so token scope may be broader than needed depending on repo defaults.
Suggested fix
jobs:
audit:
+ permissions:
+ contents: read
+ pull-requests: read
+ issues: read
runs-on: ubuntu-latest📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| jobs: | |
| audit: | |
| runs-on: ubuntu-latest | |
| steps: | |
| jobs: | |
| audit: | |
| permissions: | |
| contents: read | |
| pull-requests: read | |
| issues: read | |
| runs-on: ubuntu-latest | |
| steps: |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/audit-backport-prs.yml around lines 13 - 16, Add an
explicit minimal permissions block for the audit job so the GITHUB_TOKEN scope
is limited; update the jobs.audit definition to include a permissions map (e.g.,
set GITHUB_TOKEN to only the least privileges needed such as contents: read and
any other specific scopes the audit steps require) instead of relying on repo
defaults, ensuring the permissions are placed under the audit job definition
(jobs.audit).
| audit: | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - uses: actions/checkout@v4 |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat .github/workflows/audit-backport-prs.ymlRepository: stackrox/stackrox
Length of output: 1088
🌐 Web query:
GitHub Actions pinning commit SHA vs version tags security best practices 2025
💡 Result:
2025 best practice: pin GitHub Actions to full commit SHAs, not floating tags
Why SHA pinning is preferred
- Tags/branches are mutable (e.g.,
@v4,@v1,@main,@latest) and can be moved to a malicious commit if an upstream repo is compromised. - A full-length commit SHA in
uses:is the most reliable immutable reference and is the approach GitHub recommends for limiting supply-chain blast radius. [1]
Recommended approach (practical and auditable)
- Pin third-party actions like this:
Add the version as a comment to keep reviewability while staying immutable. [1]
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- Prefer SHAs that correspond to a tagged release (not an arbitrary commit) so automated tooling can propose clean updates. [1]
- Verify the SHA is from the official upstream repo (not a fork). [1]
When (version) tags can be acceptable
- If you rely on tags, only do so when the project uses immutable releases/tags; GitHub has been rolling out “immutable releases” specifically so tags can’t be changed or deleted once marked immutable. [2]
- Still, SHA pinning remains the safest default, because “immutable tags” can be disabled by repo owners (per GitHub guidance). [1]
Enforce it org-wide (newer governance controls)
- As of Aug 15, 2025, GitHub Actions policies can enforce SHA pinning so workflows fail if an action isn’t pinned to a full SHA; you can also block specific actions/versions. [2]
Managing the downside (updates + alerts)
- Pinning to SHAs improves integrity but reduces “automatic” updates; use Dependabot/Renovate or similar to keep pins current.
- Note: GitHub’s Well-Architected guidance says Dependabot alerts for vulnerable Actions require semantic versions; with SHA pins you’ll generally get version update PRs, not Dependabot security alerts for the action itself. [1]
Extra supply-chain hardening (commonly paired with SHA pinning)
- Avoid actions that fetch mutable dependencies at runtime (e.g., downloading “latest” binaries or using unpinned container images), because the action can still behave mutably even if the repo ref is pinned. [1]
- Consider automated checks to reject non-SHA pins and periodically audit workflow dependencies (common recommendation after multiple ecosystem compromises). [3][4]
Sources: GitHub Well-Architected Actions security recommendations [1], GitHub Changelog (SHA pinning enforcement + immutable releases) [2], Mozilla GitHub workflow security guidance [3], Snyk write-up on action tag mutability and tooling [4].
Pin GitHub Actions to commit SHAs (not floating tags).
Both actions/checkout@v4 and actions/upload-artifact@v4 use mutable floating tags, creating supply-chain risk. Floating version tags can be moved to malicious commits if upstream repositories are compromised. Pin to full-length commit SHAs instead.
Suggested fix
- - uses: actions/checkout@v4
+ - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
fetch-depth: 0 # Need full history for git tag operations
...
- - name: Upload report
- uses: actions/upload-artifact@v4
+ - name: Upload report
+ uses: actions/upload-artifact@c7d193f32eddeaaf28d526ae9f9be63deb68d8a5 # v4.1.2
if: always()
with:
name: backport-audit-report
path: backport-audit-report.mdAlso applies to: 32-32
🧰 Tools
🪛 GitHub Check: style-check
[failure] 17-17: Ratchet - Unpinned Reference
.github/workflows/audit-backport-prs.yml:17:15: The reference actions/checkout@v4 is unpinned. Either pin the reference to a SHA or mark the line with ratchet:exclude.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/audit-backport-prs.yml at line 17, Replace the mutable
action tags with pinned commit SHAs: locate the uses entries referencing
actions/checkout@v4 and actions/upload-artifact@v4 and change them to their
respective full commit SHA pins (e.g., actions/checkout@<full-sha> and
actions/upload-artifact@<full-sha>) so the workflow uses immutable references;
obtain the correct full-length SHAs from the upstream repositories'
tags/releases and update both occurrences (checkout and upload-artifact)
accordingly.
User request: "sort PR in release by author" Makes report easier to scan when multiple PRs need attention from same person. Groups related action items together rather than scattering them across different releases or PR numbers. Partially generated with AI assistance.
User requests: - "replace @app/red-hat-konflux with :konflux:" - "This is how we should mention user https://docs.slack.dev/messaging/formatting-message-text/#mentioning-users" Changes Slack mentions from `@username (U123ABC)` to `<@u123abc>` format which renders as clickable mentions in Slack. Replaces app/red-hat-konflux author with :konflux: emoji for bot-created PRs. Partially generated with AI assistance.
User request: "we need to add asignee to Jira Issues with Missing Metadata" Fetches assignee field from Jira API and displays it in the report. Shows assignee's display name or "Unassigned" if no assignee is set. Format: "ROX-12345: ✗ fixVersion, ✗ affectedVersion (Assignee: John Doe)" Partially generated with AI assistance.
User request: "add also a team" Fetches components field from Jira API and displays it as team information. Components in Jira typically represent team ownership of issues. Format: "ROX-12345: ✗ fixVersion, ✗ affectedVersion (Assignee: John Doe, Team: Platform)" Partially generated with AI assistance.
User feedback: Team should be from customfield_10001 (e.g., "ACS Install") not from components field (which is "OpenShift Operator"). Show both. Now displays: - Team: from customfield_10001.name (e.g., "ACS Install", "ACS Docs") - Component: from components[].name (e.g., "OpenShift Operator") Format: "ROX-33451: ✗ fixVersion, ✗ affectedVersion (Assignee: John Doe, Team: ACS Install, Component: OpenShift Operator)" Partially generated with AI assistance.
Applied security review findings: 1. Added minimal permissions block (contents: read, pull-requests: read) to limit GITHUB_TOKEN scope instead of using repo defaults. 2. Gated job execution for forked PRs where JIRA_TOKEN secret is unavailable using: if: github.event_name == 'workflow_dispatch' || github.event.pull_request.head.repo.full_name == github.repository 3. Replaced mutable action tags with pinned commit SHAs: - actions/checkout@v4 → @34e114876b0b11c390a56381ad16ebd13914f8d5 - actions/upload-artifact@v4 → @ea165f8d65b6e75b540449e92b4886f43607fa02 4. Fixed shellcheck SC2086: quoted $GITHUB_STEP_SUMMARY to prevent globbing and word splitting. Partially generated with AI assistance.
- Read Slack channel IDs from .github/properties - Use dry-slack-channel (C03KSV3N6N8) for PRs and dry-run mode - Use slack-channel (C05AZF8T7GW) for production runs - Post summary with counts and link to full report - Added dry_run input option to workflow_dispatch (default: true) Slack message includes: - Count of PRs missing Jira references - Count of Jira issues with missing metadata - Link to GitHub Actions run for full report Partially generated with AI assistance.
Created dedicated script to post full markdown content to Slack in sections: 1. Header message: summary stats + GitHub Actions link 2. One message per release branch with all content: - PRs missing Jira references (with Slack mentions) - Jira issues with missing metadata (assignee, team, component) - Orphaned Jira issues (if any) Script parses markdown report and posts each release section separately to avoid Slack message size limits. Uses 1-second delay between posts to avoid rate limiting. All content is posted to Slack (not just a summary), formatted with markdown blocks for readability. Partially generated with AI assistance.
Changed from literal `\n` strings to actual newline characters: 1. In awk: Use `print` statements instead of concatenating with `\n` - Each `print` outputs a line with real newline - Sections are separated by empty `print ""` statements 2. In bash: Build content with actual newlines - Use multi-line string concatenation instead of `\n` - Use `printf` to build final message with real newlines This ensures jq properly encodes newlines as JSON escape sequences that Slack will render correctly, instead of displaying literal `\n` characters in the message. Partially generated with AI assistance.
Convert markdown links `[text](url)` to Slack mrkdwn format `<url|text>`: - `[#19609](https://github.com/...)` → `<https://github.com/...|#19609>` - `[ROX-33451](https://redhat.atlassian.net/...)` → `<https://redhat.atlassian.net/...|ROX-33451>` Uses awk regex matching to find and replace all markdown-style links in each line. Handles multiple links per line correctly. This ensures links are clickable in Slack messages instead of displaying as plain markdown text. Partially generated with AI assistance.
Simplified approach: 1. Generate report with Slack-format links directly in audit script - `[#123](url)` → `<url|#123>` - No conversion needed in posting script 2. Post as single message instead of multiple sections - Simpler code, easier to read in Slack - All content in one place with scroll 3. Removed awk link conversion logic - Not needed since report has Slack links natively - Simplified build_full_message to just concatenate content Changes: - audit-backport-prs.sh: Generate <url|text> format instead of [text](url) - post-backport-audit-to-slack.sh: Remove link conversion, post single message Partially generated with AI assistance.
Added PR reference tracking to show which PRs link to each Jira issue that has missing metadata. Example output: - ROX-33451: ✗ fixVersion, ✗ affectedVersion (Assignee: ..., Team: ..., Component: ...) (PRs: #19382) - ROX-34000: ✗ fixVersion, ✗ affectedVersion (Assignee: ..., Team: ..., Component: ...) (PRs: #19876, #19877) Implementation: - Track jira_key → PR numbers mapping while checking metadata - Include PR links at end of Jira issue line - Multiple PRs shown as comma-separated list with links Makes it easier to identify which PRs need Jira metadata updates. Partially generated with AI assistance.
Replaced Unicode symbols with Slack emoji shortcodes: - ✓ → ✅ - ✗ → ❌ Also changed internal delimiter from `:` to `|` to avoid conflicts with emoji shortcodes that contain colons (e.g., `❌`, `✅`). Example output: - ROX-33451: ❌ fixVersion, ❌ affectedVersion (...) - ROX-33925: ✅ fixVersion, ❌ affectedVersion (...) Emojis will render properly in Slack instead of showing Unicode symbols that may not display correctly on all platforms. Partially generated with AI assistance.
Slack blocks have a 3001 character limit. The previous single-message approach failed with: "must be less than 3001 characters [json-pointer:/blocks/0/text/text]" New approach: 1. Post header message with summary stats 2. Split report into sections (~2800 char limit for safety margin) 3. Auto-split long sections and mark as "continued" 4. Post each section as separate message with 1s delay Benefits: - Handles reports of any size - Keeps related content together in sections - Clear continuation markers when sections split - Avoids Slack API errors Removed unused build_full_message function. Partially generated with AI assistance.
The count was always showing 0 because of incorrect grep piping: grep -c "^- <" | grep -c "ROX-" This pipes a number (e.g., "2") to grep, which never matches "ROX-", resulting in 0. Fixed to search for complete Jira link pattern: grep -c "^- <https://redhat.atlassian.net/browse/ROX-" Now correctly counts Jira issues with missing metadata. Partially generated with AI assistance.
Efficiency improvements based on code review: 1. Cache Slack ID lookups - Build lookup cache once at start of generate_report() instead of calling get-slack-user-id.sh script 30+ times. Reduces subprocess spawns and potential file/API reads. 2. Reuse orphaned issues query - Query jq once for orphaned issues per branch, reuse result instead of querying twice (once for has_content check, once for output). 3. Replace "what" comments with "why" - Changed descriptive header comment to explain why the script exists (release management needs). Estimated performance improvement: 2-5 seconds for typical workload. Partially generated with AI assistance.
Reverted caching because get-slack-user-id.sh is a pure function and the overhead of caching (iterating all authors upfront) outweighs the benefit of avoiding duplicate calls in the small output set. Keeping only the useful optimization: reusing jq query results. Partially generated with AI assistance.
Signed-off-by: Tomasz Janiszewski <tomek@redhat.com>
This reverts commit 8424505.
Draft PRs are work-in-progress and shouldn't be flagged as missing Jira references or metadata until they're marked ready for review. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Description
Automated workflow to audit backport PRs and validate Jira metadata:
Example output for release-4.10 (16 open PRs):
Runtime: ~1-2 minutes for typical branch
User-facing documentation
Testing and quality
Automated testing
How I validated my change