"""Pins the CI workflow's invariants (spec: docs/superpowers/specs/ 2026-10-04-modernization-design.md). CI drift shows up as a red test here.""" import pathlib import re import pytest import yaml ROOT = pathlib.Path(__file__).resolve().parent.parent WORKFLOW = ROOT / ".gitea" / "workflows" / "ci.yaml" VERIFY_SCRIPT = ROOT / "scripts" / "verify_index_release.sh" # Newest CPython the project claims. To add a version, bump this, the # matrix, the classifiers and the verify script's list together. NEWEST_MINOR = 14 WIP_CLAUSES = ( "!startsWith(github.event.pull_request.title, 'WIP:')", "!startsWith(github.event.pull_request.title, '[WIP]')", ) def load(): return yaml.safe_load(WORKFLOW.read_text()) def triggers(wf): # PyYAML follows YAML 1.1, where the bare key `on` parses as boolean True. return wf["on"] if "on" in wf else wf[True] def expected_versions(): text = (ROOT / "pyproject.toml").read_text() floor = int(re.search(r'requires-python\s*=\s*">=3\.(\d+)"', text).group(1)) return ["3.%d" % minor for minor in range(floor, NEWEST_MINOR + 1)] def flat(text): return " ".join(text.split()) def test_triggers(): on = triggers(load()) assert set(on["pull_request"]["types"]) == { "opened", "synchronize", "reopened", "edited", } assert on["push"]["branches"] == ["develop", "main"] assert "workflow_dispatch" in on def test_superseded_pr_runs_cancel_but_push_runs_never_do(): assert ( load()["concurrency"]["cancel-in-progress"] == "${{ github.event_name == 'pull_request' }}" ) def test_every_job_is_wip_gated(): for name, job in load()["jobs"].items(): cond = flat(job.get("if", "")) for clause in WIP_CLAUSES: assert clause in cond, "%s lacks WIP gate clause %s" % (name, clause) def test_matrix_matches_requires_python(): test = load()["jobs"]["test"] assert test["strategy"]["matrix"]["python"] == expected_versions() assert test["strategy"]["fail-fast"] is False def test_classifiers_match_matrix(): text = (ROOT / "pyproject.toml").read_text() claimed = re.findall(r'"Programming Language :: Python :: (3\.\d+)"', text) assert claimed == expected_versions() @pytest.mark.skipif(not VERIFY_SCRIPT.exists(), reason="added with the release task") def test_verify_script_covers_matrix(): match = re.search(r"^pythons=\(([^)]*)\)", VERIFY_SCRIPT.read_text(), re.M) assert match.group(1).split() == expected_versions() def test_checkout_fetches_full_history_for_setuptools_scm(): for name, job in load()["jobs"].items(): for step in job.get("steps", []): if step.get("uses", "").startswith("actions/checkout"): assert step.get("with", {}).get("fetch-depth") == 0, name def test_lint_job_runs_ruff(): runs = flat(" ".join(s.get("run", "") for s in load()["jobs"]["lint"]["steps"])) assert "uv run ruff check" in runs assert "uv run ruff format --check" in runs def test_test_lanes_run_locked_suite_and_smoke_test_both_artifacts(): runs = flat(" ".join(s.get("run", "") for s in load()["jobs"]["test"]["steps"])) assert "uv sync --locked" in runs assert "uv run pytest" in runs assert "uv build" in runs assert "dist/*.whl dist/*.tar.gz" in runs assert "scripts/smoke_import.py" in runs def test_ci_ok_fails_unless_every_gate_succeeded(): jobs = load()["jobs"] ci_ok = jobs["ci-ok"] # Gitea reports a skipped job as success, so ci-ok must run always() # and check each result explicitly. assert set(ci_ok["needs"]) == set(jobs) - {"ci-ok"} assert flat(ci_ok["if"]).startswith("always() &&") runs = " ".join(s.get("run", "") for s in ci_ok["steps"]) for needed in ci_ok["needs"]: assert 'test "${{ needs.%s.result }}" = success' % needed in runs