ci: cancel superseded master runs of test workflows

Many pushes to master left over a hundred runs queued, because every
push used its own concurrency group. Group push runs by ref and cancel
the in-flight run on master for the test, lint and E2E workflows. PR,
tag, schedule and dispatch runs keep groups that a master push cannot
cancel.

The intel cache job pushes one fixed tag, so it now keeps at most one
pending run per event and never cancels a running build.

image.yml and backend.yml are unchanged: they build from the diff of a
single push, so replacing a queued run could skip a code commit. Document
the policy in .agents/ci-caching.md.

Assisted-by: Claude:claude-sonnet-5-5 python3
This commit is contained in:
Ettore Di Giacinto committed 2026-11-03 20:51:26 +00:00
1 parent 988e05dbd5
commit f0864a47da
12 files changed
+109 -20

No files matched your search

+20
View File
@@ -289,6 +289,26 @@ concurrency:
- **PR events** group by PR number → newer pushes to the same PR cancel old runs (intended).
- **Push events** group by `github.sha` → each master commit gets its own run; rapid-fire merges don't cancel each other (this was a real issue prior — two master pushes 11 seconds apart would cancel the first's CI).
### Test, lint and E2E workflows cancel superseded master runs
`test.yml`, `lint.yml`, `build-test.yaml`, `test-extra.yml`, `tests-e2e.yml`, `tests-ui-e2e.yml`, `tests-aio.yml`, `tests-pii-ner-e2e.yml`, `realtime-conformance.yml` and `ci-tools-tests.yaml` use this instead:
```yaml
concurrency:
group: ci-<workflow>-${{ github.event.pull_request.number || (github.event_name == 'push' && github.ref) || github.sha }}-${{ github.repository }}
cancel-in-progress: ${{ github.event_name == 'pull_request' || (github.event_name == 'push' && github.ref == 'refs/heads/master') }}
```
- A newer push to master cancels the older in-flight run of the same workflow. Tests are cumulative, so the newest commit is the one worth testing. This stops a burst of merges from leaving hundreds of queued runs.
- Tag pushes group by their own tag ref, and schedule and `workflow_dispatch` runs group by SHA, so a master push never cancels them.
- The cost: the run of an older commit shows as cancelled, so a failure that the newer commit also contains is reported by the newer run, not the older one.
### Image workflows are deliberately not cancelled
`image.yml` and `backend.yml` keep the per-SHA group. Both decide what to build from the diff of one push (`event.before` to `sha`): the `changes` job in `image.yml`, `scripts/changed-backends.js` in `backend.yml`. If a queued run were replaced by a newer one, the newer run would only see its own diff. A code commit followed by a gallery-only commit would then build nothing. Do not add a pending-replacement group to them until the diff base is the last commit that was actually built.
`generate_intel_image.yaml` pushes one fixed tag, so its group is `intel-cache-<event>-<repo>` with `cancel-in-progress: false`: a running build finishes, and a newer pending run replaces an older pending one.
## Self-warming, no separate populator
There is no cron job that pre-warms the BuildKit cache for individual backends. The production builds *are* the populators. The first master build of a given matrix entry pays the cold cost; subsequent same-entry master builds reuse everything that hasn't changed (apt installs, gRPC compile in the variant `builder-fromsource` stage or skipped entirely when consuming `base-grpc-*`, Python wheel installs, etc.). The base-images workflow's weekly cron is the closest thing to a populator and only refreshes the prebuilt builder bases.
+8 -2
View File
@@ -1,3 +1,9 @@
# Concurrency policy: a newer push to master cancels the in-flight run of this
# workflow on master, because the newer commit contains the older one and the
# result that matters is the newest. PR runs group by PR number (as before),
# tag pushes group by their own tag ref, and schedule/dispatch runs group by
# commit SHA, so none of those is ever cancelled by a master push.
# See .agents/ci-caching.md, section "Concurrency".
name: Build test
on:
@@ -44,8 +50,8 @@ on:
- 'Entitlements.plist'
concurrency:
group: ci-build-test-${{ github.event.pull_request.number || github.sha }}-${{ github.repository }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
group: ci-build-test-${{ github.event.pull_request.number || (github.event_name == 'push' && github.ref) || github.sha }}-${{ github.repository }}
cancel-in-progress: ${{ github.event_name == 'pull_request' || (github.event_name == 'push' && github.ref == 'refs/heads/master') }}
jobs:
build-test:
+10
View File
@@ -1,3 +1,9 @@
# Concurrency policy: a newer push to master cancels the in-flight run of this
# workflow on master, because the newer commit contains the older one and the
# result that matters is the newest. PR runs group by PR number (as before),
# tag pushes group by their own tag ref, and schedule/dispatch runs group by
# commit SHA, so none of those is ever cancelled by a master push.
# See .agents/ci-caching.md, section "Concurrency".
---
# The packages under .github/ci/ are invisible to `go list ./...`, so neither
# `make lint` nor the repository test run ever touches them. Their specs are
@@ -13,6 +19,10 @@ on:
- master
paths:
- '.github/ci/**'
concurrency:
group: ci-tools-tests-${{ github.event.pull_request.number || (github.event_name == 'push' && github.ref) || github.sha }}-${{ github.repository }}
cancel-in-progress: ${{ github.event_name == 'pull_request' || (github.event_name == 'push' && github.ref == 'refs/heads/master') }}
jobs:
ci-tools:
name: 'Test the .github/ci generators'
+7 -2
View File
@@ -1,3 +1,8 @@
# Concurrency policy: this job pushes one fixed tag, so only the newest build
# matters. A running build is never cancelled (cancel-in-progress is false);
# GitHub keeps at most one pending run per group and replaces an older pending
# run with a newer one. Push and workflow_dispatch use separate groups, so a
# manual run is never replaced by a push.
name: 'generate and publish intel docker caches'
on:
@@ -7,8 +12,8 @@ on:
- master
concurrency:
group: intel-cache-${{ github.event.pull_request.number || github.sha }}-${{ github.repository }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
group: intel-cache-${{ github.event_name }}-${{ github.repository }}
cancel-in-progress: false
jobs:
generate_caches:
+8 -2
View File
@@ -1,4 +1,10 @@
---
# Concurrency policy: a newer push to master cancels the in-flight run of this
# workflow on master, because the newer commit contains the older one and the
# result that matters is the newest. PR runs group by PR number (as before),
# tag pushes group by their own tag ref, and schedule/dispatch runs group by
# commit SHA, so none of those is ever cancelled by a master push.
# See .agents/ci-caching.md, section "Concurrency".
name: 'lint'
on:
@@ -16,8 +22,8 @@ on:
- master
concurrency:
group: ci-lint-${{ github.event.pull_request.number || github.sha }}-${{ github.repository }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
group: ci-lint-${{ github.event.pull_request.number || (github.event_name == 'push' && github.ref) || github.sha }}-${{ github.repository }}
cancel-in-progress: ${{ github.event_name == 'pull_request' || (github.event_name == 'push' && github.ref == 'refs/heads/master') }}
jobs:
golangci-lint:
+8 -2
View File
@@ -1,4 +1,10 @@
---
# Concurrency policy: a newer push to master cancels the in-flight run of this
# workflow on master, because the newer commit contains the older one and the
# result that matters is the newest. PR runs group by PR number (as before),
# tag pushes group by their own tag ref, and schedule/dispatch runs group by
# commit SHA, so none of those is ever cancelled by a master push.
# See .agents/ci-caching.md, section "Concurrency".
name: 'realtime-conformance'
# Verifies the realtime state-machine implementations conform to their formal
@@ -36,8 +42,8 @@ on:
- 'scripts/realtime-conformance.sh'
concurrency:
group: realtime-conformance-${{ github.event.pull_request.number || github.sha }}-${{ github.repository }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
group: realtime-conformance-${{ github.event.pull_request.number || (github.event_name == 'push' && github.ref) || github.sha }}-${{ github.repository }}
cancel-in-progress: ${{ github.event_name == 'pull_request' || (github.event_name == 'push' && github.ref == 'refs/heads/master') }}
jobs:
conformance:
+8 -2
View File
@@ -1,4 +1,10 @@
---
# Concurrency policy: a newer push to master cancels the in-flight run of this
# workflow on master, because the newer commit contains the older one and the
# result that matters is the newest. PR runs group by PR number (as before),
# tag pushes group by their own tag ref, and schedule/dispatch runs group by
# commit SHA, so none of those is ever cancelled by a master push.
# See .agents/ci-caching.md, section "Concurrency".
name: 'Tests extras backends'
on:
@@ -10,8 +16,8 @@ on:
- '*'
concurrency:
group: ci-tests-extra-${{ github.event.pull_request.number || github.sha }}-${{ github.repository }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
group: ci-tests-extra-${{ github.event.pull_request.number || (github.event_name == 'push' && github.ref) || github.sha }}-${{ github.repository }}
cancel-in-progress: ${{ github.event_name == 'pull_request' || (github.event_name == 'push' && github.ref == 'refs/heads/master') }}
jobs:
detect-changes:
+8 -2
View File
@@ -1,4 +1,10 @@
---
# Concurrency policy: a newer push to master cancels the in-flight run of this
# workflow on master, because the newer commit contains the older one and the
# result that matters is the newest. PR runs group by PR number (as before),
# tag pushes group by their own tag ref, and schedule/dispatch runs group by
# commit SHA, so none of those is ever cancelled by a master push.
# See .agents/ci-caching.md, section "Concurrency".
name: 'tests'
on:
@@ -67,8 +73,8 @@ on:
workflow_dispatch:
concurrency:
group: ci-tests-${{ github.event.pull_request.number || github.sha }}-${{ github.repository }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
group: ci-tests-${{ github.event.pull_request.number || (github.event_name == 'push' && github.ref) || github.sha }}-${{ github.repository }}
cancel-in-progress: ${{ github.event_name == 'pull_request' || (github.event_name == 'push' && github.ref == 'refs/heads/master') }}
jobs:
tests-linux:
+8 -2
View File
@@ -1,4 +1,10 @@
---
# Concurrency policy: a newer push to master cancels the in-flight run of this
# workflow on master, because the newer commit contains the older one and the
# result that matters is the newest. PR runs group by PR number (as before),
# tag pushes group by their own tag ref, and schedule/dispatch runs group by
# commit SHA, so none of those is ever cancelled by a master push.
# See .agents/ci-caching.md, section "Concurrency".
name: 'tests-aio'
# Runs the all-in-one (AIO) Docker image with real backends + real models.
@@ -22,8 +28,8 @@ on:
- '*'
concurrency:
group: ci-tests-aio-${{ github.event.pull_request.number || github.sha }}-${{ github.repository }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
group: ci-tests-aio-${{ github.event.pull_request.number || (github.event_name == 'push' && github.ref) || github.sha }}-${{ github.repository }}
cancel-in-progress: ${{ github.event_name == 'pull_request' || (github.event_name == 'push' && github.ref == 'refs/heads/master') }}
jobs:
tests-aio:
+8 -2
View File
@@ -1,4 +1,10 @@
---
# Concurrency policy: a newer push to master cancels the in-flight run of this
# workflow on master, because the newer commit contains the older one and the
# result that matters is the newest. PR runs group by PR number (as before),
# tag pushes group by their own tag ref, and schedule/dispatch runs group by
# commit SHA, so none of those is ever cancelled by a master push.
# See .agents/ci-caching.md, section "Concurrency".
name: 'E2E Backend Tests'
on:
@@ -46,8 +52,8 @@ on:
- '.github/workflows/tests-e2e.yml'
concurrency:
group: ci-tests-e2e-backend-${{ github.event.pull_request.number || github.sha }}-${{ github.repository }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
group: ci-tests-e2e-backend-${{ github.event.pull_request.number || (github.event_name == 'push' && github.ref) || github.sha }}-${{ github.repository }}
cancel-in-progress: ${{ github.event_name == 'pull_request' || (github.event_name == 'push' && github.ref == 'refs/heads/master') }}
jobs:
tests-e2e-backend:
+8 -2
View File
@@ -1,4 +1,10 @@
---
# Concurrency policy: a newer push to master cancels the in-flight run of this
# workflow on master, because the newer commit contains the older one and the
# result that matters is the newest. PR runs group by PR number (as before),
# tag pushes group by their own tag ref, and schedule/dispatch runs group by
# commit SHA, so none of those is ever cancelled by a master push.
# See .agents/ci-caching.md, section "Concurrency".
name: 'PII NER tier E2E (live GGUF, CPU)'
# Runs the real privacy-filter GGUF NER tier end-to-end on CPU — the gap the
@@ -46,8 +52,8 @@ on:
- '.github/workflows/tests-pii-ner-e2e.yml'
concurrency:
group: ci-tests-pii-ner-e2e-${{ github.event.pull_request.number || github.sha }}-${{ github.repository }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
group: ci-tests-pii-ner-e2e-${{ github.event.pull_request.number || (github.event_name == 'push' && github.ref) || github.sha }}-${{ github.repository }}
cancel-in-progress: ${{ github.event_name == 'pull_request' || (github.event_name == 'push' && github.ref == 'refs/heads/master') }}
jobs:
tests-pii-ner-e2e:
+8 -2
View File
@@ -1,4 +1,10 @@
---
# Concurrency policy: a newer push to master cancels the in-flight run of this
# workflow on master, because the newer commit contains the older one and the
# result that matters is the newest. PR runs group by PR number (as before),
# tag pushes group by their own tag ref, and schedule/dispatch runs group by
# commit SHA, so none of those is ever cancelled by a master push.
# See .agents/ci-caching.md, section "Concurrency".
name: 'UI E2E Tests'
on:
@@ -45,8 +51,8 @@ on:
- '.github/actions/**'
concurrency:
group: ci-tests-ui-e2e-${{ github.event.pull_request.number || github.sha }}-${{ github.repository }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
group: ci-tests-ui-e2e-${{ github.event.pull_request.number || (github.event_name == 'push' && github.ref) || github.sha }}-${{ github.repository }}
cancel-in-progress: ${{ github.event_name == 'pull_request' || (github.event_name == 'push' && github.ref == 'refs/heads/master') }}
jobs:
tests-ui-e2e: