room-visualizer / verify_live_frontend.py
GitHub Actions
Deploy from GitHub commit feae62fa5b6d65b6bfe64ecd37fd85011bd7fb99
a4f876a
Raw
History Blame Contribute Delete
2.92 kB
"""R0-4 β€” post-deploy FRONTEND gate.
Three test rounds were invalidated by a stale frontend (Vercel promote
skipped twice; an outdated URL once). This gate verifies the ARTIFACT the
testers actually load: fetch the canonical production URL, locate the built
bundle, and assert it carries the current engine's markers and points at the
right backend. Run it after every Vercel promote β€” a failure means testers
would be looking at old code.
Pure stdlib (no pip installs in CI).
Usage:
python verify_live_frontend.py [base_url]
# default: https://room-editor-9y3b.vercel.app
Exit 0 = the served frontend is current; non-zero = block/alert.
"""
import re
import sys
import urllib.request
CANONICAL_URL = "https://room-editor-9y3b.vercel.app"
BACKEND_HOST = "modventures-room-visualizer.hf.space"
# Marker inventory: object property names survive minification; function
# names do not. Keep one marker per shipped feature generation so a stale
# build is identifiable by WHICH markers are missing.
ENGINE_MARKERS = [
("defaultRotation", "P1-4 rotation consumption"),
("keepRug", "R3-3 rug toggle"),
("tileWidthM", "R1-3/R2-1 metric sizing"),
("groutFrac", "R2-3 procedural cells"),
("rugFlags", "R3-3 rug flags"),
("Classic Checkered", "catalog present"),
("Calacatta Marble", "R2-2 seamless pack (2026-06-12)"),
("viz2d/convert", "backend API wiring"),
(BACKEND_HOST, "points at the canonical backend"),
]
def fetch(url: str, timeout: int = 30) -> str:
req = urllib.request.Request(url, headers={"User-Agent": "verify-live-frontend"})
with urllib.request.urlopen(req, timeout=timeout) as r:
return r.read().decode("utf-8", errors="replace")
def main() -> int:
base = sys.argv[1] if len(sys.argv) > 1 else CANONICAL_URL
print(f"frontend gate target: {base}", flush=True)
html = fetch(base)
ok = True
title = re.search(r"<title>([^<]*)</title>", html)
title_ok = bool(title) and "BAETES" in title.group(1)
print(f" [{'PASS' if title_ok else 'FAIL'}] title is the current app "
f"({title.group(1) if title else 'none'})")
ok &= title_ok
asset = re.search(r"assets/index-[A-Za-z0-9_-]+\.js", html)
if not asset:
print(" [FAIL] no built index-*.js asset referenced β€” not a Vite build?")
raise SystemExit("FRONTEND GATE FAILED")
print(f" bundle: {asset.group(0)}")
bundle = fetch(f"{base}/{asset.group(0)}", timeout=60)
for marker, why in ENGINE_MARKERS:
passed = marker in bundle
print(f" [{'PASS' if passed else 'FAIL'}] {marker} ({why})")
ok &= passed
if not ok:
print("\nFRONTEND GATE FAILED β€” the served bundle is stale or "
"mis-deployed. Did the Vercel production promote happen?")
return 1
print("\nFRONTEND GATE PASSED")
return 0
if __name__ == "__main__":
raise SystemExit(main())