From 625df9c81790ead8ac33d9398dde2b272d473eba Mon Sep 17 00:00:00 2001 From: LunaStev Date: Tue, 20 Oct 2026 00:06:25 +0900 Subject: [PATCH] Approve pinned std-compatible installers for Nightly Keep the exact deployed-byte gate and the v0.2.1-pre-beta installer policy. Report expected and received hashes and verify that either unapproved endpoint blocks identity export. Closes #966 Signed-off-by: LunaStev --- tools/ci/nightly.py | 8 ++++++-- tools/ci/nightly_installers.json | 4 ++-- tools/test_nightly.py | 35 ++++++++++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 4 deletions(-) diff --git a/tools/ci/nightly.py b/tools/ci/nightly.py index c5e4151b..b1003d16 100644 --- a/tools/ci/nightly.py +++ b/tools/ci/nightly.py @@ -161,8 +161,12 @@ def installers_ready(): raise RuntimeError( f"cannot verify deployed installer {url}: {error}" ) from error - if len(data) > 1024 * 1024 or hashlib.sha256(data).hexdigest() != expected: - raise ValueError(f"reviewed latest-only installer is not deployed: {url}") + actual = hashlib.sha256(data).hexdigest() + if len(data) > 1024 * 1024 or actual != expected: + raise ValueError( + f"reviewed installer is not deployed: {url} " + f"(expected SHA-256 {expected}, received {actual})" + ) def file_record(path): diff --git a/tools/ci/nightly_installers.json b/tools/ci/nightly_installers.json index 2e912a05..6e54d0c3 100644 --- a/tools/ci/nightly_installers.json +++ b/tools/ci/nightly_installers.json @@ -1,4 +1,4 @@ { - "https://wave-lang.dev/install.sh": "861356ad9822868ed3e5aa25438c2158ca503c62012639ea24b68c54ff274921", - "https://wave-lang.dev/install.ps1": "2dc19d0e8cfe9907d11b1a204e1812e0e53955ffc755774b4936e9eb21106995" + "https://wave-lang.dev/install.sh": "f6f38509dfa3b7d0172bf79b6eef8ed589d5261c82c09cd781b1f90d600b4b3f", + "https://wave-lang.dev/install.ps1": "5f01fb3fcae0ee9804107afe5df2d8945f2244c8acb385ed3afea10bc10d562e" } diff --git a/tools/test_nightly.py b/tools/test_nightly.py index 5b55f57c..7878ed0b 100644 --- a/tools/test_nightly.py +++ b/tools/test_nightly.py @@ -646,6 +646,41 @@ def test_gate_reports_installer_url_and_exports_nothing_on_network_failure(self) self.assertIn(str(error), stderr.getvalue()) self.assertFalse(output.exists()) + def test_gate_blocks_each_unapproved_installer_without_exporting_identity(self): + identity = dict(source_sha=A, compiler_version=self.version, ci_run_id=10) + approved = b"reviewed pinned installer" + urls = list(json.loads(nightly.INSTALLERS_FILE.read_text())) + expected = hashlib.sha256(approved).hexdigest() + pins = self.root / "pins.json" + pins.write_text(json.dumps({url: expected for url in urls})) + for index, url in enumerate(urls): + for unapproved in (b"previous latest-only installer", b"changed pinned installer"): + with self.subTest(url=url, data=unapproved): + output = self.root / "mismatched-outputs" + stderr = io.StringIO() + responses = [io.BytesIO(approved) for _ in range(index)] + responses.append(io.BytesIO(unapproved)) + with patch.dict( + os.environ, + { + "GITHUB_REPOSITORY": nightly.REPO, + "GITHUB_OUTPUT": str(output), + }, + ), patch.object( + nightly, "eligible", return_value=identity + ), patch.object( + nightly, "urlopen", side_effect=responses + ), patch.object(nightly, "INSTALLERS_FILE", pins), patch( + "sys.stderr", stderr + ): + self.assertEqual(nightly.main(["--stage", "gate"]), 1) + self.assertIn( + f"reviewed installer is not deployed: {url}", stderr.getvalue() + ) + self.assertIn(f"expected SHA-256 {expected}", stderr.getvalue()) + self.assertIn(hashlib.sha256(unapproved).hexdigest(), stderr.getvalue()) + self.assertFalse(output.exists()) + def test_publication_is_opt_in_and_versioned_rules_remain_strict(self): with patch.dict(os.environ, {"GITHUB_REPOSITORY": "fork/Wave"}), patch.object( nightly, "GitHub"