Repository navigation
chore(ci): pin runner images to ubuntu-26.04 - #997
Conversation
Owner directive, 2026-09-29: never float on ubuntu-latest. Ubuntu 26.04 went GA on GitHub-hosted runners 2026-09-17 (actions/runner-images#14747), and ubuntu-latest itself moves to 26.04 between 2026-10-19 and 2026-11-19 (actions/runner-images#14748), so this pins to where the fleet is already heading. Node, .NET and Playwright are installed per-workflow via setup-node/setup-dotnet/playwright install and are unaffected by the runner OS version; Playwright 1.61+ (this repo runs 1.62.1) already recognizes ubuntu26.04-x64 for --with-deps. Leaves ci.yml's image and publish jobs on ubuntu-latest: PR #996 (ci/multi-arch-images) is mid-flight rewriting both into a two-architecture matrix with its own ubuntu-26.04 / ubuntu-26.04-arm runner labels. Adds .github/actionlint.yaml so actionlint (v1.7.12, predates the 26.04 GA) stops flagging the new label as unknown. Adds an AGENTS.md paragraph next to the Actions-SHA-pinning rule recording why the label is pinned, what triggers a review of the pin, and that #995's multi-arch image publishing depends on the Docker version specifically.
CI verification (head
|
| job | this PR (26.04) | main range (24.04, n=3) |
|---|---|---|
| Tests (domain) | 34s | 31s |
| Tests (application) | 58s | 58s |
| Tests (apphost) | 57s | 49s |
| Tests (integration) | 646s | 432–621s |
| Build, audit and schema docs | 113s | 70–105s |
| Web typecheck, test, and build | 509s | 328–498s |
| Image build + Trivy scan | 119s | 131s |
Everything here sits inside the run-to-run noise main already shows on the same ubuntu-latest label (e.g. integration alone spans 432–621s across three same-image runs). Nothing moved materially because of the pin.
3. .github/actionlint.yaml
actionlint v1.7.12 (the current release) was cut before the 26.04 GA and has no ubuntu-26.04 in its bundled runner-label list, so it reports every pinned line as an unknown label (upstream: rhysd/actionlint#682, unresolved). This file adds ubuntu-26.04 and ubuntu-26.04-arm to self-hosted-runner.labels, actionlint's documented extension point for exactly this case (per its own error message), so it stops flagging a known-good GitHub-hosted label. It doesn't silence anything else: ran actionlint over every changed workflow with the config in place and it's clean, and without the config in place it reproduces the same 17 "unknown label" errors this PR's lines would otherwise cause.
4. ci.yml scope
git diff origin/main...HEAD -- .github/workflows/ci.yml touches exactly 6 runs-on lines: classifier-self-test, changes, tests, build-and-test, web, dependency-review. The image and publish jobs are untouched, left on ubuntu-latest for #996 to pin as part of its multi-arch matrix.
Review round 1 on #997 (Codex): the comment had the removal condition backwards. #996 landing is when a workflow starts USING the ubuntu-26.04-arm label, not when the entry becomes safe to drop. Keep both entries; the only correct trigger to remove the file is a released actionlint that knows the labels natively (rhysd/actionlint#682). Also corrects an overstated PR-body/comment claim (Node/.NET/Playwright are the only per-job-installed tools, Docker the only runner-provided one) and softens the optical-size.spec.ts flake framing per the same review round; those are PR body/comment edits, no code change.
Summary
Owner directive, 2026-09-29: "we should not use
ubuntu-latest, we should use a specific version." Pins all 17runs-on: ubuntu-latestoccurrences this PR owns toubuntu-26.04, adds anAGENTS.mdrule that keeps it pinned, and teachesactionlintthe new label.actions/runner-images#14747), andubuntu-latestitself migrates to 26.04 between 2026-10-19 and 2026-11-19 (actions/runner-images#14748). Pinning toubuntu-26.04now lands on where the fleet is already heading, not away from it.actions/setup-dotnet(dotnet-version: 10.0.x), Node viaactions/setup-nodewhere that action is used, and Playwright downloads its own Chromium rather than using a system browser, so none of those three versions ride the runner image. The runner-provided set is larger than just Docker, though:ci.yml'sclassifier-self-testandbuild-and-testjobs invoke the runner's nativenodedirectly (nosetup-nodestep), the simulation scripts (tools/simulation/*.sh) call nativepython3,opensslanddocker compose, and release promotion (docs/releasing.md) uses the runner's nativeghandjq. Checked each against what 26.04 changes and found no incompatibility, but the claim is "checked, no issue found," not "these don't touch the runner image."ubuntu26.04-x64forplaywright install --with-deps;tools/simulation/ui/package.jsonis already on^1.62.1, soe2e-smoke.ymlis safe to pin.ci.yml'simageandpublishjobs are left onubuntu-lateston purpose: feat(ci): publish amd64 and arm64 images #996 (ci/multi-arch-images) owns pinning both as part of its multi-arch matrix..github/actionlint.yamldeclaringubuntu-26.04/ubuntu-26.04-armas known labels. actionlint v1.7.12 predates the 26.04 GA and otherwise reports the label as unknown (rhysd/actionlint#682, unresolved upstream).AGENTS.mdparagraph next to the existing Actions-SHA-pinning rule: the label is pinned deliberately, what it's pinned to, why (a silentubuntu-latestretarget can change what's preinstalled on the image under a pipeline that stays green, and Publish multi-arch images so Cluckwork runs on arm64 (Raspberry Pi and ARM VPS) #995's multi-arch image publishing depends on the Docker version specifically), and what triggers a review (a new Ubuntu LTS reaching GA, or a deprecation announcement for the pinned one).Verification
actionlint(v1.7.12) is clean over every changed workflow with the new config in place, and reproduces 17 unknown-label errors without it, so the config isn't silencing anything else.37bcb2e0: https://github.com/mforce/cluckwork/pull/997/checks. Job durations sit inside the run-to-run noisemainalready shows onubuntu-latest; nothing moved materially. See the verification comment below for the per-job breakdown and one flake investigation.Related: owner directive 2026-09-29 (no tracked issue). Coordinates with #996 on
ci.yml'simage/publishjobs.