-
Notifications
You must be signed in to change notification settings - Fork 3k
fix(ci): retry sandbox image builds and file an issue when a release build fails #9916
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
Merged
Merged
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
8a0dfeb
fix(ci): retry sandbox image builds and file an issue when a release …
yiliang114 5324b29
fix(ci): move the image-build failure issue logic to .github/scripts/
yiliang114 adf2482
fix(ci): grant the failure-issue job contents permission and normaliz…
yiliang114 d511a5a
test(ci): pin the failure-issue gate and retry step invariants
yiliang114 8231b5a
fix(ci): gate the failure-issue job on the exported publish decision
yiliang114 cd5717e
fix(ci): skip the failure-issue job for versionless publishing dispat…
yiliang114 83a0dd7
test(ci): pin the PUSH_IMAGE value and the login gate at the definiti…
yiliang114 3e4152a
test(ci): replay the image-build failure-issue script under a gh stub
yiliang114 3cf4347
fix(ci): describe release build job failures without asserting a buil…
yiliang114 154b9a7
fix(ci): preserve annotations and recorded runs when updating the fai…
yiliang114 a9c3623
test(ci): pin the dedup label on create and the open-state filter on …
yiliang114 dc5929d
fix(ci): document the pre-first-step gap in the failure-issue gate
yiliang114 f1f42e8
fix(ci): record build-and-publish-image.yml's shipped size in the wor…
yiliang114 23078f8
Merge remote-tracking branch 'origin/main' into resolve-9916
yiliang114 95d3e23
fix(ci): harden the image-build failure reporter per review round 4 (…
yiliang114 9276ca6
fix(ci): document the version-marker dedup gap on the failure-issue j…
yiliang114 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,162 @@ | ||
| #!/usr/bin/env bash | ||
| # File (or update) one issue per version when the sandbox image build job | ||
| # fails. The job gate is the WHOLE build job — checkout, version processing, | ||
| # QEMU/buildx setup, metadata extraction, registry login, or either build | ||
| # step — so the wording below must not assert which step failed. | ||
| # | ||
| # The body below is the 'File or update the image-build failure issue' step | ||
| # of the file-failure-issue job in .github/workflows/build-and-publish-image.yml. | ||
| # A released npm version without a matching GHCR sandbox image breaks every | ||
| # sandbox-based CI lane (/resolve, sandboxed review, autofix) with | ||
| # "manifest unknown", and nothing else surfaces that state — see #9898. | ||
|
yiliang114 marked this conversation as resolved.
|
||
| set -euo pipefail | ||
|
|
||
| # Tag pushes name the version through the tag; manual recovery dispatches | ||
| # carry it in the version input. | ||
| if [[ "${EVENT_NAME}" == 'push' ]]; then | ||
| version="${TAG_NAME}" | ||
| else | ||
| version="${INPUT_VERSION}" | ||
| fi | ||
|
yiliang114 marked this conversation as resolved.
|
||
| # Both paths may carry a leading `v` (tag names always do; a dispatcher may | ||
| # type one). Normalize once so the dedup marker and the image tag — which the | ||
| # build job publishes without a `v` — always agree, instead of filing a | ||
| # duplicate issue for a `v`-prefixed tag that can never exist. | ||
| version="${version#v}" | ||
| if [[ -z "${version}" ]]; then | ||
| echo "::error::No version resolved for the image-build failure issue." | ||
|
yiliang114 marked this conversation as resolved.
yiliang114 marked this conversation as resolved.
|
||
| exit 1 | ||
| fi | ||
| marker="image-build-failure:${version}" | ||
| marker_html="<!-- ${marker} -->" | ||
|
|
||
| # Dedup by an exact body marker, matched CLIENT-side: GitHub search | ||
| # tokenizes the colon out of the marker, so a search-based lookup | ||
| # never finds the issues this job files. | ||
| issues_file="${RUNNER_TEMP}/open-issues.json" | ||
| gh issue list \ | ||
| --repo "${REPO}" \ | ||
| --state open \ | ||
| --label "${DEDUP_LABEL}" \ | ||
| --json number,body \ | ||
| --limit 200 \ | ||
| > "${issues_file}" | ||
| existing="$( | ||
| jq -r --arg marker_html "${marker_html}" \ | ||
| '.[] | select(.body | contains($marker_html)) | .number' \ | ||
| "${issues_file}" \ | ||
| | head -n 1 | ||
| )" | ||
|
|
||
| # The machine-owned recurrence block: every recorded failed run is a bullet | ||
|
yiliang114 marked this conversation as resolved.
|
||
| # under this marker, newest first. On recurrence ONLY this block is rebuilt — | ||
| # hand-written annotations anywhere else in the body survive verbatim. This is | ||
| # the same merge contract splitOccurrenceBlock()/renderIssueBody() in | ||
| # .github/scripts/ci/main-failure-signature.mjs implements for | ||
| # main-ci-failure-issue.yml; a fix to one must be applied to the other. | ||
| runs_heading='## Failed runs' | ||
| occurrences_marker='<!-- image-build-failure-occurrences -->' | ||
| max_runs=10 | ||
|
|
||
| body_file="${RUNNER_TEMP}/image-build-failure.md" | ||
| head_file="${RUNNER_TEMP}/body-head.md" | ||
| runs_file="${RUNNER_TEMP}/body-runs.txt" | ||
|
|
||
| # The backticks in these formats are literal markdown, not command | ||
| # substitution, so shellcheck's SC2016 expansion warning is disabled. | ||
| # shellcheck disable=SC2016 | ||
| write_prose() { | ||
| printf '%s\n' "${marker_html}" | ||
| printf '\n' | ||
| printf 'The release build job for `%s` failed before `ghcr.io/qwenlm/qwen-code:%s` could be published.\n' "${version}" "${version}" | ||
|
yiliang114 marked this conversation as resolved.
|
||
| printf '\n' | ||
| printf 'Until the image exists, every sandbox-based CI lane (`/resolve`, sandboxed review, autofix) crashes with `manifest unknown` when it installs the matching npm version.\n' | ||
| printf '\n' | ||
| printf 'Open the newest run below to see which step failed, then rerun the failed jobs (transient failures — for example buildx `ETXTBSY` races during the build steps — usually pass on retry), or dispatch `Build and Publish Docker Image` with `version=%s`, `publish=true`.\n' "${version}" | ||
| } | ||
|
|
||
| write_body() { | ||
| { | ||
| cat "${head_file}" | ||
| printf '\n%s\n\n%s\n' "${runs_heading}" "${occurrences_marker}" | ||
| cat "${runs_file}" | ||
| } > "${body_file}" | ||
| } | ||
|
|
||
| if [[ -z "${existing}" ]]; then | ||
| write_prose > "${head_file}" | ||
| printf -- '- %s\n' "${RUN_URL}" > "${runs_file}" | ||
| write_body | ||
| gh issue create \ | ||
| --repo "${REPO}" \ | ||
| --title "Sandbox image for ${version} not published: release build job failed" \ | ||
| --body-file "${body_file}" \ | ||
| --label 'type/bug' \ | ||
| --label "${DEDUP_LABEL}" | ||
| exit 0 | ||
| fi | ||
|
|
||
| # Recurrence: re-plan against the existing body instead of overwriting it. | ||
| existing_body="${RUNNER_TEMP}/existing-body.md" | ||
| gh issue view "${existing}" \ | ||
| --repo "${REPO}" \ | ||
| --json body \ | ||
| --jq '.body' > "${existing_body}" | ||
|
|
||
| tail_file="${RUNNER_TEMP}/body-tail.md" | ||
| : > "${head_file}" | ||
| : > "${runs_file}" | ||
| : > "${tail_file}" | ||
| # Split head / recorded runs / tail around the occurrences marker. Anything | ||
| # that is not a recorded-run bullet below the marker was written by a human; | ||
| # it lands in the tail and is re-emitted with the head prose. | ||
| awk -v marker="${occurrences_marker}" \ | ||
| -v head_f="${head_file}" -v runs_f="${runs_file}" -v tail_f="${tail_file}" ' | ||
| BEGIN { state = "head" } | ||
| state == "head" { | ||
| if ($0 == marker) { state = "runs"; next } | ||
| print > head_f | ||
| next | ||
| } | ||
| state == "runs" { | ||
| line = $0 | ||
| sub(/^[ \t]+/, "", line) | ||
| sub(/[ \t]+$/, "", line) | ||
| if (line == "") next | ||
| if (line ~ /^- https:\/\/[^ ]+\/actions\/runs\/[0-9]+$/) { print > runs_f; next } | ||
| state = "tail" | ||
| } | ||
| state == "tail" { print > tail_f; next } | ||
| ' "${existing_body}" | ||
|
|
||
| # Drop trailing blank lines, and a stranded heading left behind if the | ||
| # occurrences marker line was edited away — the rebuilt block re-emits | ||
| # both. sed, not `head -n -1`: BSD head rejects negative line counts. | ||
| printf '%s\n' "$(cat "${head_file}")" > "${head_file}" | ||
| if [[ "$(tail -n 1 "${head_file}")" == "${runs_heading}" ]]; then | ||
| printf '%s\n' "$(sed '$d' "${head_file}")" > "${head_file}" | ||
| fi | ||
| # Re-check AFTER the strip, which can itself empty the head: fall back to | ||
| # the generated prose so the narrative (and the dedup marker it carries) | ||
| # is never lost. | ||
| if [[ -z "$(cat "${head_file}")" ]]; then | ||
| write_prose > "${head_file}" | ||
| fi | ||
|
|
||
| if [[ -s "${tail_file}" ]]; then | ||
| printf '\n' >> "${head_file}" | ||
| cat "${tail_file}" >> "${head_file}" | ||
| fi | ||
|
|
||
| # Newest first; a re-run of the same run must not add a second line for it. | ||
| # awk (not head) applies the cap so the pipeline never dies on SIGPIPE. | ||
| { printf -- '- %s\n' "${RUN_URL}"; cat "${runs_file}"; } \ | ||
| | awk -v max="${max_runs}" '!seen[$0]++ && ++n <= max' \ | ||
|
yiliang114 marked this conversation as resolved.
|
||
| > "${runs_file}.merged" | ||
| mv "${runs_file}.merged" "${runs_file}" | ||
|
|
||
| write_body | ||
| gh issue edit "${existing}" \ | ||
|
yiliang114 marked this conversation as resolved.
|
||
| --repo "${REPO}" \ | ||
| --body-file "${body_file}" | ||
| echo "Recorded this failure on issue #${existing}." | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.