|
| 1 | +"""Exercise release guards with actual Git histories, without registry access.""" |
| 2 | + |
| 3 | +import os |
| 4 | +from pathlib import Path |
| 5 | +import subprocess |
| 6 | +import sys |
| 7 | +import tempfile |
| 8 | +import unittest |
| 9 | + |
| 10 | +SCRIPTS = Path(__file__).resolve().parent |
| 11 | + |
| 12 | + |
| 13 | +class ReleaseValidationTests(unittest.TestCase): |
| 14 | + def setUp(self): |
| 15 | + self.temp = tempfile.TemporaryDirectory(prefix="sdk-release-") |
| 16 | + self.addCleanup(self.temp.cleanup) |
| 17 | + self.cwd = Path(self.temp.name) |
| 18 | + self.env = { |
| 19 | + **os.environ, |
| 20 | + "GIT_AUTHOR_NAME": "Release test", |
| 21 | + "GIT_AUTHOR_EMAIL": "test@example.com", |
| 22 | + "GIT_COMMITTER_NAME": "Release test", |
| 23 | + "GIT_COMMITTER_EMAIL": "test@example.com", |
| 24 | + } |
| 25 | + self.git("init", "--initial-branch=main") |
| 26 | + self.git("commit", "--allow-empty", "-m", "Approved release") |
| 27 | + self.approved = self.git("rev-parse", "HEAD") |
| 28 | + self.git("update-ref", "refs/remotes/origin/main", self.approved) |
| 29 | + (self.cwd / "pyproject.toml").write_text('[tool.poetry]\nversion = "3.0.0"\n') |
| 30 | + self.env.update(GITHUB_REF="refs/tags/v3.0.0", GITHUB_REF_NAME="v3.0.0", GITHUB_SHA=self.approved) |
| 31 | + |
| 32 | + def git(self, *args): |
| 33 | + return subprocess.run(["git", *args], cwd=self.cwd, env=self.env, check=True, capture_output=True, text=True).stdout.strip() |
| 34 | + |
| 35 | + def guard(self): |
| 36 | + return subprocess.run(["bash", str(SCRIPTS / "verify-release-commit.sh")], cwd=self.cwd, env=self.env, capture_output=True, text=True) |
| 37 | + |
| 38 | + def test_approved_commit_and_ancestor_are_allowed(self): |
| 39 | + self.assertEqual(self.guard().returncode, 0) |
| 40 | + self.git("commit", "--allow-empty", "-m", "Newer approved commit") |
| 41 | + self.git("update-ref", "refs/remotes/origin/main", self.git("rev-parse", "HEAD")) |
| 42 | + self.git("checkout", "--detach", self.approved) |
| 43 | + self.assertEqual(self.guard().returncode, 0) |
| 44 | + |
| 45 | + def test_annotated_tag_is_resolved_to_commit(self): |
| 46 | + self.git("tag", "-a", "v3.0.0", "-m", "Release") |
| 47 | + self.env["GITHUB_SHA"] = self.git("rev-parse", "v3.0.0") |
| 48 | + self.assertEqual(self.guard().returncode, 0) |
| 49 | + |
| 50 | + def test_unmerged_commit_is_rejected(self): |
| 51 | + self.git("checkout", "-b", "unapproved") |
| 52 | + self.git("commit", "--allow-empty", "-m", "Unreviewed change") |
| 53 | + self.env["GITHUB_SHA"] = self.git("rev-parse", "HEAD") |
| 54 | + result = self.guard() |
| 55 | + self.assertNotEqual(result.returncode, 0) |
| 56 | + self.assertIn("must already be merged", result.stdout) |
| 57 | + |
| 58 | + def test_wrong_checkout_is_rejected(self): |
| 59 | + self.git("commit", "--allow-empty", "-m", "Different checkout") |
| 60 | + result = self.guard() |
| 61 | + self.assertNotEqual(result.returncode, 0) |
| 62 | + self.assertIn("does not match", result.stdout) |
| 63 | + |
| 64 | + def test_missing_main_or_event_commit_is_rejected(self): |
| 65 | + self.git("update-ref", "-d", "refs/remotes/origin/main") |
| 66 | + self.assertNotEqual(self.guard().returncode, 0) |
| 67 | + del self.env["GITHUB_SHA"] |
| 68 | + self.assertNotEqual(self.guard().returncode, 0) |
| 69 | + |
| 70 | + def test_package_version_and_ref(self): |
| 71 | + for tag in ("3.0.0", "v3.0.0"): |
| 72 | + self.env.update(GITHUB_REF=f"refs/tags/{tag}", GITHUB_REF_NAME=tag) |
| 73 | + result = subprocess.run([sys.executable, str(SCRIPTS / "validate-release.py")], cwd=self.cwd, env=self.env, capture_output=True, text=True) |
| 74 | + self.assertEqual(result.returncode, 0, result.stderr) |
| 75 | + for ref, tag in (("refs/heads/main", "main"), ("refs/heads/refs/tags/v3.0.0", "refs/tags/v3.0.0"), ("refs/tags/v3.0.1", "v3.0.1"), ("refs/tags/vv3.0.0", "vv3.0.0"), ("refs/tags/v3.0.0", "3.0.0"), ("", "")): |
| 76 | + with self.subTest(ref=ref, tag=tag): |
| 77 | + self.env.update(GITHUB_REF=ref, GITHUB_REF_NAME=tag) |
| 78 | + if not ref.startswith("refs/tags/"): |
| 79 | + self.assertNotEqual(self.guard().returncode, 0) |
| 80 | + result = subprocess.run([sys.executable, str(SCRIPTS / "validate-release.py")], cwd=self.cwd, env=self.env, capture_output=True, text=True) |
| 81 | + self.assertNotEqual(result.returncode, 0) |
| 82 | + |
| 83 | + |
| 84 | +if __name__ == "__main__": |
| 85 | + unittest.main() |
0 commit comments