diff --git a/.gitea/workflows/.trigger b/.gitea/workflows/.trigger deleted file mode 100644 index 997b246..0000000 --- a/.gitea/workflows/.trigger +++ /dev/null @@ -1 +0,0 @@ -ci: re-run after routing fix diff --git a/.gitea/workflows/check.yml b/.gitea/workflows/check.yml index 186278f..55545f4 100644 --- a/.gitea/workflows/check.yml +++ b/.gitea/workflows/check.yml @@ -16,6 +16,18 @@ on: jobs: gate: runs-on: veron-1 + # Run IN a Python 3.12 image rather than trusting the runner image's + # toolchain. The first real CI run wedged here: catthehacker/ubuntu:act-22.04 + # ships Python 3.10.12, this project declares requires-python >=3.12, and pip + # answered that by backtracking through the entire release history of every + # dependency looking for something 3.10-compatible. It churned for 14 minutes + # at 100% CPU with `-q` hiding all of it, and would have churned until the + # runner timeout. A version mismatch presenting as a hang, not an error. + container: + image: python:3.12-bookworm + # And a hard ceiling, so a wedged step is a red check in minutes rather than + # an occupied runner for half an hour. + timeout-minutes: 12 services: postgres: image: postgres:16-alpine @@ -32,8 +44,10 @@ jobs: - name: install run: | + python3 --version python3 -m venv .venv - .venv/bin/pip install -q -e ".[dev]" + .venv/bin/pip install -q --upgrade pip + .venv/bin/pip install -e ".[dev]" - name: lint run: .venv/bin/ruff check api scripts diff --git a/api/tests/test_invariants.py b/api/tests/test_invariants.py index 2aee0e9..395e18f 100644 --- a/api/tests/test_invariants.py +++ b/api/tests/test_invariants.py @@ -461,3 +461,33 @@ def test_g75_workflows_use_a_label_this_runner_actually_provides(): continue label = ln.split("runs-on:")[1].strip() assert label in provided, f"{wf.name}: '{label}' is not a provided label" + + +def test_g73_workflow_pins_a_python_that_satisfies_requires_python(): + """The first real CI run wedged for 14 minutes because the runner image + ships Python 3.10 and this project requires 3.12: pip answered by + backtracking through every historical version of every dependency, at full + CPU, silently. A version mismatch presenting as a hang rather than an + error.""" + import re as _re + + pyproject = (ROOT / "pyproject.toml").read_text() + m = _re.search(r'requires-python\s*=\s*">=(\d+)\.(\d+)"', pyproject) + assert m, "pyproject declares no requires-python" + major, minor = int(m.group(1)), int(m.group(2)) + + for wf in ROOT.rglob(".gitea/workflows/*.y*ml"): + text = wf.read_text() + img = _re.search(r"image:\s*python:(\d+)\.(\d+)", text) + assert img, f"{wf.name}: job does not pin a python image" + assert (int(img.group(1)), int(img.group(2))) >= (major, minor), ( + f"{wf.name}: pins python {img.group(0)} but the project requires " + f">={major}.{minor}" + ) + + +def test_g73_every_job_has_a_timeout(): + """A wedged step should be a red check in minutes, not an occupied runner + for half an hour.""" + for wf in ROOT.rglob(".gitea/workflows/*.y*ml"): + assert "timeout-minutes:" in wf.read_text(), f"{wf.name}: no job timeout"