#!/usr/bin/env python3 """Capture the unmodified pinned PWA with synthetic data, never a user's server.""" import argparse import json import os from pathlib import Path import secrets import subprocess import tempfile import time ROOT = Path(__file__).resolve().parents[1] REVISION = "10bfef7686ed7303b4c74c7fcc55a5eb16356c9d" def run(upstream, playwright_module, destination, width=360, height=640): revision = subprocess.check_output(["git", "-C", str(upstream), "rev-parse", "HEAD"], text=True).strip() assert revision == REVISION, "Visual reference must use pinned unmodified upstream" assert not subprocess.check_output(["git", "-C", str(upstream), "diff", "HEAD", "--name-only"], text=True).strip() destination.mkdir(parents=True, exist_ok=True) base = "/tmp/opencode" if Path("/tmp/opencode").exists() else None with tempfile.TemporaryDirectory(prefix="yuvomi-visual-", dir=base) as temporary: directory = Path(temporary) subprocess.run([ "openssl", "req", "-x509", "-newkey", "rsa:2048", "-nodes", "-days", "1", "-keyout", str(directory / "key.pem"), "-out", str(directory / "cert.pem"), "-subj", "/CN=Yuvomi visual fixture", "-addext", "subjectAltName=IP:127.0.0.1,DNS:localhost", ], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) env = {**os.environ, "NODE_EXTRA_CA_CERTS": str(directory / "cert.pem"), "YUVOMI_TEST_DISABLE_OIDC": "true", "YUVOMI_TEST_PASSWORD": secrets.token_urlsafe(24), "YUVOMI_VISUAL_WIDTH": str(width), "YUVOMI_VISUAL_HEIGHT": str(height)} with (directory / "server.log").open("w") as log: server = subprocess.Popen(["node", str(ROOT / "scripts/integration-server.mjs"), str(upstream), temporary], cwd=upstream, env=env, stdout=log, stderr=log) try: for _ in range(200): if (directory / "ready.json").exists(): break if server.poll() is not None: raise RuntimeError("Visual fixture failed to start; no private logs exported") time.sleep(0.1) ready = json.loads((directory / "ready.json").read_text()) env["YUVOMI_TEST_URL"] = f"https://127.0.0.1:{ready['port']}" subprocess.run(["node", str(ROOT / "scripts/capture-pwa-reference.mjs"), str(playwright_module), str(destination)], env=env, check=True) finally: server.terminate() try: server.wait(timeout=10) except subprocess.TimeoutExpired: server.kill() server.wait() if __name__ == "__main__": parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("upstream", type=Path) parser.add_argument("--playwright-module", type=Path, required=True) parser.add_argument("--output", type=Path, default=ROOT / "artifacts/visual/pwa") parser.add_argument("--width", type=int, default=360) parser.add_argument("--height", type=int, default=640) args = parser.parse_args() assert 320 <= args.width <= 1600 and 320 <= args.height <= 1800, "Invalid reference viewport" run(args.upstream.resolve(), args.playwright_module.resolve(), args.output.resolve(), args.width, args.height)