From bc1e40493f976bd496a9e55091c5fa7d472268d1 Mon Sep 17 00:00:00 2001 From: Talha Date: Mon, 3 Aug 2026 16:57:51 +0500 Subject: [PATCH] ci(release): accept generated release metadata --- .github/dependabot.yml | 9 +++++ tests/test_published_distribution.py | 56 ++++++++++++++++++++++++++ tests/test_release_contract.py | 23 ++++++++--- utils/verify_published_distribution.py | 16 ++++++-- 4 files changed, 94 insertions(+), 10 deletions(-) create mode 100644 tests/test_published_distribution.py diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 7b0420f..a414a0b 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -25,4 +25,13 @@ updates: github-actions: patterns: - "*" + ignore: + # The action ref is the Rust version to install, not an action release. + # Dependabot otherwise proposes unreleased toolchains such as 1.100.0. + - dependency-name: dtolnay/rust-toolchain + # The v4 ref already follows current v4 security and patch releases. + - dependency-name: github/codeql-action + update-types: + - version-update:semver-minor + - version-update:semver-patch open-pull-requests-limit: 2 diff --git a/tests/test_published_distribution.py b/tests/test_published_distribution.py new file mode 100644 index 0000000..bc29738 --- /dev/null +++ b/tests/test_published_distribution.py @@ -0,0 +1,56 @@ +import unittest + +from utils.verify_published_distribution import distribution_files + + +class PublishedDistributionTests(unittest.TestCase): + def test_registry_attestations_are_not_distribution_archives(self): + payload = { + "urls": [ + { + "filename": "vidxp-0.4.0.dev19-py3-none-any.whl", + "digests": {"sha256": "wheel"}, + }, + { + "filename": ( + "vidxp-0.4.0.dev19-py3-none-any.whl.publish.attestation" + ), + "digests": {"sha256": "wheel-attestation"}, + }, + { + "filename": "vidxp-0.4.0.dev19.tar.gz", + "digests": {"sha256": "sdist"}, + }, + { + "filename": "vidxp-0.4.0.dev19.tar.gz.publish.attestation", + "digests": {"sha256": "sdist-attestation"}, + }, + ] + } + + self.assertEqual( + distribution_files(payload), + { + "vidxp-0.4.0.dev19-py3-none-any.whl": "wheel", + "vidxp-0.4.0.dev19.tar.gz": "sdist", + }, + ) + + def test_unknown_registry_files_are_still_compared(self): + payload = { + "urls": [ + { + "filename": "unexpected.zip", + "digests": {"sha256": "unexpected"}, + } + ] + } + + self.assertEqual( + distribution_files(payload), + {"unexpected.zip": "unexpected"}, + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_release_contract.py b/tests/test_release_contract.py index 49c384e..72d0305 100644 --- a/tests/test_release_contract.py +++ b/tests/test_release_contract.py @@ -3,11 +3,18 @@ import unittest from pathlib import Path -from utils.release_contract import validate +from utils.release_contract import validate, version_sources from utils.prepare_nightly import prepare class ReleaseContractTests(unittest.TestCase): + def current_release(self) -> tuple[str, str]: + versions = set(version_sources().values()) + self.assertEqual(len(versions), 1) + version = versions.pop() + channel = "beta" if "-b" in version else "stable" + return version, channel + def copy_contract(self, destination: Path) -> None: root = Path(__file__).resolve().parents[1] for relative in ( @@ -22,8 +29,9 @@ def copy_contract(self, destination: Path) -> None: target.parent.mkdir(parents=True, exist_ok=True) target.write_bytes((root / relative).read_bytes()) - def test_beta_contract_matches_every_release_source(self): - self.assertEqual(validate("beta", "v0.4.0-b"), "0.4.0-b") + def test_current_contract_matches_every_release_source(self): + version, channel = self.current_release() + self.assertEqual(validate(channel, f"v{version}"), version) def test_rejects_a_divergent_desktop_version(self): with tempfile.TemporaryDirectory() as temporary: @@ -36,12 +44,15 @@ def test_rejects_a_divergent_desktop_version(self): validate("beta", None, root) def test_beta_and_stable_channels_are_not_interchangeable(self): - with self.assertRaisesRegex(ValueError, "stable release version"): - validate("stable", None) + _, channel = self.current_release() + other_channel = "stable" if channel == "beta" else "beta" + with self.assertRaisesRegex(ValueError, f"{other_channel} release version"): + validate(other_channel, None) def test_tag_must_match_the_combined_version(self): + _, channel = self.current_release() with self.assertRaisesRegex(ValueError, "does not match"): - validate("beta", "v0.4.0-b.9") + validate(channel, "v999.999.999") def test_nightly_version_is_unique_without_changing_release_sources(self): with tempfile.TemporaryDirectory() as temporary: diff --git a/utils/verify_published_distribution.py b/utils/verify_published_distribution.py index 3cac159..125e2e8 100644 --- a/utils/verify_published_distribution.py +++ b/utils/verify_published_distribution.py @@ -13,6 +13,9 @@ from urllib.request import urlopen +_PUBLISH_ATTESTATION_SUFFIX = ".publish.attestation" + + def sha256(path: Path) -> str: digest = hashlib.sha256() with path.open("rb") as file: @@ -21,6 +24,14 @@ def sha256(path: Path) -> str: return digest.hexdigest() +def distribution_files(payload: dict[str, object]) -> dict[str, str]: + return { + file["filename"]: file["digests"]["sha256"] + for file in payload.get("urls", []) + if not file["filename"].endswith(_PUBLISH_ATTESTATION_SUFFIX) + } + + def main() -> int: parser = argparse.ArgumentParser() parser.add_argument("--repository", required=True) @@ -50,10 +61,7 @@ def main() -> int: return 0 raise - remote = { - file["filename"]: file["digests"]["sha256"] - for file in payload.get("urls", []) - } + remote = distribution_files(payload) if local == remote: print("identical") return 0