Skip to content

build(ci): detect changes in-job and cancel superseded PR runs - #2010

Merged
lukaszlenart merged 1 commit into
mainfrom
ci-single-queue-wait
Oct 14, 2026
Merged

lukaszlenart merged 1 commit into
mainfrom
ci-single-queue-wait

Conversation

@lukaszlenart

Copy link
Copy Markdown
Member

Why

GitHub Actions jobs on this repo spend far longer queued than running. Over the last ~40 completed runs:

Job Avg queue Max queue Avg run
changes (2 s job) 37 min 97 min 0.1 min
Build and Test (each of 6) ~27 min 80 min ~4 min
CodeQL Analyze 30 min 79 min 7 min
Sonar Scan 31 min 82 min 6 min
OWASP 34 min 99 min 1.7 min

The queue is the shared ASF runner pool, which we cannot change. But we were doubling our own wait: every build job needs: changes, a separate job, so each build queued once for the two-second detection job and then again for its own runner.

Smaller runners such as ubuntu-slim do not help: GitHub documents them as sharing the concurrency model of the standard runners, and the table shows a 2 s job queues as long as a 7 min one.

What changes

  • One queue wait per job. .github/workflows/changes.yml becomes the local composite action .github/actions/detect-changes. The path logic and skip pattern are unchanged. Each job (Maven matrix, CodeQL, OWASP, Sonar) now runs checkout → detect → the real steps gated on steps.changes.outputs.code == 'true'.
    • OWASP and CodeQL lose their job-level if: and gate their steps instead, so a docs-only change reports success rather than skipped. maven.yml already did this.
    • Sonar keeps its fork/Dependabot job condition.
    • CodeQL's job permissions gain pull-requests: read, which the removed changes job already had.
  • Cancel superseded PR runs. Each workflow gets a concurrency group keyed by PR number with cancel-in-progress: true. Pushes are keyed by run_id, so GitHub's rule that a newer pending run replaces an older one cannot drop a stacked build on main.

Trade-off

A docs-only change now briefly takes a runner in each of the four jobs (detection, then every step skipped), instead of one changes runner per workflow. The runner cost is about the same, and code changes lose the whole first wait.

Verification

  • actionlint 1.7.12 is clean apart from two pre-existing shellcheck infos in the unchanged "SonarCloud Scan -- SKIPPED" step.
  • The real check is this PR's own runs: every check should show a single queue wait and no changes / Detect changes … job.

🤖 Generated with Claude Code

The change detection ran as a job of its own that every build job
needed, so each build queued twice on the shared ASF runner pool: once
for the two-second detection job, then again for its own runner. Over
the last ~40 runs that first wait averaged 37 minutes (max 97) before
a build job could even enter the queue.

Detection now runs as a step at the start of each job, from a local
composite action, and gates the remaining steps. Each job queues once.

Every workflow also gets a concurrency group: a new push to a pull
request cancels the superseded run, while pushes get a group per run
so no branch build is dropped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@lukaszlenart
lukaszlenart marked this pull request as ready for review October 14, 2026 08:53
@lukaszlenart
lukaszlenart merged commit 6ad8d09 into main Oct 14, 2026
9 of 17 checks passed
@lukaszlenart
lukaszlenart deleted the ci-single-queue-wait branch October 14, 2026 11:40
@sonarqubecloud

Copy link
Copy Markdown

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