cat / goq
clone

turns a linux box into a private steam streaming appliance: plug in, tap your key, play from another computer

fix/docs-activation-truth
goq / scripts / tests / test_portal_release.py
test_portal_release.py 13.7K
1#!/usr/bin/env python323from __future__ import annotations45import hashlib6import importlib.util7import json8import pathlib9import tempfile10import unittest111213REPO_DIR = pathlib.Path(__file__).resolve().parents[2]14MODULE_PATH = REPO_DIR / "scripts" / "verify-portal-release.py"15SPEC = importlib.util.spec_from_file_location("portal_release_verifier", MODULE_PATH)16assert SPEC is not None and SPEC.loader is not None17VERIFIER = importlib.util.module_from_spec(SPEC)18SPEC.loader.exec_module(VERIFIER)192021class FakeGit:22 def __init__(self, *, dirty: bool = False, tag_commit: str = "a" * 40) -> None:23 self.dirty = dirty24 self.tag_commit = tag_commit25 self.commands: list[tuple[str, ...]] = []2627 def __call__(self, command: list[str], _cwd: pathlib.Path) -> str:28 self.commands.append(tuple(command))29 if command[1] == "status":30 return " M tracked" if self.dirty else ""31 if command[1] == "rev-parse":32 return "a" * 4033 if command[1] == "rev-list":34 return self.tag_commit35 raise AssertionError(command)363738class PortalReleaseVerifierTests(unittest.TestCase):39 def make_repo(self, root: pathlib.Path, version: str = "0.1.0") -> pathlib.Path:40 repo = root / "repo"41 (repo / ".git").mkdir(parents=True)42 (repo / "src-tauri").mkdir()43 (repo / "crates" / "sigil-host").mkdir(parents=True)44 (repo / "crates" / "sigil-protocol").mkdir(parents=True)45 (repo / "Cargo.toml").write_text(46 '[workspace]\nmembers = ["crates/sigil-host", "crates/sigil-protocol", "src-tauri"]\n',47 encoding="utf-8",48 )49 for path, name in [50 (repo / "crates" / "sigil-host" / "Cargo.toml", "sigil-host"),51 (repo / "crates" / "sigil-protocol" / "Cargo.toml", "sigil-protocol"),52 ]:53 path.write_text(f'[package]\nname = "{name}"\nversion = "{version}"\n', encoding="utf-8")54 (repo / "src-tauri" / "Cargo.toml").write_text(55 f'[package]\nname = "portal"\nversion = "{version}"\n'56 '[features]\ndemo-direct-node = []\n'57 'experimental-non-macos-pointer-capture = []\n',58 encoding="utf-8",59 )60 (repo / "src-tauri" / "tauri.conf.json").write_text(61 json.dumps(62 {63 "productName": "Portal",64 "identifier": "sh.goq.portal",65 "version": version,66 }67 ),68 encoding="utf-8",69 )70 return repo7172 def test_source_binds_clean_tag_head_and_all_versions(self) -> None:73 with tempfile.TemporaryDirectory() as temporary:74 repo = self.make_repo(pathlib.Path(temporary))75 fake_git = FakeGit()76 result = VERIFIER.validate_source(repo, "v0.1.0", fake_git)77 self.assertEqual(result["architecture"], "arm64")78 self.assertFalse(result["demo_direct_node"])79 self.assertEqual(len(result["workspace_packages"]), 3)80 self.assertIn(("git", "status", "--porcelain=v1", "--untracked-files=all"), fake_git.commands)8182 def test_source_rejects_dirty_mismatched_or_demo_default(self) -> None:83 with tempfile.TemporaryDirectory() as temporary:84 root = pathlib.Path(temporary)85 repo = self.make_repo(root)86 with self.assertRaisesRegex(VERIFIER.VerificationError, "clean"):87 VERIFIER.validate_source(repo, "v0.1.0", FakeGit(dirty=True))88 with self.assertRaisesRegex(VERIFIER.VerificationError, "exactly to HEAD"):89 VERIFIER.validate_source(repo, "v0.1.0", FakeGit(tag_commit="b" * 40))90 (repo / "src-tauri" / "Cargo.toml").write_text(91 '[package]\nname = "portal"\nversion = "0.1.0"\n'92 '[features]\ndefault = ["demo-direct-node"]\ndemo-direct-node = []\n',93 encoding="utf-8",94 )95 with self.assertRaisesRegex(VERIFIER.VerificationError, "enabled by default"):96 VERIFIER.validate_source(repo, "v0.1.0", FakeGit())97 (repo / "src-tauri" / "Cargo.toml").write_text(98 '[package]\nname = "portal"\nversion = "0.1.0"\n'99 '[features]\n'100 'default = ["experimental-non-macos-pointer-capture"]\n'101 'demo-direct-node = []\n'102 'experimental-non-macos-pointer-capture = []\n',103 encoding="utf-8",104 )105 with self.assertRaisesRegex(106 VERIFIER.VerificationError,107 "experimental-non-macos-pointer-capture must not be enabled by default",108 ):109 VERIFIER.validate_source(repo, "v0.1.0", FakeGit())110111 def write_assets(self, assets: pathlib.Path) -> None:112 assets.mkdir()113 dmg_name, checksum_name, manifest_name = VERIFIER.expected_asset_names("v0.1.0")114 dmg = assets / dmg_name115 dmg.write_bytes(b"adhoc-signed-dmg")116 digest = hashlib.sha256(dmg.read_bytes()).hexdigest()117 (assets / checksum_name).write_text(f"{digest} {dmg_name}\n", encoding="utf-8")118 manifest = VERIFIER.expected_release_manifest("v0.1.0", "a" * 40, digest)119 (assets / manifest_name).write_text(json.dumps(manifest), encoding="utf-8")120121 def test_assets_require_exact_names_digest_and_manifest(self) -> None:122 with tempfile.TemporaryDirectory() as temporary:123 root = pathlib.Path(temporary)124 repo = self.make_repo(root)125 assets = root / "assets"126 self.write_assets(assets)127 result = VERIFIER.validate_assets(repo, "v0.1.0", assets, FakeGit())128 self.assertFalse(result["gatekeeper_verified"])129 self.assertFalse(result["notarized"])130 self.assertFalse(result["developer_id_signed"])131 self.assertFalse(result["stapled"])132 self.assertFalse(result["hardened_runtime"])133 self.assertEqual(result["signing"], "adhoc")134 self.assertEqual(result["format"], 2)135 _, checksum_name, manifest_name = VERIFIER.expected_asset_names("v0.1.0")136 original_checksum = (assets / checksum_name).read_text(encoding="utf-8")137 (assets / checksum_name).write_text("0" * 64 + " wrong.dmg\n", encoding="utf-8")138 with self.assertRaisesRegex(VERIFIER.VerificationError, "checksum file"):139 VERIFIER.validate_assets(repo, "v0.1.0", assets, FakeGit())140 (assets / checksum_name).write_text(original_checksum, encoding="utf-8")141 manifest = json.loads((assets / manifest_name).read_text(encoding="utf-8"))142 manifest["notarized"] = True143 (assets / manifest_name).write_text(json.dumps(manifest), encoding="utf-8")144 with self.assertRaisesRegex(VERIFIER.VerificationError, "release manifest"):145 VERIFIER.validate_assets(repo, "v0.1.0", assets, FakeGit())146 self.write_assets_replacement(assets)147 (assets / "extra.txt").write_text("unexpected", encoding="utf-8")148 with self.assertRaisesRegex(VERIFIER.VerificationError, "exact Portal asset set"):149 VERIFIER.validate_assets(repo, "v0.1.0", assets, FakeGit())150151 def write_assets_replacement(self, assets: pathlib.Path) -> None:152 dmg_name, checksum_name, manifest_name = VERIFIER.expected_asset_names("v0.1.0")153 digest = hashlib.sha256((assets / dmg_name).read_bytes()).hexdigest()154 (assets / checksum_name).write_text(f"{digest} {dmg_name}\n", encoding="utf-8")155 manifest = VERIFIER.expected_release_manifest("v0.1.0", "a" * 40, digest)156 (assets / manifest_name).write_text(json.dumps(manifest), encoding="utf-8")157158 def test_remote_release_names_are_exact(self) -> None:159 expected = list(VERIFIER.expected_asset_names("v0.1.0"))160 self.assertEqual(161 VERIFIER.validate_release_asset_names("v0.1.0", list(reversed(expected))),162 sorted(expected),163 )164 with self.assertRaisesRegex(VERIFIER.VerificationError, "exact Portal asset set"):165 VERIFIER.validate_release_asset_names("v0.1.0", expected + ["extra"])166167 def test_website_manifest_fails_closed_and_accepts_exact_promoted_release(self) -> None:168 with tempfile.TemporaryDirectory() as temporary:169 path = pathlib.Path(temporary) / "portal-release.json"170 unavailable = {171 "format": 1,172 "product": "portal-client",173 "builds": {174 "macos-arm64": {175 "available": False,176 "reason": "No signed release has been promoted.",177 }178 },179 }180 path.write_text(json.dumps(unavailable), encoding="utf-8")181 VERIFIER.validate_website_manifest(path)182183 tag = "v0.1.0"184 asset, checksum, manifest = VERIFIER.expected_asset_names(tag)185 base = f"https://github.com/FelineStateMachine/goq/releases/download/{tag}"186 promoted = {187 "format": 1,188 "product": "portal-client",189 "builds": {190 "macos-arm64": {191 "architecture": "arm64",192 "asset": asset,193 "available": True,194 "checksum_asset": checksum,195 "checksum_url": f"{base}/{checksum}",196 "download_url": f"{base}/{asset}",197 "manifest_asset": manifest,198 "manifest_url": f"{base}/{manifest}",199 "platform": "macos",200 "release_tag": tag,201 "release_url": f"https://github.com/FelineStateMachine/goq/releases/tag/{tag}",202 "sha256": "1" * 64,203 "verification": VERIFIER.VERIFICATION,204 "version": "0.1.0",205 }206 },207 }208 path.write_text(json.dumps(promoted), encoding="utf-8")209 VERIFIER.validate_website_manifest(path)210 promoted["builds"]["macos-arm64"]["download_url"] = "https://example.invalid/dev.dmg"211 path.write_text(json.dumps(promoted), encoding="utf-8")212 with self.assertRaisesRegex(VERIFIER.VerificationError, "download URL"):213 VERIFIER.validate_website_manifest(path)214215 def test_workflow_keeps_secrets_protected_and_attaches_to_shared_draft(self) -> None:216 workflow = (REPO_DIR / ".github" / "workflows" / "portal-release.yml").read_text(encoding="utf-8")217 self.assertIn("environment:\n name: main", workflow)218 self.assertIn("workflow_dispatch:", workflow)219 self.assertIn("public-release-${{ inputs.tag }}", workflow)220 self.assertIn("release-assets", workflow)221 self.assertIn("Require the exact Sigil candidate draft", workflow)222 self.assertIn("five-asset pre-signature contract", workflow)223 self.assertIn("actions/attest@f7c74d28b9d84cb8768d0b8ca14a4bac6ef463e6", workflow)224 self.assertIn('test "$GITHUB_REF" = "refs/tags/$RELEASE_TAG"', workflow)225 self.assertIn("artifact-metadata: write", workflow)226 self.assertIn("attestations: write", workflow)227 self.assertIn("id-token: write", workflow)228 self.assertNotIn("release create", workflow)229 self.assertNotIn("--draft=false --prerelease", workflow)230 self.assertNotIn("demo-direct-node", workflow)231 self.assertNotIn("PORTAL_APPLE_CERTIFICATE_BASE64", workflow)232 self.assertNotIn("APPLE_SIGNING_IDENTITY", workflow)233 self.assertNotIn("security create-keychain", workflow)234 self.assertIn("--signing adhoc", workflow)235236 sigil_workflow = (REPO_DIR / ".github" / "workflows" / "sigil-release.yml").read_text(237 encoding="utf-8"238 )239 self.assertIn("complete six-asset public-alpha contract", sigil_workflow)240 self.assertIn("verify-portal-release.py assets", sigil_workflow)241 self.assertIn("runs-on: macos-26", sigil_workflow)242 self.assertIn("verify-macos-portal-signature.sh", sigil_workflow)243 self.assertIn("gh attestation verify", sigil_workflow)244 self.assertIn("--signer-workflow", sigil_workflow)245 self.assertIn("--source-ref", sigil_workflow)246 self.assertIn("--source-digest", sigil_workflow)247 self.assertNotIn("portal-apple-team-id.txt", sigil_workflow)248 self.assertNotIn("PORTAL_APPLE_TEAM_ID", sigil_workflow)249 self.assertIn("--signing adhoc", sigil_workflow)250 self.assertLess(251 sigil_workflow.index("verify-portal-release.py assets"),252 sigil_workflow.index("--draft=false"),253 )254 self.assertLess(255 sigil_workflow.index("verify-macos-portal-signature.sh"),256 sigil_workflow.index("--draft=false"),257 )258 package = (REPO_DIR / "scripts" / "package-macos-client.sh").read_text(encoding="utf-8")259 self.assertIn("--target aarch64-apple-darwin", package)260 self.assertIn('[[ "$architectures" == arm64 ]]', package)261 self.assertNotIn("--expected-arch", package)262 self.assertNotIn("--features", package)263 self.assertIn("--signing adhoc|developer-id is required", package)264 self.assertIn("Signature=adhoc", package)265 self.assertIn('[[ "${APPLE_TEAM_ID:-}" == "$expected_team_id" ]]', package)266267 native_verifier = (REPO_DIR / "scripts" / "verify-macos-portal-signature.sh").read_text(268 encoding="utf-8"269 )270 self.assertIn("--expected-team-id", native_verifier)271 self.assertIn('[[ "$team_identifier" == "$expected_team_id" ]]', native_verifier)272 self.assertIn("Signature=adhoc", native_verifier)273 self.assertIn("unexpectedly carries a notarization ticket", native_verifier)274275276if __name__ == "__main__":277 unittest.main()