Skip to content

Survive concurrent SonarQube analyses (fixes spurious red builds) - #70

Merged
untraceablez merged 1 commit into
mainfrom
fix/sonar-analysis-collision
Jul 27, 2026
Merged

Survive concurrent SonarQube analyses (fixes spurious red builds)#70
untraceablez merged 1 commit into
mainfrom
fix/sonar-analysis-collision

Conversation

@untraceablez

Copy link
Copy Markdown
Owner

Fixes the failure that red-ed main build 20, and re-greens main.

What actually failed

Build 20 passed everything that matters — 452 frontend tests, 417 backend tests, and the quality gate read OK (new_violations 0, coverage 99.7%). It failed anyway, because SonarQube rejected the analysis server-side:

Validation of project failed:
  Date of analysis cannot be older than the date of the last known analysis
  on this project. Value: "2026-07-27T22:54:38+0000".
  Latest analysis: "2026-07-27T22:54:38+0000".

Two analyses submitted in the same second — main's build and a branch build — and SonarQube failed the loser. Confirmed via api/ce/activity:

FAILED   submitted=22:55:27  executed=22:55:32   <- main build 20
SUCCESS  submitted=22:55:27  executed=22:55:32   <- branch build

This is nasty to diagnose: the Jenkins log shows only SonarQube task '<id>' status is 'FAILED' — none of the usual "Pipeline aborted due to quality gate failure" wording — and querying the gate afterwards returns OK, because the winner's result is what got stored. It looks like a passing gate on a failing build.

Why not the obvious fixes

Every build (main, branch, PR) analyses into the single pantrie project, which is what allows the collision.

  • sonar.branch.name would isolate them, but it is not available here. Verified empirically against a throwaway project rather than assumed — the scanner rejects it outright:

    To use the property "sonar.branch.name" and analyze branches, Developer Edition or above is required.

    This server is Community Build 26.7. Adding that property would have broken every build.

  • lock('sonar-pantrie') would be the cleanest fix, but the Lockable Resources plugin is not installed on this controller (checked all 376 installed plugins — no lock/throttle/concurrency plugin). lock() would fail at runtime.

What this does instead

Analysis and the gate check merge into one stage and retry together, so a collision simply re-scans with a fresh timestamp.

Crucially, only the collision path retries. waitForQualityGate throws when the compute-engine task fails — that's what we want to re-run — while abortPipeline: false makes a genuine ERROR gate return normally and fall through to an explicit check. Real quality failures still fail fast instead of paying for a second full scan.

Worst case if waitForQualityGate returns null rather than throwing, the explicit check errors out — i.e. today's behaviour, not worse.

The Jenkinsfile comment records both rejected options so the next person doesn't retry them, including the note to prefer lock() if the plugin is ever installed.

Note

lastStableBuild is currently 19. Merging this produces a fresh main build that should go green and supersede build 20.

🤖 Generated with Claude Code

Main build 20 went red with no quality problem: 452 frontend and 417
backend tests passed and the gate read OK, but the build failed anyway.
The compute-engine task had been rejected server-side:

  Date of analysis cannot be older than the date of the last known
  analysis on this project. Value: "...T22:54:38+0000".
  Latest analysis: "...T22:54:38+0000".

Two analyses submitted in the same second -- main's build and a branch
build -- and SonarQube failed the loser. The Jenkins log shows only
"SonarQube task '<id>' status is 'FAILED'", none of the usual quality
gate wording, and the stored gate still reads OK because the winner's
result is what got saved. Confirmed via api/ce/activity: two tasks, one
SUCCESS one FAILED, identical submittedAt.

The root cause is that every build analyses into the one `pantrie`
project. Isolating branches with sonar.branch.name is NOT available:
verified against a throwaway project that the scanner rejects it with
"Developer Edition or above is required" -- this server is Community
Build 26.7. lock() would be the next-best fix, but the Lockable
Resources plugin is not installed on this controller.

So the analysis and gate check merge into one stage and retry together.
Only the collision path retries: waitForQualityGate throws when the CE
task fails, while abortPipeline:false makes a genuine ERROR gate return
normally, so real failures still fail fast instead of paying for a
second full scan.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@untraceablez
untraceablez merged commit 33e141e into main Jul 27, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant