From 17fe4894ba707582baa36e65bff58a459ebb467c Mon Sep 17 00:00:00 2001 From: Brett Heap Date: Wed, 21 Oct 2026 11:37:46 +0000 Subject: [PATCH] CI and the release workflow run on ubuntu-24.04, because ubuntu-latest is now Ubuntu 26.04, where setup-python has no Python 3.9 and the TERM-ignoring kill test fails Since about 04:33Z on 2026-10-21, GitHub's ubuntu-latest resolves to Ubuntu 26.04 (job log: "Image: ubuntu-26.04", image release ubuntu26/20261012.163). Main's nightly run 39544671103, at main a42946e, failed on it, and the same jobs were green on Ubuntu 24.04 before: * package: actions/setup-python said "The version '3.9' with architecture 'x64' was not found for Ubuntu 26.04." * tests and tests-no-submodule: one failure each, tests/test_claude_current.py::test_a_command_that_ignores_term_is_killed_after_the_grace[host], "AssertionError: KILL never came: 30.0s". Everything else passed. release.yml's install check has an ubuntu-latest leg on Python 3.9, so the release run of the v1.1.0 tag would have failed the same way. Pinning every ubuntu runner keeps CI and the release on the environment v1.1.0 was tested on. Whether to support 26.04 (the Python 3.9 leg, and the kill-after behaviour there) is a separate v1.2.0 question: claude-current and its test are not touched. What changed: * .github/workflows/tests.yml: guard-launch-mode, tests, tests-no-submodule and package run on ubuntu-24.04, with one comment, at the first, that says why. * .github/workflows/release.yml: build, testpypi and pypi, and the install matrix's two ubuntu legs, run on ubuntu-24.04, with one comment, at build. macos-latest stays. * tests/test_tests_workflow.py: the existing-jobs test and the package-job test expect ubuntu-24.04. * tests/test_release_workflow.py: INSTALL_LEGS expects ubuntu-24.04 on both ubuntu legs. * specs/006-release-pypi/contracts/workflows.md and plan.md: the install matrix, the package job and the CI target name ubuntu-24.04 and say why. Co-Authored-By: Claude Sonnet 5.5 --- .github/workflows/release.yml | 13 ++++++++----- .github/workflows/tests.yml | 11 +++++++---- specs/006-release-pypi/contracts/workflows.md | 12 ++++++++---- specs/006-release-pypi/plan.md | 5 +++-- tests/test_release_workflow.py | 7 +++++-- tests/test_tests_workflow.py | 4 ++-- 6 files changed, 33 insertions(+), 19 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9a27e2ed..092ddc05 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -266,7 +266,10 @@ env: jobs: build: name: build and verify - runs-on: ubuntu-latest + # EVERY UBUNTU RUNNER IS ON 24.04: `ubuntu-latest` became Ubuntu 26.04 on + # 2026-10-21. There setup-python has no Python 3.9 and the `[host]` case of + # the TERM-ignoring kill test fails. Pinned until 26.04 support is decided. + runs-on: ubuntu-24.04 timeout-minutes: 30 permissions: contents: read # the checkout and its submodule, the tag, and the forge's compare and tree listing @@ -1032,7 +1035,7 @@ jobs: # (their deployment limit is the tags v*). With no status function in # it, `if:` also requires the build to have succeeded. if: startsWith(github.ref, 'refs/tags/v') && needs.build.outputs.to-testpypi == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 timeout-minutes: 30 environment: name: testpypi @@ -1254,11 +1257,11 @@ jobs: fail-fast: false matrix: include: - - os: ubuntu-latest + - os: ubuntu-24.04 python: '3.x' - os: macos-latest python: '3.x' - - os: ubuntu-latest + - os: ubuntu-24.04 python: '3.9' env: WHEEL: ${{ needs.build.outputs.wheel }} @@ -1557,7 +1560,7 @@ jobs: # opensoft/openRepoTools-code alone (FR-048), so in an rc rehearsal # this job is skipped and never asks for the `pypi` approval. if: startsWith(github.ref, 'refs/tags/v') && needs.build.outputs.to-pypi == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 timeout-minutes: 30 environment: name: pypi diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 2cfa173f..46feb68a 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -52,7 +52,10 @@ jobs: # lane-helper suite runs. Each GitHub job has its own workstation runner; # the canonical wrapper still serializes any pytest processes on that runner. guard-launch-mode: - runs-on: ubuntu-latest + # EVERY UBUNTU RUNNER IS ON 24.04: `ubuntu-latest` became Ubuntu 26.04 on + # 2026-10-21. There setup-python has no Python 3.9 and the `[host]` case of + # the TERM-ignoring kill test fails. Pinned until 26.04 support is decided. + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 with: @@ -72,7 +75,7 @@ jobs: run: bash tests/run.sh -k 'guard_launch_mode or repo_hygiene' tests: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: # PINNED BY COMMIT, not by tag — openRepoShape's own rule, and the same # two commits every job here pins. A tag can be moved and a commit @@ -128,7 +131,7 @@ jobs: # the assertion; `-rs` prints the skip reasons, which is what a person # reading this job wants to see. tests-no-submodule: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 with: @@ -173,7 +176,7 @@ jobs: # hatchling and pipx reaches it for its shared pip; the `--install` run # reaches nothing. package: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: # The same two commits the other jobs pin. - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 diff --git a/specs/006-release-pypi/contracts/workflows.md b/specs/006-release-pypi/contracts/workflows.md index 706fe50b..8b46eece 100644 --- a/specs/006-release-pypi/contracts/workflows.md +++ b/specs/006-release-pypi/contracts/workflows.md @@ -265,8 +265,10 @@ this run's approvals, and `contents` for the tag, read again. - It needs `build` and `testpypi`. It runs on a tag after `testpypi` succeeded, and on a branch when `testpypi` was skipped. This is the model's condition. -- The matrix is `ubuntu-latest` on `3.x`, `macos-latest` on `3.x`, and - `ubuntu-latest` on `3.9`. There is no Windows leg. +- The matrix is `ubuntu-24.04` on `3.x`, `macos-latest` on `3.x`, and + `ubuntu-24.04` on `3.9`. There is no Windows leg. The ubuntu legs are + pinned because `ubuntu-latest` became Ubuntu 26.04 on 2026-10-21, where + setup-python has no Python `3.9`. - `PIP_INDEX_URL` is PyPI's, as the model has it. The steps: @@ -359,8 +361,10 @@ These are the model's grants (`release.yml:1133-1136` at v1.1.0). version and `run:` commands. These are asserted from the file's text, not from history, because CI checks out one commit at `fetch-depth: 1`. The pull request's diff review confirms the rest of "unchanged". -- The `package` job runs on `ubuntu-latest` with Python `3.9` and - `submodules: true`, and carries steps 4 to 11 in order. +- The `package` job runs on `ubuntu-24.04` with Python `3.9` and + `submodules: true`, and carries steps 4 to 11 in order. The runner is + pinned because `ubuntu-latest` became Ubuntu 26.04 on 2026-10-21, where + setup-python has no Python `3.9`. - Its `tomli` comes from the build lock's own entry, and the job holds no `--hash` of its own. - Its wheel-check heredoc (step 6) is run on the same four synthetic wheels diff --git a/specs/006-release-pypi/plan.md b/specs/006-release-pypi/plan.md index 18d6972d..73a21e6d 100644 --- a/specs/006-release-pypi/plan.md +++ b/specs/006-release-pypi/plan.md @@ -99,8 +99,9 @@ this plan. - **Runs on**: Linux and macOS, and WSL2 on Windows. Python 3.9 or later; bash 3.2 or later. -- **CI**: `ubuntu-latest`. The release install check adds `macos-latest`, - run under `/bin/bash` 3.2. +- **CI**: `ubuntu-24.04`, pinned because `ubuntu-latest` became Ubuntu 26.04 + on 2026-10-21, where setup-python has no Python 3.9. The release install + check adds `macos-latest`, run under `/bin/bash` 3.2. **Project Type**: a command-line toolset, the bash installer and the commands it places, published as one Python wheel, with CI and release diff --git a/tests/test_release_workflow.py b/tests/test_release_workflow.py index 8273f400..34ae79af 100644 --- a/tests/test_release_workflow.py +++ b/tests/test_release_workflow.py @@ -192,8 +192,11 @@ "(needs.testpypi.result == 'success' || " "(needs.testpypi.result == 'skipped' && " "!startsWith(github.ref, 'refs/tags/'))) }}") -INSTALL_LEGS = [("ubuntu-latest", "3.x"), ("macos-latest", "3.x"), - ("ubuntu-latest", "3.9")] + +#: The two ubuntu legs are pinned to 24.04, and macOS is not: `ubuntu-latest` +#: became Ubuntu 26.04 on 2026-10-21, where setup-python has no Python 3.9. +INSTALL_LEGS = [("ubuntu-24.04", "3.x"), ("macos-latest", "3.x"), + ("ubuntu-24.04", "3.9")] #: THE BUILD'S STEPS ADAPTED FROM THE MODEL, BY THE MODEL'S OWN NAMES, each #: a prefix the step's `name:` begins with (contracts/workflows.md's build diff --git a/tests/test_tests_workflow.py b/tests/test_tests_workflow.py index 33a1f6b0..030880c7 100644 --- a/tests/test_tests_workflow.py +++ b/tests/test_tests_workflow.py @@ -855,7 +855,7 @@ def test_tests_yml_existing_job_keeps_todays_runner_checkout_python_and_runs( assert isinstance(job, dict), f"tests.yml has no `{job_id}` job" want = EXISTING_JOBS[job_id] steps = job.get("steps") or [] - assert job.get("runs-on") == "ubuntu-latest", job.get("runs-on") + assert job.get("runs-on") == "ubuntu-24.04", job.get("runs-on") assert len(steps) >= 2, f"`{job_id}` has {len(steps)} steps" assert steps[0].get("uses") == CHECKOUT, steps[0] assert steps[0].get("with") == want["checkout"], steps[0].get("with") @@ -910,7 +910,7 @@ def test_tests_yml_package_runs_on_ubuntu_python_3_9_with_the_submodule( Python the package supports, on ubuntu.""" job = package_job(workflow) steps = job["steps"] - assert job.get("runs-on") == "ubuntu-latest", job.get("runs-on") + assert job.get("runs-on") == "ubuntu-24.04", job.get("runs-on") assert len(steps) >= 2, f"the package job has {len(steps)} steps" assert steps[0].get("uses") == CHECKOUT, steps[0] assert steps[0].get("with") == {"submodules": True, "fetch-depth": 1}, \