ci: move SonarCloud branch scans out of the merge queue, serialize per branch#21669
Conversation
…r branch
SonarCloud rejects a branch analysis dated older than the branch's
latest processed one. All merge-queue entries analyze the target branch,
so when entry N's report uploaded after entry N+1's, SonarCloud rejected
it and flipped the commit's SonarCloud check to cancelled ("Date of
analysis cannot be older than the date of the last known analysis").
Seen on 5027d25, where the scans of three stacked queue entries
(#21648, #21584, #21640) raced and #21648's analysis lost.
Restructure so branch analyses can never be submitted out of order:
- merge_group runs keep `make test-sonar-coverage` as a gate check but
no longer scan; they upload coverage-test-all.out as a short-retention
artifact instead.
- A new push-triggered SonarCloud Branch Scan workflow (main and
release/**, matching the branches SonarCloud tracks) downloads that
artifact — the merge queue fast-forwards the target branch to the
already-tested merge commit, so the pushed SHA equals the merge-queue
run's head SHA and the artifact maps to the commit exactly — and runs
only the scanner (~7 min instead of ~25). If no artifact exists
(direct push, expired retention), it falls back to running the tests.
- Scans are serialized per branch FIFO via `concurrency.queue: max`
(GitHub Actions, 2026-05), so every merged commit is analyzed, in
order, with no runs cancelled in merge bursts.
- PR scans (Sonar PR analyses, no main-branch date ordering) and
cache-warming runs are unchanged.
actionlint (≤ v1.7.12) does not know the `queue` key yet, so lint.yml
gains a narrow --ignore alongside the existing workspace one.
A failed analysis upload also no longer involves the gate: scanner
failures now surface on a post-merge run instead, while PR runs keep
exercising the scanner so breakage is still caught before merge.
There was a problem hiding this comment.
Pull request overview
This PR restructures SonarCloud branch analysis so merge-queue runs generate and upload coverage data but do not submit branch scans, while a dedicated post-merge workflow performs the branch scan in a per-branch FIFO-serialized manner to prevent SonarCloud’s “out-of-order analysis date” rejections.
Changes:
- Add a
scan-onlymode to the reusable Sonar workflow to reuse a merge-queue coverage artifact (with a fallback to running coverage tests if missing). - Skip SonarCloud scanning during
merge_groupruns and instead uploadcoverage-test-all.outas a short-retention artifact. - Introduce a
SonarCloud Branch Scanworkflow triggered on pushes tomain/release/**, serialized viaconcurrency.queue: max, and adjust actionlint ignores accordingly.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| .github/workflows/sonar.yml | Adds scan-only artifact reuse logic; skips scanning on merge-queue runs; uploads coverage artifact for post-merge scans. |
| .github/workflows/sonar-branch-scan.yml | New push-triggered workflow to run branch scans serially per branch using the reusable sonar workflow. |
| .github/workflows/lint.yml | Extends actionlint ignores to tolerate the new concurrency.queue key. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| { | ||
| echo "found=$found" | ||
| echo "run-id=$run_id" | ||
| } >> "$GITHUB_OUTPUT" | ||
|
|
||
| - name: Download merge-queue coverage artifact | ||
| if: inputs.scan-only && steps.coverage-artifact.outputs.found == 'true' | ||
| uses: actions/download-artifact@v8 | ||
| with: | ||
| name: sonar-coverage | ||
| run-id: ${{ steps.coverage-artifact.outputs.run-id }} | ||
| github-token: ${{ github.token }} |
There was a problem hiding this comment.
This is a false positive — GitHub Actions' expression parser allows hyphens in property names accessed via dot notation. The proof is in this same file: the pre-existing, CI-proven steps.engine-cache.outputs.cache-hit != 'true' (a hyphenated step id and a hyphenated output), and the canonical actions/cache steps.<id>.outputs.cache-hit pattern used by millions of workflows. So outputs.run-id resolved correctly.
That said, I've renamed the output to run_id (cbe966a) — underscore is more idiomatic for user-defined outputs and removes any tooling/reader doubt.
|
@claude review |
|
Claude finished @taratorio's task in 2m 18s —— View job Code Review
The design is sound. Moving branch scans out of the merge queue and serializing them via
|
Underscore is the idiomatic form for user-defined step outputs. No behavior change (hyphenated outputs work with dot notation, as the pre-existing steps.engine-cache.outputs.cache-hit reference shows); the rename just preempts reviewer/tooling confusion.
|
Addressed the review feedback (thanks @claude / Copilot): 1. 2. 3. Merge-queue fast-forward assumption. Already documented — see the comment at the
|
|
Claude finished @taratorio's task in 0s —— View job I'll analyze this and get back to you. |
Problem
SonarCloud rejects a branch analysis dated older than the branch's latest processed one. Every merge-queue entry analyzes the target branch, so when queue entry N's report uploads after entry N+1's, SonarCloud rejects N's with "Date of analysis cannot be older than the date of the last known analysis" and flips that commit's SonarCloud Code Analysis check to
cancelled.Seen on
5027d259c2: three stacked queue entries (#21648, #21584, #21640) scanned concurrently; #21584 started 6s later but uploaded 21s earlier, so #21648's analysis was rejected as out of order. No impact on the gate (the scan step iscontinue-on-errorand the merge succeeded), but it leaves a spurious failed check on a merged commit.Fix
Make it structurally impossible to submit branch analyses out of order:
merge_groupruns keepmake test-sonar-coverageas a gate check but no longer scan. They uploadcoverage-test-all.outas a 7-day artifact.SonarCloud Branch Scanworkflow (pushonmain+release/**, matching the branches SonarCloud tracks) downloads that artifact and runs only the scanner (~7 min vs ~25). The merge queue fast-forwards the target branch to the already-tested merge commit, so the pushed SHA equals the merge-queue run's head SHA and the artifact maps to the commit exactly. If no artifact exists (direct push, or expired retention), it falls back to running the tests itself.concurrency.queue: max(GitHub Actions, 2026-05), so every merged commit is analyzed, in order, with no runs cancelled during merge bursts.A failed analysis upload no longer touches the gate at all: scanner failures now surface on a post-merge run, while PR runs keep exercising the scanner so config/scanner breakage is still caught before merge.
Notes
actionlint(≤ v1.7.12, the latest release) does not know thequeueconcurrency key yet, solint.ymlgets a narrow--ignorealongside the existingworkspaceone. Removable once actionlint learns the key.concurrency.queue: maxis ~1 month old — first real merge-burst after this lands is where FIFO ordering gets exercised live.Testing
make lint,actionlint(CI flags), andzizmor(repo config) all clean; the new workflow is finding-free.