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.61% +0.03%
==========================================
Files 2766 2765 -1
Lines 208540 208649 +109
==========================================
+ Hits 103390 103523 +133
+ Misses 97474 97471 -3
+ Partials 7676 7655 -21
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 5b1bdfe. To use with deploy scripts: export MAIN_IMAGE_TAG=4.11.x-681-g5b1bdfeaeb |
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.
This reverts commit e6838f6.
Convert PR numbers from plain text to clickable GitHub links using rich_text cells with multiple link elements. Changes: - PR column now uses rich_text cell with link elements - Each PR is a clickable link: #1234 → #1234 - Multiple PRs separated by commas: #1234, #1235 - Empty state remains plain text: "—" Example table cell structure: { "type": "rich_text", "elements": [{ "type": "rich_text_section", "elements": [ {"type": "link", "url": "https://...", "text": "#1234"}, {"type": "text", "text": ", "}, {"type": "link", "url": "https://...", "text": "#1235"} ] }] } Users can now click PR numbers directly in Slack tables to view the pull requests on GitHub. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Slack rich_text requires using emoji element type rather than
including emoji shortcodes in text elements.
Changes:
- Created _create_table_cell_emoji() for emoji elements
- Extract emoji names from shortcodes (":red_circle:" → "red_circle")
- Use emoji element structure:
{"type": "emoji", "name": "red_circle"}
instead of:
{"type": "text", "text": ":red_circle:"}
Affected columns:
- Urgency: :red_circle:, :large_yellow_circle:, :large_green_circle:
- Fix: :white_check_mark:, :x:
- Affected: :white_check_mark:, :x:
- Priority: :jira-critical:, :jira-major:, :jira-undefined:
This matches Slack's rich_text element pattern where specific
types exist for text, links, and emojis rather than embedding
everything as text.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Replace abbreviated table headers with full descriptive names for better clarity and readability. Changes: - "U" → "Urgency" - "Fix" → "fixVersion" - "Aff" → "affectedVersion" Also updated legend for clarity: - Split into two sections: Urgency and Versions - "Urgency: 🔴 overdue/critical | 🟡 high | 🟢 normal" - "Versions: ✅ present | ❌ missing" Table header now reads: Urgency | Issue | fixVersion | affectedVersion | Priority | Severity | Deadline | PRs Full names make the table self-documenting without needing to reference external documentation. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Replace separate "PRs Missing Jira" and "Issues with Missing Metadata" sections with one unified "Release Contents" table showing everything. Changes: - New _create_all_pr_rows() function collects all PRs and issues - All PRs shown in table (with or without Jira) - All issues shown (complete and incomplete metadata) - PRs without Jira show "No Jira" in Issue column - Problems sorted first (incomplete), then by urgency - Complete items shown after problems Table structure: - Urgency | Issue | fixVersion | affectedVersion | Priority | Severity | Deadline | PRs - Shows ❌ for missing metadata, ✅ for present - "No Jira" for PRs without Jira reference - "—" for N/A fields Benefits: - Single comprehensive view of entire release - Problems highlighted at top (sorted by completeness, then urgency) - No separate sections to cross-reference - Easy to see both what's good and what needs attention Example rows: - ❌ issue: ROX-123 | ❌ | ✅ | :jira-critical: | Important | Due: 2d | #1234 - ✅ issue: ROX-456 | ✅ | ✅ | :jira-major: | — | — | #1235 - No Jira PR: — | No Jira | — | — | — | — | — | #1236 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add Author column to help identify who needs to fix missing Jira references, and move No Jira PRs to the top of the table. Changes: - New "Author" column in table header - Replace "Deadline" with "PR Title" column - No Jira PRs now appear at TOP of table (most urgent to fix) - No Jira PRs show: - Urgency: — (not applicable) - Issue: "No Jira" - fixVersion: ❌ (missing) - affectedVersion: ❌ (missing) - Priority: :jira-undefined: - Severity: — - PR Title: actual PR title - PRs: clickable PR number - Author: Slack mention (@username) - Jira issues show: - Author: — (not applicable) - PR Title: from first associated PR Table columns now: Urgency | Issue | fixVersion | affectedVersion | Priority | Severity | PR Title | PRs | Author Benefits: - No Jira PRs highlighted at top (require immediate attention) - Author mentions make it easy to notify responsible developers - PR titles provide context without clicking through - Clear visual indication that fixVersion/affectedVersion are missing Example No Jira row: — | No Jira | ❌ | ❌ | :jira-undefined: | — | Fix auth bug | #1234 | @john Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
The Author cell needs to properly render Slack user mentions and emojis, which requires using rich_text instead of raw_text. Changes: - Created _create_table_cell_mention() helper function - Handles three mention types: 1. <@u123456> → user element (Slack user mention) 2. :konflux: → emoji element (custom emoji) 3. @username → text element (plain text) - Author cell now uses rich_text for proper rendering Examples: - Slack user: <@U0218FUVDMJ> → renders as clickable @janisz - Konflux bot: :konflux: → renders as konflux emoji - Unknown user: @someuser → renders as plain text This ensures Author mentions are clickable and emojis render properly in the table, matching the behavior of other rich_text cells (Priority, fixVersion, etc.). Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Problem: Author column was only populated for PRs without Jira references, leaving author information missing for PRs linked to Jira issues. Solution: Extract and display unique authors from all PRs associated with each Jira issue, using proper Slack mention/emoji formatting. Multiple authors are comma-separated when multiple PRs reference the same issue. User request: "we should fill authors for all PRs not only for one without jira" Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
New order: Urgency, Issue, PRs, PR Title, Author, fixVersion, affectedVersion, Priority, Severity Rationale: Grouping PR-related columns (PRs, PR Title, Author) together makes it easier to scan and understand the release contents at a glance. User request: "let's reorder columns, start with urgency, then issue, PRs, PR title, author and then the rest" Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
When a Jira issue has both fixVersion and affectedVersion (complete metadata), show the Jira assignee in the Author column without Slack notification. When metadata is missing, show PR authors with Slack mentions to notify them to fix the issue metadata. Rationale: Don't notify people when everything is already correct. Only ping PR authors when they need to take action (add missing fixVersion or affectedVersion). User request: "when everything is fine with jira and prs let's do not notify people so instead slack let's use jira asignee" Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
When an issue has complete metadata (fixVersion + affectedVersion) but is unassigned, mention PR authors via Slack with a note "(issue unassigned)" to request assignment. Behavior: - Complete + assigned: show assignee (no notification) - Complete + unassigned: mention PR authors + "(issue unassigned)" - Incomplete: mention PR authors (existing behavior) User request: "when issue is Unassigned we should menton it by slack handle with information issue unassigned" Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Added instructions section explaining what to do when mentioned: - Missing Jira reference (:x: in Issue column) - Missing fixVersion (:x: in fixVersion column) - Missing affectedVersion (:x: in affectedVersion column) - Unassigned issue (note in Author column) Includes link to Patch Release Process documentation. Changed "No Jira" text to :x: emoji for consistency with other missing metadata indicators. User requests: - "we need to add some audit description, e.g. what people should do when mentione and link to patch releease document" - "change No Jira to :x: as it's missing" Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add Slack team mention (S07D1FQCU9M) to notify the team when backport audit report is posted. User request: "let's mention S07D1FQCU9M team in the description" Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Added alignment support to all table cell helper functions and applied formatting: - Emoji cells (Urgency, Issue ❌, fixVersion, affectedVersion, Priority): centered - PRs column: right-aligned - PR Title column: left-aligned - Other columns (Issue links, Author, Severity): default alignment This improves visual scanning and makes the table easier to read. User request: "can we format the table e.g. center the emoji cells, PRs to right and PR title to Left" Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Added is_wrapped parameter to all cell creation functions: - Text cells: wrapped by default (PR titles, severity, etc.) - Emoji cells: not wrapped by default (icons should stay single-line) - Link cells: wrapped by default (issue links, PR links) - Mention cells: wrapped by default (author mentions) - Placeholder "—": not wrapped (should stay compact) This allows long PR titles and other text content to wrap properly instead of being truncated. User request: "I think we can allow wrapping for collumns is_wrapped=true" Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Separate team mention from "Action Required:" heading with a newline for better readability. User request: "Instead of @acs-release-patch-eng Action Required: If you're mentioned in the table below: we should have new line after team" Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
New column order: Urgency, PRs, Issue, PR Title, Author, fixVersion, affectedVersion, Priority, Severity This puts PR numbers closer to the urgency indicator for easier scanning of which PRs need attention. User request: "actually maybe PRs should be before Issue column" Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Slack table blocks don't support align or is_wrapped on individual cells. Instead, use the column_settings array on the table block to configure alignment and wrapping for entire columns. Column configuration: - Urgency: center, no wrap (emoji) - PRs: right, wrap (can have multiple PRs) - Issue: left, wrap (Jira links) - PR Title: left, wrap (can be long) - Author: left, wrap (can have multiple authors) - fixVersion: center, no wrap (emoji) - affectedVersion: center, no wrap (emoji) - Priority: center, no wrap (emoji) - Severity: left, wrap (text) Fixes: invalid_blocks error with align and is_wrapped properties Reference: https://docs.slack.dev/reference/block-kit/blocks/table-block/#schema-for-column_settings Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
dd07c01 to
bb84d73
Compare
Enhanced documentation and guidance based on ProdSec Jira triage procedures to help developers properly handle vulnerability tracking. Key additions: - 2025 policy: Handle Important/Critical + Moderate with CVSS >= 7.0 - Critical warning: DO NOT CREATE SEPARATE JIRA ISSUES - amend Affected Version/s field instead - Requirement: Close Jira before SLA deadline regardless of severity - Added link to ProdSec Jira Triage Guide alongside Patch Release Process documentation Updated files: - urgency.py: Added ProdSec policy and triage guide reference - models.py: Enhanced JiraIssue docstring with critical field guidance and warnings - report_slack.py: Updated Action Required section with complete guidance and both documentation links - report_markdown.py: Added Action Required header section to markdown reports Reference: https://redhat.atlassian.net/wiki/spaces/StackRox/pages/309334614/How+to+triage+and+resolve+ProdSec+Jiras Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
The Slack report already has comprehensive action guidance. The markdown report is primarily for GitHub Actions logs and doesn't need the duplicated instructions. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Fetch PRs that have been merged since the last release tag by parsing git commit messages for PR numbers. This gives a complete view of what's included in the upcoming release. Implementation: - Added `merged` and `commit_sha` fields to PR model - New function `_fetch_merged_prs_from_commits()` parses git log after last tag to extract PR numbers from commit messages - Fetches PR details from GitHub API, falls back to git commit data (subject, author) if API fails - Merged PRs are combined with open PRs and sorted by urgency - Display :pr-merged: icon in Slack table for merged PRs Behavior: - Branches without tags: only show open PRs (no merged section) - Commits without PR numbers: skipped silently - Git command failures: logged, that branch skipped - GitHub API failures: use commit message as PR title/author User request: "I found a way how to get back merge prs information back. We need to check commits that were merged after the last tag on given branch" Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…th open PRs Slack Block Kit tables have a 100 row limit. Previously we fetched merged PRs for ALL release branches, which caused the audit to exceed this limit when processing multiple branches. Now we only fetch merged PRs for branches that have open backport PRs, which dramatically reduces the row count and keeps the Slack payload within limits. Why: Prevents "invalid_blocks: no more than 100 items allowed" API error Context: User request to only check git for branches with open PRs Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
PR descriptions often contain Jira references that aren't in the title. For example, a commit title "chore(deps): bump opentelemetry" might have "Fixes ROX-12345" only in the PR body. Now we search both title and body for ROX-XXXXX patterns and combine the results. This applies to both open and merged PRs. Why: Ensures complete Jira tracking for backport audit Context: User noted PR metadata includes body with potential Jira refs Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Previously we only fetched author and body, so pr_data.get("title", subject)
always fell back to the commit message. This meant that if a PR title had
Jira references but the commit message didn't, we'd miss them.
Now we fetch the actual PR title from the API, ensuring we extract Jira keys
from the real PR metadata rather than just the merge commit message.
Why: Merged PRs with Jira in PR description but not commit message weren't tracked
Context: User reported merged PRs still missing Jira references
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Merged PRs now show with strikethrough in both Slack and markdown reports: - Slack: Rich text with strike style on PR title cells - Markdown: ~~strikethrough~~ syntax on PR titles - Both: :pr-merged: emoji prefix for merged PRs This makes it immediately clear which PRs are already merged vs still open, even when they're mixed together in the same table. Why: User requested visual distinction for merged vs open PRs Context: PR #19748 example - needed to show merged status clearly Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Changed strikethrough formatting from PR titles to PR numbers, and only apply
to PRs that are closed but not merged (e.g. abandoned backport PRs).
Changes:
- Added state field to PR model ("open", "closed", "merged")
- Fetch all PRs (not just open) to detect closed ones
- Strikethrough on ~~#19748~~ in markdown
- Strikethrough on PR number links in Slack (using strike style)
- Merged PRs show :pr-merged: emoji but no strikethrough
- Removed unused _create_table_cell_text_with_style function
Why: Closed-but-not-merged PRs need visual distinction from active work
Context: User requested strikethrough on PR number, not title
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Closed Jira issues (status = "Closed") now show with strikethrough formatting: - Markdown: ~~ROX-33872~~ - Slack: Jira link with strike style This helps visually distinguish issues that have been closed from active ones, making it clear which items still need attention. Changes: - Added status field to JiraIssue model - Parse status from Jira API response - Apply strikethrough to closed issues in both report formats Why: Closed Jira issues should be visually distinguished like closed PRs Context: User requested strikethrough for ROX-33872 (closed issue) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Fetching all PRs (open + closed + merged) exceeded Slack's 100 row table limit. We only need open PRs from the API since merged PRs are already fetched from git commits after the last tag. Closed-but-not-merged PRs (abandoned/rejected backports) don't need tracking and were causing the table to exceed limits. Why: Fix "no more than 100 items allowed" Slack API error Context: Multiple branches had >100 total rows when including all closed PRs 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