"""Real bootstrap downloads preserve the pin across an upstream outage.""" import hashlib import io import json import os from pathlib import Path import shutil import subprocess import tarfile import threading from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer import pytest ROOT = Path(__file__).resolve().parents[2] @pytest.fixture def server(): class Handler(BaseHTTPRequestHandler): def do_GET(self): self.server.requests.append(self.path) body = self.server.files.get(self.path) self.send_response(200 if body is not None else 404) self.send_header("Content-Length", str(len(body or b""))) self.end_headers() self.wfile.write(body or b"") def log_message(self, *_args): pass http = ThreadingHTTPServer(("127.0.0.1", 0), Handler) http.files, http.requests = {}, [] thread = threading.Thread(target=http.serve_forever, daemon=True) thread.start() try: yield http, f"http://127.0.0.1:{http.server_port}" finally: http.shutdown() http.server_close() thread.join(timeout=5) def fixture_bytes(server, mode, body): http, base = server if mode != "missing": http.files["/primary"] = b"bad bytes" if mode == "corrupt" else body http.files["/mirror"] = body return base + "/primary", base + "/mirror", hashlib.sha256(body).hexdigest() @pytest.mark.platforms("windows") @pytest.mark.parametrize("mode", ["primary", "missing", "corrupt", "both-missing"]) def test_windows_download_uses_only_verified_candidates(tmp_path, server, mode): body = b"exact archive bytes" primary, mirror, digest = fixture_bytes(server, mode, body) if mode == "both-missing": server[0].files.clear() destination = tmp_path / "out.zip" script = tmp_path / "driver.ps1" script.write_text( f". '{ROOT / 'scripts/install.ps1'}' -HermesHome '{tmp_path / 'home'}' -InstallDir '{tmp_path / 'repo'}'\n" f"Invoke-VerifiedDownload -Url '{primary}' -MirrorUrl '{mirror}' -Sha256 '{digest}' -OutFile '{destination}'\n", encoding="utf-8", ) result = subprocess.run(["powershell", "-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-File", str(script)], capture_output=True, text=True, timeout=60) if mode in ("primary", "missing"): assert result.returncode == 0, result.stdout + result.stderr assert destination.read_bytes() == body else: assert result.returncode != 0 assert primary in result.stdout + result.stderr if mode == "both-missing": assert mirror in result.stdout + result.stderr assert server[0].requests == (["/primary", "/mirror"] if mode in ("missing", "both-missing") else ["/primary"]) @pytest.mark.platforms("windows") @pytest.mark.parametrize("name", ["uv", "git"]) def test_windows_bootstrap_rejects_corrupt_bytes_before_extract(tmp_path, server, name): primary, mirror, digest = fixture_bytes(server, "corrupt", b"expected archive") script = tmp_path / "caller.ps1" script.write_text( f". '{ROOT / 'scripts/install.ps1'}' -HermesHome '{tmp_path / 'home'}'\n" f"$env:HERMES_RUNTIME_DIR = '{tmp_path / 'tools'}'\n" "function Get-Command { param($Name) return $null }\n" "$target = 'win32-' + (Get-WindowsArch)\n" f"$pin = @{{Url='{primary}'; MirrorUrl='{mirror}'; Sha256='{digest}'}}\n" + ("$script:UvPinFiles[$target] = $pin\nGet-Uv\n" if name == "uv" else "$script:GitPinFiles[$target] = $pin\nGet-PinnedGit\n"), encoding="utf-8", ) result = subprocess.run(["powershell", "-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-File", str(script)], capture_output=True, text=True, timeout=60) assert result.returncode != 0 assert "digest mismatch" in result.stdout + result.stderr assert server[0].requests == ["/primary"] assert not (tmp_path / "tools").exists() def uv_archive(): data = b"#!/bin/sh\n[ \"$1\" = --version ] && { printf 'fixture uv\\n'; exit 0; }; exit 73\n" buffer = io.BytesIO() with tarfile.open(fileobj=buffer, mode="w:gz") as archive: member = tarfile.TarInfo("uv/uv") member.mode, member.size = 0o755, len(data) archive.addfile(member, io.BytesIO(data)) return buffer.getvalue() @pytest.mark.platforms("posix") @pytest.mark.parametrize("mode", ["primary", "missing", "corrupt", "both-missing"]) def test_posix_download_keeps_the_pinned_hash(tmp_path, server, mode): primary, mirror, digest = fixture_bytes(server, mode, uv_archive()) if mode == "both-missing": server[0].files.clear() script = f""" source '{ROOT / 'scripts/install.sh'}' command() {{ if [ "$*" = '-v uv' ]; then return 1; fi; builtin command "$@"; }} uv_bootstrap_pin() {{ UV_PIN_VERSION=fixture; UV_PIN_URL='{primary}'; UV_PIN_MIRROR='{mirror}'; UV_PIN_SHA256='{digest}'; }} ensure_uv """ env = {**os.environ, "HERMES_HOME": str(tmp_path / "home"), "HOME": str(tmp_path / "home"), "HERMES_RUNTIME_DIR": str(tmp_path / "tools")} result = subprocess.run(["bash", "-c", script], cwd=tmp_path, env=env, capture_output=True, text=True, timeout=60) assert (result.returncode == 0) == (mode in ("primary", "missing")), result.stdout + result.stderr if mode == "corrupt": assert "digest mismatch" in result.stderr if mode == "both-missing": assert primary in result.stderr and mirror in result.stderr assert server[0].requests == (["/primary", "/mirror"] if mode in ("missing", "both-missing") else ["/primary"]) @pytest.mark.platforms("windows") def test_generated_windows_pins_match_the_shared_authority(tmp_path): from pm.artifact_mirror import mirror_url script = tmp_path / "pins.ps1" script.write_text( f". '{ROOT / 'scripts/install.ps1'}' -HermesHome '{tmp_path / 'home'}'\n" "@{uv=$script:UvPinFiles; git=$script:GitPinFiles} | ConvertTo-Json -Depth 5 -Compress\n", encoding="utf-8", ) result = subprocess.run(["powershell", "-NoProfile", "-NonInteractive", "-File", str(script)], capture_output=True, text=True, timeout=30) assert result.returncode == 0, result.stderr emitted = json.loads(result.stdout) lock = json.loads((ROOT / "pm/lock.json").read_text(encoding="utf-8"))["packages"] for name, targets in emitted.items(): assert targets for target, pin in targets.items(): authority = lock[name]["artifacts"][target] assert (pin["Url"], pin["Sha256"], pin["MirrorUrl"]) == (authority["url"], authority["sha256"], mirror_url(authority["sha256"])) @pytest.mark.platforms("posix") def test_dev_setup_reaches_the_same_mirror_without_python(tmp_path, server): from pm.store import current_target body = uv_archive() digest = hashlib.sha256(body).hexdigest() http, base = server http.files["/archive/" + digest] = body repo = tmp_path / "repo" (repo / "pm").mkdir(parents=True) shutil.copyfile(ROOT / "setup-hermes.sh", repo / "setup-hermes.sh") (repo / "pm/artifact-mirror.json").write_text(json.dumps({"origin": base, "prefix": "archive/"}, indent=2), encoding="utf-8") (repo / "pm/lock.json").write_text(json.dumps({"packages": { "uv": {"version": "fixture", "artifacts": {current_target(): {"url": base + "/missing.tar.gz", "sha256": digest}}}, "python": {"version": "3.14.7"}, }}, indent=2, sort_keys=True), encoding="utf-8") env = {**os.environ, "HERMES_HOME": str(tmp_path / "home"), "HOME": str(tmp_path / "home"), "HERMES_RUNTIME_DIR": str(tmp_path / "tools")} result = subprocess.run(["bash", str(repo / "setup-hermes.sh")], cwd=repo, env=env, text=True, capture_output=True, timeout=60) assert result.returncode == 73, result.stdout + result.stderr # stop at the bootstrap interpreter boundary assert http.requests == ["/missing.tar.gz", "/archive/" + digest]