From 75dacfcba34b5996ea4203ee1816799141af1915 Mon Sep 17 00:00:00 2001 From: ethernet Date: Mon, 21 Sep 2026 20:47:14 -0400 Subject: [PATCH] feat(release): admit a stable release from its claim tag main carries 0.0.0, so comparing the tag against pyproject.toml refuses every release. Admission reads the version from the -rc claim and checks only that the commit is on main. --- scripts/releases/stable.py | 40 +++++++++++++++++++------- tests/scripts/test_stable_admission.py | 30 +++++++++++++++++++ 2 files changed, 60 insertions(+), 10 deletions(-) create mode 100644 tests/scripts/test_stable_admission.py diff --git a/scripts/releases/stable.py b/scripts/releases/stable.py index f371e7bc2b..0f5d65ee09 100644 --- a/scripts/releases/stable.py +++ b/scripts/releases/stable.py @@ -24,6 +24,23 @@ SMOKE_JOBS = { } +def admit_claim(tag: str, commit: str, *, on_main) -> dict: + """Admit a release from its claim tag. The checkout version is not read. + + ``main`` carries ``0.0.0`` on purpose, so the version comes from the + ``-rc`` tag and the only question about the commit is whether it is on + ``main``. + """ + if not isinstance(tag, str) or not tag.endswith("-rc"): + raise ValueError(f"{tag} is not a claim tag") + version = tag[1:-3] + if not re.fullmatch(r"(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)", version): + raise ValueError(f"{tag} is not a claim tag") + if not on_main(commit): + raise ValueError(f"{commit} is not on main") + return {"version": version, "commit": commit} + + def require_stable_identity(tag: str, commit: str, ref: str) -> None: if not isinstance(tag, str) or not STABLE_TAG_RE.fullmatch(tag) or not SHA.fullmatch(commit or "") or ref != f"refs/tags/{tag}": raise ValueError("Stable release must run on its exact stable tag and commit") @@ -195,16 +212,19 @@ def summary(text: str, env: dict) -> None: def admit(env: dict) -> None: - tag, commit = check_tag(env) - with Path("pyproject.toml").open("rb") as file: - version = tomllib.load(file)["project"]["version"] - if f"v{version}" != tag: - raise ValueError("Stable tag must match the project version") - release = json.loads(output(["gh", "release", "view", tag, "--repo", env["GITHUB_REPOSITORY"], "--json", "tagName,isDraft,isPrerelease"])) - if release["tagName"] != tag or not release["isDraft"] or release["isPrerelease"]: - raise ValueError("Stable candidate must have a non-prerelease draft") - emit({"tag": tag, "commit": commit}, env) - summary(f"## Stable candidate {tag}\nCommit: {commit}\n\nDesktop Playwright E2E: deferred by owner, not passed.\nOSV findings retain the existing advisory policy.", env) + """Admit the claim. The checkout carries 0.0.0, so the tag is the version.""" + tag, commit = env.get("RELEASE_TAG"), env.get("GITHUB_SHA") + admitted = admit_claim(tag, commit, on_main=lambda sha: _on_main(sha, env)) + emit({"tag": tag, "commit": admitted["commit"], "version": admitted["version"]}, env) + summary(f"## Stable candidate {tag}\nCommit: {commit}\nVersion: {admitted['version']}\n", env) + + +def _on_main(commit: str, env: dict) -> bool: + try: + output(["git", "merge-base", "--is-ancestor", commit, "origin/main"]) + except subprocess.CalledProcessError: + return False + return True def transitions(env: dict) -> None: diff --git a/tests/scripts/test_stable_admission.py b/tests/scripts/test_stable_admission.py new file mode 100644 index 0000000000..48453e42f8 --- /dev/null +++ b/tests/scripts/test_stable_admission.py @@ -0,0 +1,30 @@ +"""Stable admission reads the version from the claim, not from the checkout. + +``main`` carries ``0.0.0`` by design, so comparing the tag against +``pyproject.toml`` would refuse every release. The claim tag is the version, +and the only question about the commit is whether it is on ``main``. +""" +import pytest + + +def test_admission_reads_the_version_from_the_claim(): + from scripts.releases.stable import admit_claim + + commit = "a" * 40 + admitted = admit_claim("v0.21.5-rc", commit, on_main=lambda sha: sha == commit) + + assert admitted == {"version": "0.21.5", "commit": commit} + + +def test_admission_refuses_a_claim_for_a_commit_off_main(): + from scripts.releases.stable import admit_claim + + with pytest.raises(ValueError, match="not on main"): + admit_claim("v0.21.5-rc", "b" * 40, on_main=lambda _sha: False) + + +def test_admission_refuses_a_tag_that_is_not_a_claim(): + from scripts.releases.stable import admit_claim + + with pytest.raises(ValueError, match="claim"): + admit_claim("v0.21.5", "a" * 40, on_main=lambda _sha: True)