fix(ci): pin GitHub-hosted runners to ubuntu-24.04

`ubuntu-latest` moved from ubuntu-24.04 to ubuntu-26.04 on its own
schedule, in the middle of run 39166850273, and every job that landed on
the new image met a different OS: sudo-rs instead of sudo, no ip_tables
loaded, and now the NFS probe-write of svc-storage-nfs-client failing on
26.04 swarm nodes. The same branch went red without a code change.

Every `runs-on` now names `ubuntu-24.04`. GitHub still rebuilds that image
weekly, so patches and tool updates keep arriving; only the release jump
becomes a deliberate change of the label. The CodeQL job drops its dead
swift/macos-latest branch, since no swift language is in its matrix. act
already maps ubuntu-24.04 to the local platform image.

A lint rejects any job whose `runs-on` contains a `*-latest` label.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
kevinveenbirkenbachandClaude Opus 5.5 committed 2026-10-19 15:57:12 +02:00
1 parent 155ca9a8a9
commit 52ad8549e0
30 files changed
+85 -51

No files matched your search

+1 -1
View File
@@ -43,7 +43,7 @@ concurrency:
jobs:
build:
name: 💿 Image
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
permissions:
contents: read
packages: write
@@ -78,7 +78,7 @@ concurrency:
jobs:
sync:
name: 🪞 Mirror
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
permissions:
contents: read
packages: write
+1 -1
View File
@@ -10,7 +10,7 @@ permissions:
jobs:
lint:
name: 🧹 Lint
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
timeout-minutes: 45
env:
INFINITO_LINT_RUNNER: host
+2 -2
View File
@@ -343,7 +343,7 @@ jobs:
- test-deploy-chunk-2
- test-deploy-chunk-3
if: ${{ always() && github.ref == 'refs/heads/main' && (contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')) }}
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
permissions:
issues: write
actions: read
@@ -374,7 +374,7 @@ jobs:
- lint
- security-codeql
if: ${{ always() }}
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
steps:
- name: 🛑 Fail if any upstream job failed or was cancelled
if: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }}
+2 -2
View File
@@ -30,7 +30,7 @@ env:
jobs:
check-ci-images:
name: 🔎 Check CI images
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
outputs:
missing: ${{ steps.check.outputs.missing }}
ci_tag: ${{ steps.citag.outputs.ci_tag }}
@@ -123,7 +123,7 @@ jobs:
needs.build-ci-images-if-missing.result == 'success' ||
needs.build-ci-images-if-missing.result == 'skipped'
)
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
permissions:
contents: read
packages: write
+2 -2
View File
@@ -163,7 +163,7 @@ env:
jobs:
discover:
name: 🔍 Chunk ${{ inputs.index }}${{ inputs.marker }}
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
timeout-minutes: 60
outputs:
apps: ${{ steps.discover.outputs.apps }}
@@ -251,7 +251,7 @@ jobs:
deploy:
name: "${{ matrix.label }}"
if: needs.discover.outputs.apps != '' && needs.discover.outputs.apps != '[]'
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
timeout-minutes: 355
needs: discover
+1 -1
View File
@@ -23,7 +23,7 @@ permissions:
jobs:
dns:
name: 🌐 DNS
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
timeout-minutes: 30
steps:
- name: 🧹 Free disk space
+1 -1
View File
@@ -10,7 +10,7 @@ permissions:
jobs:
test-install-make:
name: 🛠️ Makefile
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
timeout-minutes: 30
env:
NIX_CONFIG: |
@@ -10,7 +10,7 @@ permissions:
jobs:
test-install-pkgmgr:
name: 📦 PKGMGR
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
timeout-minutes: 30
env:
NIX_CONFIG: |
+2 -2
View File
@@ -21,7 +21,7 @@ permissions:
jobs:
prepare:
name: 🧑‍💻 Workspace Matrix
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
timeout-minutes: 5
outputs:
matrix: ${{ steps.matrix.outputs.matrix }}
@@ -44,7 +44,7 @@ jobs:
test-workspace:
name: ${{ matrix.icon }} Workspace (${{ matrix.label }})
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
timeout-minutes: 345
needs: prepare
+1 -1
View File
@@ -10,7 +10,7 @@ permissions:
jobs:
test:
name: 🧪 Test
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
timeout-minutes: 45
env:
INFINITO_TEST_RUNNER: host
+3 -3
View File
@@ -84,7 +84,7 @@ permissions:
jobs:
validation:
name: ✅ Validation
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
steps:
- name: ⬇️ Checkout the calling ref for its tooling
uses: actions/checkout@v7.0.0
@@ -148,7 +148,7 @@ jobs:
wait-fork-prereq-run:
name: ⏳ Wait for fork run
if: ${{ inputs.fork_prereqs }}
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
env:
WAIT_REQUIRED: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository && !inputs.trusted_pr }}
steps:
@@ -194,7 +194,7 @@ jobs:
name: 🚦 Gate Dependencies
needs: wait-fork-prereq-run
if: ${{ !cancelled() && inputs.fork_prereqs }}
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v7.0.0
- name: ✅ Assert privileged prerequisites are ready
+3 -3
View File
@@ -12,7 +12,7 @@ permissions:
jobs:
stale:
name: 🏷️ Close inactive issues and pull requests
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
permissions:
issues: write
pull-requests: write
@@ -45,7 +45,7 @@ jobs:
delete-stale-branches:
name: 🌿 Delete inactive branches
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
permissions:
contents: write
issues: write
@@ -65,7 +65,7 @@ jobs:
cleanup-ghcr-ci-images:
name: 📦 Delete old GHCR CI images
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
permissions:
contents: read
packages: write
+1 -1
View File
@@ -18,7 +18,7 @@ jobs:
benchmark-collection-sources:
name: ⚖️ Time Galaxy against the git fallback
if: vars.CI_ENABLE_AUTO_UPDATES == 'true'
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
env:
HISTORY_PATH: build/collection-source-history.json
steps:
+1 -1
View File
@@ -22,7 +22,7 @@ concurrency:
jobs:
cleanup:
name: 🧹 Delete old ci-* image versions
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
permissions:
actions: read
contents: read
+1 -1
View File
@@ -61,7 +61,7 @@ concurrency:
jobs:
sync:
name: 🪞 Mirror
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
permissions:
contents: read
packages: write
+1 -1
View File
@@ -19,7 +19,7 @@ env:
jobs:
check-and-trigger:
name: 🔎 Backfill highest missing release tag
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
permissions:
contents: read
packages: read
+1 -1
View File
@@ -34,7 +34,7 @@ jobs:
# - https://gh.io/supported-runners-and-hardware-resources
# - https://gh.io/using-larger-runners (GitHub.com only)
# Consider using larger runners or machines with greater resources for possible analysis time improvements.
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
runs-on: ubuntu-24.04
permissions:
security-events: write
packages: read
+1 -1
View File
@@ -16,7 +16,7 @@ permissions:
jobs:
fuzz-yaml-bootstrap:
name: 🧬 Fuzz the bootstrap YAML reader
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
timeout-minutes: 30
steps:
- name: ⬇️ Checkout
@@ -26,7 +26,7 @@ permissions: read-all
jobs:
analysis:
name: 🔍 Scorecard analysis
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
# `publish_results: true` only works when run from the default branch. conditional can be removed if disabled.
if: github.event.repository.default_branch == github.ref_name || github.event_name == 'pull_request'
permissions:
+5 -5
View File
@@ -21,7 +21,7 @@ jobs:
update-docker-image-versions:
name: 🐳 Update Docker image versions
if: vars.CI_ENABLE_AUTO_UPDATES == 'true'
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
steps:
- name: ⬇️ Checkout
uses: actions/checkout@v7.0.0
@@ -68,7 +68,7 @@ jobs:
update-version-sources:
name: 🔖 Update declared version sources
if: vars.CI_ENABLE_AUTO_UPDATES == 'true'
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
steps:
- name: ⬇️ Checkout
uses: actions/checkout@v7.0.0
@@ -115,7 +115,7 @@ jobs:
update-pip-pins:
name: 🐍 Update pinned pip requirements
if: vars.CI_ENABLE_AUTO_UPDATES == 'true'
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
steps:
- name: ⬇️ Checkout
uses: actions/checkout@v7.0.0
@@ -162,7 +162,7 @@ jobs:
update-galaxy-pins:
name: 🌌 Update pinned Ansible collections
if: vars.CI_ENABLE_AUTO_UPDATES == 'true'
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
steps:
- name: ⬇️ Checkout
uses: actions/checkout@v7.0.0
@@ -209,7 +209,7 @@ jobs:
update-repository-refs:
name: 🌿 Update repository refs
if: vars.CI_ENABLE_AUTO_UPDATES == 'true'
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
steps:
- name: ⬇️ Checkout
uses: actions/checkout@v7.0.0
@@ -27,7 +27,7 @@ jobs:
cancel-branch-runs:
name: 🚫 Cancel superseded branch runs
if: ${{ github.event_name == 'push' && (github.ref_name != 'main' || (github.ref_name == 'main' && startsWith(github.event.head_commit.message, 'Release version '))) }}
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
permissions:
actions: write
contents: read
@@ -52,7 +52,7 @@ jobs:
cancel-pull-request-runs:
name: 🚫 Cancel superseded pull request runs
if: ${{ github.event_name == 'pull_request_target' }}
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
permissions:
actions: write
contents: read
+1 -1
View File
@@ -20,7 +20,7 @@ permissions:
jobs:
cancel-branch-runs:
if: ${{ github.event.ref_type == 'branch' }}
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
permissions:
actions: write
contents: read
@@ -40,7 +40,7 @@ permissions:
jobs:
cleanup:
name: 🗑️ Delete private GHCR mirror packages
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
permissions:
contents: read
packages: write
@@ -29,7 +29,7 @@ jobs:
detect-pr-scope:
name: 🔎 Detect PR scope
if: ${{ github.event.pull_request.draft == false && (github.event_name == 'pull_request' || github.event.pull_request.head.repo.full_name != github.repository) }}
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
outputs:
scope: ${{ steps.resolve.outputs.scope }}
run_ci_orchestrator: ${{ steps.resolve.outputs.run_ci_orchestrator }}
@@ -59,7 +59,7 @@ jobs:
- detect-pr-scope
- validation
if: ${{ needs.detect-pr-scope.outputs.run_ci_orchestrator == 'false' }}
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
steps:
- name: ✅ Confirm lightweight PR scope
shell: bash
@@ -73,7 +73,7 @@ jobs:
- detect-pr-scope
- validation
if: ${{ needs.detect-pr-scope.outputs.run_ci_orchestrator == 'true' }}
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
outputs:
whitelist: ${{ steps.resolve.outputs.whitelist }}
steps:
@@ -124,7 +124,7 @@ jobs:
- detect-pr-scope
- validation
if: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.full_name != github.repository && needs.detect-pr-scope.outputs.run_ci_orchestrator == 'true' }}
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
outputs:
checkout_ref: ${{ steps.out.outputs.checkout_ref }}
image_tag: ${{ steps.out.outputs.image_tag }}
@@ -15,7 +15,7 @@ permissions:
jobs:
cancel-pr-runs:
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
permissions:
actions: write
contents: read
@@ -19,7 +19,7 @@ jobs:
close-when-disabled:
name: 🚫 Close Dependabot PR while gated
if: github.event.pull_request.user.login == 'dependabot[bot]' && vars.CI_ENABLE_AUTO_UPDATES != 'true'
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
steps:
- name: 📪 Close PR
env:
+5 -5
View File
@@ -21,7 +21,7 @@ permissions:
jobs:
run-policy:
name: 🧭 Decide CI policy
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
permissions:
contents: read
pull-requests: read
@@ -59,7 +59,7 @@ jobs:
name: ⏭️ Skip CI
needs: run-policy
if: needs.run-policy.outputs.should_run != 'true'
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
steps:
- name: ⏭️ Explain skip
shell: bash
@@ -72,7 +72,7 @@ jobs:
if: needs.run-policy.outputs.should_run == 'true'
needs: run-policy
name: 🏷️ Detect release tag
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
outputs:
is_version: ${{ steps.pick.outputs.is_version }}
version_tag: ${{ steps.pick.outputs.version_tag }}
@@ -91,7 +91,7 @@ jobs:
if: needs.run-policy.outputs.should_run == 'true'
name: ⭐ Resolve new-role priority
needs: run-policy
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
outputs:
priority: ${{ steps.resolve.outputs.priority }}
steps:
@@ -132,7 +132,7 @@ jobs:
- run-policy
- detect-release
- ci-orchestrator
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
outputs:
allowed: ${{ steps.guard.outputs.allowed }}
steps:
+3 -3
View File
@@ -49,11 +49,11 @@ Some CI failures (most notably OOM-kills such as [#162](https://github.com/infin
### GitHub-Hosted Runner Specs
All [workflows](../../../../.github/workflows/) target `runs-on: ubuntu-latest`, which maps to the GitHub-hosted standard Linux runner. The effective ceiling depends on the repository's visibility and plan:
All [workflows](../../../../.github/workflows/) target `runs-on: ubuntu-24.04`, the GitHub-hosted standard Linux runner pinned to one Ubuntu release. GitHub still refreshes that image weekly; moving to the next release is a deliberate change of the label, and `tests/lint/repository/test_workflow_runner_pinned.py` rejects `*-latest` labels. The effective ceiling depends on the repository's visibility and plan:
| Repository / plan | vCPU | RAM | SSD |
|------------------------------------------|------|--------|-------|
| Public repo (this repo, `ubuntu-latest`) | 4 | 16 GB | 14 GB |
| Public repo (this repo, `ubuntu-24.04`) | 4 | 16 GB | 14 GB |
| Private repo on Free / Pro / Team | 2 | 7 GB | 14 GB |
| Private repo on Enterprise | 4 | 16 GB | 14 GB |
@@ -70,7 +70,7 @@ For the authoritative specs, see the upstream docs for [standard hosted runners]
Pick the profile that matches the runner you want to mimic:
```sh
# Public repo runner (ubuntu-latest, 4 vCPU / 16 GB): the default CI target
# Public repo runner (ubuntu-24.04, 4 vCPU / 16 GB): the default CI target
INFINITO_MEM_LIMIT=16g INFINITO_MEMSWAP_LIMIT=16g INFINITO_CPUS=4 make <target>
# Private repo runner on Free/Pro/Team (2 vCPU / 7 GB)
@@ -0,0 +1,34 @@
from __future__ import annotations
import unittest
from utils.cache.yaml import load_yaml_any
from . import PROJECT_ROOT
_WORKFLOWS_DIR = PROJECT_ROOT / ".github" / "workflows"
class TestWorkflowRunnerPinned(unittest.TestCase):
def test_no_job_runs_on_a_latest_label(self) -> None:
offenders: list[str] = []
for path in sorted(_WORKFLOWS_DIR.glob("*.y*ml")):
document = load_yaml_any(str(path), default_if_missing={}) or {}
for job_id, job in (document.get("jobs") or {}).items():
if not isinstance(job, dict) or "runs-on" not in job:
continue
if "-latest" in str(job["runs-on"]):
offenders.append(f"{path.name}: {job_id}: {job['runs-on']}")
if offenders:
self.fail(
f"{len(offenders)} job(s) run on a moving '*-latest' runner label. "
"GitHub moves that label to the next OS release on its own "
"schedule (ubuntu-latest jumped to 26.04 mid-run); pin the "
"release, e.g. 'ubuntu-24.04', and move it in a dedicated "
"change.\n" + "\n".join(offenders)
)
if __name__ == "__main__":
unittest.main()