ci(codeql): a failed SARIF upload is retried once, in a job of its own (#1499) - #1504
Merged
Merged
Conversation
#1499) analyze uploaded its own SARIF and Analyze (rust) failed on #1468 with the log ending at Uploading results and no finding, the failure class #1461 gave Scorecard and zizmor a retry for. analyze now writes the SARIF without uploading it (upload: never), a step of its own uploads it and may fail without failing the job, and a failure leaves the SARIF as an artifact; analyze-retry, a job of its own because codeql-action refuses a second upload of one category in a job, uploads what the artifacts hold with the same category, analysis key and matrix. A real rejection fails the retry and the run. Closes #1499.
DrunkOnJava
marked this pull request as ready for review
November 5, 2026 23:44
…language, and fail on a SARIF no step uploads (#1499) Second-read findings on the retry. analyze takes upload: failure-only, not never: both leave the SARIF in output and upload nothing of it (the upload is gated on always), but never makes codeql-action skip the failed-run SARIF when the analysis itself dies. The kept artifact overwrites an earlier attempt's. Each language's retry step runs whatever an earlier one did, a last step fails on a kept SARIF no step covers, the retry job drops actions: read (with the analysis key set upload-sarif never asks about the run), and docs/maintaining.md's CodeQL bullet names the retry job. Closes #1499.
…ob's own upload steps (#1499) The first version listed the four file names beside the four steps, which a step removed or a name added would leave out of step. It now fails on a kept SARIF whose retry/<name>.sarif no step of the workflow names, checked on fake kept files and on the workflow with a step's file renamed. Closes #1499.
DrunkOnJava
enabled auto-merge (squash)
November 6, 2026 00:29
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Summary
CodeQL's
analyzejob retries a failed SARIF upload once, in a job of its own, with the same shape #1461 gave Scorecard and zizmor. A real rejection of the file still fails the run.Motivation
#1499:
Analyze (rust)failed on #1468 (CodeQL run 42128776536) atgithub/codeql-action/analyzewith the log ending at "Uploading results" and no finding.arm.shrefuses a head with a red check, so the landing waited for a hand rerun.Changes
.github/workflows/codeql.ymlanddocs/maintaining.md; the matrix stays the four languages and the check namesAnalyze (<language>)do not change.analyzegetsupload: failure-onlyandoutput: ${{ runner.temp }}/codeql-sarif: it writes the SARIF, with the category already in it (analyze passescategoryto the CLI as the SARIF category), and does not upload it. At the pinned commit (v4.39.1,analyze-action.ts) analyze post-processes and writes the files for every value ofupload, and sends them only foralways; the one-category-per-job sentinel is set only by an upload (validateUniqueCategory, insideuploadPostProcessedFiles), sofailure-onlyleaves the later upload step free of it. It isfailure-onlyand notneverbecausenevermakesinit-action-post-helper.ts(prepareFailedSarif) skip the failed-run SARIF when the analysis itself dies, and analyze'saction.ymlcallsnevernot recommended. It still saves the overlay base database onmain.Upload the results to code scanning(upload-sarif, same category,continue-on-error: true, waits for processing as analyze did), uploads that directory. Its failure does not fail the leg.Keep the SARIF for the retryruns only when that step failed and uploads the directory as the artifactcodeql-sarif-<language>(7 days,overwrite: trueso that "Re-run all jobs" can replace the one an earlier attempt left; an artifact name is taken once per run). A healthy leg leaves no artifact.analyze-retry(new job,needs: analyze, runs whenanalyzesucceeded; skipped on a draft and when a leg really failed) downloadscodeql-sarif-*into one directory (merge-multiple, since a single match is otherwise not put in a directory of its own) and, for each language whose SARIF is there, uploads it once more with that language's category, the matrix input the leg has ({"language": "<language>"}, astoJson(matrix)writes it) andCODEQL_ACTION_ANALYSIS_KEY: .github/workflows/codeql.yml:analyze, so the retry is the same configuration in code scanning and leaves no second one holding alerts. The steps are one per language, eachif: ${{ !cancelled() && hashFiles('retry/<codeql language>.sarif') != '' }}(javascript-typescript's file isjavascript.sarif): a rejected file fails its step and the job, and the next language is still tried.Fail on a SARIF no step above uploads, fails on a keptretry/<name>.sarifthat nosarif_file:of the workflow names, so a language added toanalyze's matrix and not to the retry cannot pass with its kept results unsent. It reads the steps from the job's own checkout, not from a second list of names.contents: readandsecurity-events: write. It does not holdactions: read: with the analysis key set, the only callupload-sarifmakes about the run (GET /repos/:owner/:repo/actions/runs/:run_id,getWorkflowRelativePathinapi-client.ts) is not made, and the otheractionsAPI call in the action's source is the cache listinganalyzeuses.upload-lib.tsexports a sentinel into the job'sGITHUB_ENVbefore it sends anything and refuses a second upload of one category in a job ("only one run of the codeql/analyze or codeql/upload-sarif actions is allowed per job per tool/category"), so a second step would fail at once whatever the first died of. Shown by ci: wheels without LTO, CodeQL on ready pull requests, one RE1 pin list, a retried SARIF upload (#1457) #1461 (run 42096477400).docs/maintaining.md's CodeQL bullet names the retry job.upload-sarifat the pinned v4.39.1,upload-artifactv7.0.2,download-artifactv8, as inscorecard.yml).Left alone
filterAlertsByDiffRange(inpostProcessSarifFiles,upload-lib.ts) reads the diff-ranges file from the leg's ownRUNNER_TEMP, which the retry job does not have, while the first attempt, in the leg's job, applies it. The query-timerestrictAlertsToof the diff-informed analysis still narrows the results, so the retried file is the one the analysis wrote, not the one the post-filter would have trimmed further.Testing
Unit tests are banned in this repository: do not add them. Show the change working end to end on real files instead.
tools/check-local.shpasses. N/A: CI-only change; no local compute on this lane.On this head (
4837ed23):actionlintexits 0.python3 tools/ci/check-workflows.py: 0 errors, 0 warnings (66 jobs run steps in 17 workflows, the new job has its timeout).zizmor1.32.0 offline over.github: 45 findings (2 ignored, 37 suppressed, 6 medium: the Dependabot 3-day cooldown), the same count asorigin/main; at the pedantic persona the retry job has no finding. The first version of the retry steps passed the matrix input astoJson(fromJSON('{...}')), which zizmor reports as obfuscation, so it is written out.upload: never,actions: readheld, a list of file names in the last step; run 42130620963, scratch branch, a copy of this workflow with the triggerpushon that branch, the matrix cut toactionsandpython, and in the first upload of both legsCODEQL_ACTION_ANALYSIS_KEYset, so that the action needs no API call before it sends the file, andtokenset to a refused one for theactionsleg only): theactionsleg's upload loggedPost-processing sarif files,Uploading results, thenBad credentials, failed, and the job stayed green;codeql-sarif-actionswas kept;python's upload succeeded and left no artifact; the retry job warned, uploadedretry/actions.sarif(Successfully uploaded results,Analysis upload status is complete) and skipped the other languages; code scanning listed the retried analysis with category/language:actions, analysis key.github/workflows/codeql.yml:analyzeand environment{"language":"actions"}, the same three valuesmain'sactionsanalysis has.upload: failure-only, noactions: read,!cancelled(); run 42133787781, scratch branch, the same copy with the matrixactions,javascript-typescriptandpython, every first upload given the refused token, the retry ofactionsgiven a refused token as well, and the python retry step removed): all three legs' uploads failed withBad credentialsafterUploading results(soanalyzeunderfailure-onlyhad not uploaded, and set no sentinel) and the jobs stayed green; the three artifacts were kept; the retry job (permissionscontents: readandsecurity-events: writeonly) downloaded the three, warned for each, failed theactionsretry step on the refused token, still ran thejavascript-typescriptretry step, which uploaded (Successfully uploaded results,Analysis upload status is complete), skipped rust, and the job endedfailure, so the run did. That is a real rejection staying red with the next language still tried, and the upload working withoutactions: read.actions,javascript,pythonandrustexits 0; a keptgo.sarifexits 1 with the error naming it; the workflow with the python step'ssarif_filerenamed and a keptpython.sarifexits 1. Not run on a runner.upload: failure-onlyfollowed by the upload step, the retry job finding nothing, and all four languages on the real file is the CI run of this head; a retry ofrust(the shape is the others', withrust.sarif's name taken from a run's log);overwriteon a re-run.Audit-honesty checklist
docs/maintaining.md, and the workflow's header comment says what the retry is and why)changelog.d/<section>/. N/A: CI only, as agreed for CI: where the minutes go, and what to change #1457.Legal and contribution hygiene
Breaking changes
None.
Screenshots or output samples
See Testing.
Related issues
Closes #1499