-
Notifications
You must be signed in to change notification settings - Fork 0
ROX-34722: Add periodic Konflux retest #104
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -59,51 +59,41 @@ jobs: | |||||
| workflow-ref: v1 | ||||||
| ``` | ||||||
|
|
||||||
| ## Auto retest failed Konflux builds | ||||||
| ## Periodic retest failed Konflux builds | ||||||
|
|
||||||
| ### Overview | ||||||
|
|
||||||
| When a Konflux build check fails on a pull request, this action will automatically post a `/retest <check-name>` comment to trigger a rebuild. It includes retry limits to prevent infinite retry loops and automatically cleans up old retest comments when new commits are pushed. | ||||||
| Periodically scans all open pull requests for failed Konflux build checks and posts a | ||||||
| `/retest <check-name>` comment to trigger a rebuild. Retries up to `max_retries` times | ||||||
| per check per commit, then stops. Old retest comments from previous commit cycles are | ||||||
| cleaned up automatically so the retry counter always reflects the current commit only. | ||||||
|
|
||||||
| Add the `disable-konflux-auto-retest` label to a PR to opt it out of automatic retesting. | ||||||
|
|
||||||
| ### All options | ||||||
|
|
||||||
| | Input | Description | Required | Default | | ||||||
| |-------|-------------|----------|---------| | ||||||
| | `max_retries` | Maximum number of retries for failed builds | No | `3` | | ||||||
| | `check_name_suffix` | Suffix to filter Konflux build check names (e.g., `-on-push`) | No | `-on-push` | | ||||||
| | `retest_command` | Command to trigger Konflux retest (e.g., /retest). Useful to use non default when OpenShift CI uses the same /retest syntax - prevents OpenShift CI from spamming comments saying it does not understand Konflux-specific retest commands. | No | `/retest` | | ||||||
|
|
||||||
| ## Detailed options | ||||||
|
|
||||||
| - **Automatic Retesting**: Posts retest commands when Konflux builds fail | ||||||
| - **Configurable Retry Limit**: Set maximum retry attempts to prevent infinite loops | ||||||
| - **Auto-Cleanup**: Removes old retest comments when new commits are pushed | ||||||
| - **Filtered Checks**: Only retests checks matching a specific name suffix (e.g., `-on-push`) | ||||||
| - **Custom Retest Command**: Configure the command used to trigger retests (default: `/retest`) | ||||||
| - **Disable via Label**: Add the `disable-konflux-auto-retest` label to a PR to skip automatic retesting | ||||||
|
|
||||||
| | `max_retries` | Maximum number of retries per failed check per commit | No | `3` | | ||||||
| | `check_name_suffix` | Suffix to filter Konflux check names (e.g. `-on-push`, `-on-pull-request`) | No | `-on-push` | | ||||||
| | `retest_command` | Comment body used to trigger a Konflux retest. Use a non-default value when OpenShift CI shares the same `/retest` syntax, to avoid cross-system noise. | No | `/retest` | | ||||||
| | `konflux_app_id` | GitHub App ID for Red Hat Konflux, used to filter check suites | No | `296509` | | ||||||
|
|
||||||
| ### Usage | ||||||
|
|
||||||
| Add this to your repository's workflow file (e.g., `.github/workflows/konflux-auto-retest.yml`): | ||||||
| Create a workflow file in your repository (e.g. `.github/workflows/konflux-retest-periodic.yml`): | ||||||
|
|
||||||
| ```yaml | ||||||
| name: Auto-retest Konflux Builds | ||||||
| name: Periodic Retest Failed Konflux Builds | ||||||
|
|
||||||
| on: | ||||||
| check_run: | ||||||
| types: [completed] | ||||||
| pull_request: | ||||||
| types: [synchronize] | ||||||
| schedule: | ||||||
| - cron: '5,15,25,35,45,55 * * * *' # every 10 minutes | ||||||
| workflow_dispatch: | ||||||
|
|
||||||
| jobs: | ||||||
| retest-failed-konflux-builds: | ||||||
| uses: stackrox/actions/.github/workflows/retest-konflux-builds.yml@v1 | ||||||
| permissions: | ||||||
| pull-requests: write | ||||||
| issues: write | ||||||
| retest: | ||||||
| uses: stackrox/actions/.github/workflows/periodic-retest-konflux-builds.yml@main | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| with: | ||||||
| max_retries: 3 | ||||||
| check_name_suffix: '-on-push' | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm guessing you're suggesting to keep the suffix empty for our workflows? |
||||||
| retest_command: '/retest' | ||||||
| ``` | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,148 @@ | ||
| # NOTE on `issues: write` permission: GitHub treats PR conversation comments as issue comments | ||
| # (both share the same /issues/{number}/comments API endpoint). | ||
| # The `pull-requests: write` permission only controls review-specific actions (approvals, review comments, dismissals). | ||
| # To post a plain comment in a PR conversation, `issues: write` is required. | ||
| name: Periodic Retest Failed Konflux Builds | ||
|
|
||
| on: | ||
| workflow_call: | ||
| inputs: | ||
| max_retries: | ||
| description: 'Maximum number of retries per failed check per commit' | ||
| required: false | ||
| type: number | ||
| default: 3 | ||
|
Comment on lines
+10
to
+14
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🌐 Web query:
💡 Result: Yes, the GitHub Actions workflow_call input type 'number' accepts decimal (floating-point) values [1]. When you define an input with type 'number' in your on.workflow_call configuration, it is designed to handle both integers and floating-point numbers (e.g., 10, 3.14, -5) [1]. The value is exposed and accessed within the called workflow through the inputs context using the syntax ${{ inputs.input_id }} [1][2]. Key technical considerations include: - Strict Typing: workflow_call inputs are strictly typed [3]. When passing values from a caller workflow using the with keyword, it is recommended to pass the value as a literal number (e.g., retries: 3) rather than a quoted string to ensure it is treated correctly as a number [3]. - Expression Coercion: If the value you are passing comes from an expression (which typically yields a string), you should use the fromJSON function to explicitly convert the result into a numeric type before passing it, ensuring compatibility with the 'number' type definition [3]. - Default Values: If no value is provided and no default is specified in the workflow_call definition, the default value for an input of type 'number' is 0 [4][5]. Citations:
🏁 Script executed: #!/bin/bash
set -euo pipefail
file=".github/workflows/periodic-retest-konflux-builds.yml"
printf '%s\n' '--- workflow excerpt ---'
sed -n '1,160p' "$file"
printf '%s\n' '--- relevant references ---'
rg -n -C 3 'max_retries|konflux_app_id|MAX_RETRIES|KONFLUX_APP_ID|retry_count|graphql|Int!' "$file"Repository: stackrox/actions Length of output: 8937 🏁 Script executed: #!/bin/bash
set -u
printf '%s\n' '--- Bash comparison behavior ---'
for max_retries in 1.5 -1 0 3; do
retry_count=0
if [ "$retry_count" -ge "$max_retries" ]; then
result="limit"
else
result="retry"
fi
printf 'MAX_RETRIES=%s result=%s status=%s\n' \
"$max_retries" "$result" "$?" 2>&1
done
printf '%s\n' '--- Arithmetic behavior used by the retry message ---'
for retry_count in 0 1; do
printf 'RETRY_COUNT=%s next=%s\n' "$retry_count" "$((retry_count + 1))"
doneRepository: stackrox/actions Length of output: 480 Validate numeric inputs as integers.
Reject negative and non-integer values before the query and retry logic. 🤖 Prompt for AI Agents |
||
| check_name_suffix: | ||
| description: 'Suffix to filter Konflux check names (e.g. -on-push, -on-pull-request)' | ||
| required: false | ||
| type: string | ||
| default: '-on-push' | ||
|
Comment on lines
+15
to
+19
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just from looking at this: What about |
||
| retest_command: | ||
| description: 'Comment body used to trigger a Konflux retest' | ||
| required: false | ||
| type: string | ||
| default: '/retest' | ||
| konflux_app_id: | ||
| description: 'GitHub App ID for Red Hat Konflux, used to filter check suites' | ||
| required: false | ||
| type: number | ||
| default: 296509 | ||
|
|
||
| jobs: | ||
| periodic-retest-failed-konflux-builds: | ||
| runs-on: ubuntu-latest | ||
|
Comment on lines
+31
to
+33
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win Serialize retest scans for each repository. Two overlapping runs can read the same Add a repository-scoped concurrency group with Proposed concurrency control jobs:
periodic-retest-failed-konflux-builds:
runs-on: ubuntu-latest
+ concurrency:
+ group: periodic-retest-konflux-${{ github.repository }}
+ cancel-in-progress: falseAlso applies to: 130-142 🤖 Prompt for AI Agents
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is legitimate. Instead of per job concurrency control, you could consider a workflow-level concurrency setting (not sure if this works in this re-usable workflow) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🤖 web_search executed:
Length of output: 11773
The called workflow receives the caller’s For this single-job workflow, workflow-level concurrency is preferable. It prevents a duplicate scan from starting a runner. concurrency:
group: periodic-retest-konflux-${{ github.repository }}
cancel-in-progress: false
jobs:
periodic-retest-failed-konflux-builds:
runs-on: ubuntu-latest
You are interacting with an AI system. |
||
|
|
||
| permissions: | ||
| pull-requests: write | ||
| # We need `issues: write` permission to write conversation comments. | ||
| # See top of the file comment for `issues` and conversation comments explanation. | ||
| issues: write | ||
| # required for fetching checks data via GraphQL API | ||
| checks: read | ||
| # required for getting check's content | ||
| contents: read | ||
|
|
||
| steps: | ||
| - name: Scan and retest failed Konflux builds | ||
| env: | ||
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| MAX_RETRIES: ${{ inputs.max_retries }} | ||
| CHECK_NAME_SUFFIX: ${{ inputs.check_name_suffix }} | ||
| RETEST_COMMAND: ${{ inputs.retest_command }} | ||
| KONFLUX_APP_ID: ${{ inputs.konflux_app_id }} | ||
| run: | | ||
| set -euo pipefail | ||
|
|
||
| echo "Starting periodic scan for failed Konflux builds..." | ||
|
|
||
| # A single GraphQL query returns all open PRs with failed Konflux check names and | ||
| # the last commit time, avoiding per-PR gh-pr-checks calls. | ||
| # filterBy:{conclusions:[FAILURE]} only matches completed runs, so in-progress | ||
| # re-runs are naturally excluded. | ||
| # Konflux ignores /retest commands sent to already-running pipelines, so any duplicate | ||
| # comment posted between a /retest and Konflux picking it up is harmless. | ||
| # github.repository is expanded by GHA before the shell sees the string; single quotes | ||
| # are intentional for jq's $appId. | ||
| # shellcheck disable=SC2016 | ||
| PR_DATA=$(gh api graphql \ | ||
| -F appId="${KONFLUX_APP_ID}" \ | ||
| -f query='query($appId: Int!) { | ||
| search(query: "repo:${{ github.repository }} is:pr is:open -label:disable-konflux-auto-retest", type: ISSUE, first: 100) { | ||
| nodes { ... on PullRequest { number | ||
| commits(last: 1) { nodes { commit { | ||
| committedDate | ||
| # Filtered to a single app, usually 1 Konflux check suite per PR in practice; 10 is a safe ceiling. | ||
| checkSuites(first: 10, filterBy: {appId: $appId}) { nodes { | ||
| # Konflux exposes one check run per pipeline component; 50 covers even large repos. | ||
| checkRuns(first: 50, filterBy: {conclusions: [FAILURE]}) { nodes { | ||
|
Comment on lines
+70
to
+77
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift Paginate every GraphQL connection that affects eligibility. This query only scans the first 100 matching PRs. It also truncates each PR at 10 Konflux check suites and 50 failed check runs. The workflow silently omits later results because it does not retrieve Add cursor pagination, or define and document an explicit supported limit. The current implementation does not meet the stated all-open-PR scan behavior. 🤖 Prompt for AI Agents
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @kurlov please address this. |
||
| name | ||
| completedAt | ||
| }} | ||
| }} | ||
| }}} | ||
| }} | ||
| } | ||
| }' \ | ||
| --jq '[.data.search.nodes[] | { | ||
| pr: .number, | ||
| last_commit: .commits.nodes[0].commit.committedDate, | ||
| failed: [.commits.nodes[0].commit.checkSuites.nodes[].checkRuns.nodes[] | ||
| | select(.name | ltrimstr("Red Hat Konflux / ") | endswith("'"$CHECK_NAME_SUFFIX"'")) | ||
| | {name: (.name | ltrimstr("Red Hat Konflux / ")), completed_at: .completedAt}] | ||
|
Comment on lines
+72
to
+91
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift Revalidate the PR head before changing retest comments. The query snapshot can become stale before this loop reaches a PR. If a new commit arrives, this run can post a retest command for a failed check from the previous commit. That new comment can then count against the new commit retry budget. Query the head SHA and verify it is still current before deleting, counting, or posting comments. Also applies to: 102-142 🤖 Prompt for AI Agents
Comment on lines
+90
to
+91
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. While it is true tha our Konflux App is called "Red Hat Konflux", not all of them are: For example, I recently used a staging Konflux cluster, where the GH app name was "Konflux Staging": https://github.com/st3penta/golden-container/runs/90574648533 Can we expose this as a workflow parameter, next to the app ID? |
||
| } | select(.failed | length > 0)]') | ||
|
|
||
| PR_COUNT=$(echo "$PR_DATA" | jq 'length') | ||
| if [ "$PR_COUNT" -eq 0 ]; then | ||
| echo "No open PRs with failed Konflux checks found" | ||
| exit 0 | ||
| fi | ||
|
|
||
| echo "Found $PR_COUNT PRs with failed Konflux checks" | ||
|
|
||
| echo "$PR_DATA" | jq -c '.[]' | while read -r PR_ENTRY; do | ||
| PR_NUMBER=$(echo "$PR_ENTRY" | jq -r '.pr') | ||
| LAST_COMMIT_TIME=$(echo "$PR_ENTRY" | jq -r '.last_commit') | ||
| echo "" | ||
| echo "Processing PR #$PR_NUMBER (last commit: $LAST_COMMIT_TIME)..." | ||
|
|
||
| echo "$PR_ENTRY" | jq -c '.failed[]' | while IFS= read -r CHECK_ENTRY; do | ||
| BASE_CHECK_NAME=$(echo "$CHECK_ENTRY" | jq -r '.name') | ||
| COMPLETED_AT=$(echo "$CHECK_ENTRY" | jq -r '.completed_at') | ||
|
|
||
| if [ -z "$BASE_CHECK_NAME" ]; then | ||
| continue | ||
| fi | ||
|
|
||
| echo " Found failed check: $BASE_CHECK_NAME (failed at: $COMPLETED_AT)" | ||
|
|
||
| # Delete retest comments from previous commit cycles so they cannot be | ||
| # miscounted against the current commit's retry budget. | ||
| gh api --paginate "repos/${{ github.repository }}/issues/$PR_NUMBER/comments" \ | ||
| --jq '[.[] | select( | ||
| .user.login == "github-actions[bot]" and | ||
| (.body | contains("'"$RETEST_COMMAND $BASE_CHECK_NAME"'")) and | ||
| .created_at < "'"$LAST_COMMIT_TIME"'" | ||
| ) | .id] | .[]' | \ | ||
| while read -r COMMENT_ID; do | ||
| gh api -X DELETE "repos/${{ github.repository }}/issues/comments/$COMMENT_ID" | ||
| done | ||
|
|
||
| # Count retest comments posted since the last commit. | ||
| RETRY_COUNT="$(gh api --paginate "repos/${{ github.repository }}/issues/$PR_NUMBER/comments" \ | ||
| --jq '[.[] | select( | ||
| .user.login == "github-actions[bot]" and | ||
| (.body | contains("'"$RETEST_COMMAND $BASE_CHECK_NAME"'")) and | ||
| .created_at > "'"$LAST_COMMIT_TIME"'" | ||
| )] | length')" | ||
|
|
||
| if [ "$RETRY_COUNT" -ge "$MAX_RETRIES" ]; then | ||
| echo " Maximum retry limit ($MAX_RETRIES) reached for $BASE_CHECK_NAME on PR #$PR_NUMBER" | ||
| else | ||
| echo " Retrying $BASE_CHECK_NAME (attempt $((RETRY_COUNT + 1))/$MAX_RETRIES)" | ||
| gh pr comment "$PR_NUMBER" --repo ${{ github.repository }} --body "$RETEST_COMMAND $BASE_CHECK_NAME" | ||
| fi | ||
| done | ||
| done | ||
|
|
||
| echo "" | ||
| echo "Periodic scan complete" | ||
Uh oh!
There was an error while loading. Please reload this page.