Files
shacraft-launcher/scripts/release_gate_test.py
T

128 lines
6.4 KiB
Python

"""Execute the actual trusted workflow gate against disposable local Git history."""
import os
import re
import subprocess
import tempfile
import unittest
from pathlib import Path
ROOT = Path(os.environ.get("RELEASE_TEST_ROOT", Path(__file__).resolve().parents[1]))
WORKFLOWS = [ROOT / ".github/workflows" / name for name in ("release.yml", "release-publish.yml")]
GATE = " - name: Resolve tag using trusted workflow Git commands\n"
def workflow_gate(workflow):
after = workflow.read_text().split(GATE, 1)[1]
lines = after.split(" run: |\n", 1)[1].splitlines()
body = []
for line in lines:
if not line.startswith(" "):
break
body.append(line[10:])
return "\n".join(body) + "\n"
class ReleaseGateTests(unittest.TestCase):
def setUp(self):
self.temporary = tempfile.TemporaryDirectory(prefix="shacraft-source-gate-")
self.directory = Path(self.temporary.name)
self.environment = dict(os.environ, GIT_CONFIG_NOSYSTEM="1", GIT_CONFIG_GLOBAL=os.devnull)
self.git("init", "--initial-branch=main")
self.git("config", "user.email", "test@example.invalid")
self.git("config", "user.name", "Release Gate Test")
(self.directory / "reviewed.txt").write_text("Reviewed source\n")
self.git("add", ".")
self.git("commit", "-m", "Reviewed main commit")
self.good = self.git("rev-parse", "HEAD")
self.git("update-ref", "refs/remotes/origin/main", self.good)
self.git("tag", "v0.2.0")
self.git("checkout", "-b", "unreviewed")
(self.directory / "scripts").mkdir()
# This validator would falsely accept its own tag if a workflow ran it.
(self.directory / "scripts/release.py").write_text(
"from pathlib import Path\nPath('untrusted-code-ran').write_text('bypassed')\n"
)
self.git("add", ".")
self.git("commit", "-m", "Unreviewed tag with false validator")
self.bad = self.git("rev-parse", "HEAD")
self.git("tag", "v9.9.9")
self.git("checkout", "main")
def tearDown(self):
self.temporary.cleanup()
def git(self, *args):
return subprocess.check_output(["git", *args], cwd=self.directory,
env=self.environment, text=True, stderr=subprocess.DEVNULL).strip()
def run_gate(self, workflow, tag):
output = self.directory / (workflow.stem + "-output")
output.unlink(missing_ok=True)
result = subprocess.run(["bash", "-c", workflow_gate(workflow)], cwd=self.directory,
env=dict(self.environment, RELEASE_TAG=tag, GITHUB_OUTPUT=str(output)),
capture_output=True, text=True, check=False)
return result, output.read_text() if output.exists() else ""
def test_main_tag_emits_immutable_commit(self):
for workflow in WORKFLOWS:
with self.subTest(workflow=workflow.name):
result, output = self.run_gate(workflow, "v0.2.0")
self.assertEqual(result.returncode, 0, result.stderr)
self.assertEqual(output, f"commit={self.good}\n")
def test_unreviewed_tag_cannot_replace_its_own_ancestry_validator(self):
for workflow in WORKFLOWS:
with self.subTest(workflow=workflow.name):
result, output = self.run_gate(workflow, "v9.9.9")
self.assertNotEqual(result.returncode, 0)
self.assertEqual(output, "")
self.assertFalse((self.directory / "untrusted-code-ran").exists())
def test_missing_or_retargeted_tag_cannot_change_validated_source(self):
for workflow in WORKFLOWS:
with self.subTest(workflow=workflow.name):
result, output = self.run_gate(workflow, "v8.8.8")
self.assertNotEqual(result.returncode, 0)
self.assertEqual(output, "")
self.git("tag", "-f", "v0.2.0", self.good)
result, output = self.run_gate(workflow, "v0.2.0")
self.assertEqual(result.returncode, 0, result.stderr)
self.git("tag", "-f", "v0.2.0", self.bad)
self.assertEqual(output, f"commit={self.good}\n")
result, fresh_output = self.run_gate(workflow, "v0.2.0")
self.assertNotEqual(result.returncode, 0)
self.assertEqual(fresh_output, "")
def test_workflows_execute_candidate_code_only_after_gate_and_checkout_sha(self):
for workflow in WORKFLOWS:
with self.subTest(workflow=workflow.name):
text = workflow.read_text()
preflight = text.split(" preflight:\n", 1)[1].split("\n build:\n", 1)[0].split("\n publish:\n", 1)[0]
before, after = preflight.split(GATE, 1)
self.assertEqual(before.count("uses: actions/checkout@v4"), 1)
self.assertIn("fetch-depth: 0", before)
self.assertNotIn("ref:", before) # Initial checkout is trusted workflow main.
self.assertIn("release.version_tag", before)
self.assertLess(after.index("git merge-base --is-ancestor"), after.index("uses: actions/checkout@v4"))
self.assertIn("ref: ${{ steps.source.outputs.commit }}", after)
self.assertNotIn("ref: ${{ inputs.tag }}", text)
downstream = text[len(text.split(" preflight:\n", 1)[0]) + len(" preflight:\n") + len(preflight):]
self.assertGreater(downstream.count("ref: ${{ needs.preflight.outputs.commit }}"), 0)
self.assertEqual(downstream.count("uses: actions/checkout@v4"), downstream.count("ref: ${{ needs.preflight.outputs.commit }}"))
self.assertEqual(text.count("uses: actions/checkout@v4"), text.count("persist-credentials: false"))
def test_write_token_is_only_exposed_to_explicit_final_publish_step(self):
text = WORKFLOWS[1].read_text().split("\n publish:\n", 1)[1]
job_environment = re.search(r"(?m)^ env:\n((?: .*\n)+)", text)
self.assertIsNotNone(job_environment)
self.assertNotIn("GH_TOKEN", job_environment.group(1))
before, final = text.split(" - name: Re-download, verify signatures/metadata/assets and explicitly publish\n", 1)
self.assertNotIn("GH_TOKEN", before)
self.assertIn("GH_TOKEN: ${{ github.token }}", final)
if __name__ == "__main__":
unittest.main()