| |
| """ |
| RUBRA β scripts/test_hf_render.py |
| Decoupled HyperFrames verification probe. |
| |
| Run this manually (or in a CI/CD pre-deploy step) AFTER the Dockerfile |
| installs node + hyperframes, BEFORE flipping RUBRA_VIDEO_ENABLED=true. |
| |
| What it does: |
| 1. Renders a tiny, deliberately cheap HTML scene (small viewport, ~1s, |
| no external assets) β enough to prove the node/hyperframes/CPU |
| pipeline works on THIS container, without burning a full render budget. |
| 2. Never raises past its own boundary β any exception is caught and |
| reported as a clean FAIL, so this can't itself crash a CI runner. |
| 3. Exits 0 on pass (safe to flip the env var) / 1 on fail (keep it off). |
| 4. Optionally writes the verdict to a state file so a deploy script can |
| read pass/fail without parsing stdout. |
| |
| This script is intentionally decoupled from main.py/agent.py β it |
| imports nothing from the live app, so it can be run standalone in a |
| throwaway container during image build/CI, before the real app ever |
| starts. |
| """ |
|
|
| import os |
| import sys |
| import json |
| import time |
| import shutil |
| import subprocess |
| import tempfile |
| from pathlib import Path |
|
|
| RENDER_TIMEOUT_S = 45 |
| STATE_FILE = os.getenv("RUBRA_RENDER_TEST_STATE", "/tmp/hf_render_test_result.json") |
|
|
| |
| |
| |
| _PROBE_HTML = """<!DOCTYPE html> |
| <html><head><style> |
| body { margin:0; width:160px; height:120px; background:#111; } |
| .box { width:40px; height:40px; background:#0f0; margin:40px auto; |
| animation: spin 1s linear; } |
| @keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } |
| </style></head> |
| <body><div class="box"></div></body></html>""" |
|
|
|
|
| def _write_state(passed: bool, detail: str): |
| try: |
| Path(STATE_FILE).write_text(json.dumps({ |
| "passed": passed, |
| "detail": detail[:500], |
| "timestamp": time.time(), |
| })) |
| except Exception: |
| pass |
|
|
|
|
| def check_binaries() -> tuple[bool, str]: |
| """Cheap pre-check before even attempting a render.""" |
| if shutil.which("node") is None: |
| return False, "node binary not found on PATH β Dockerfile node install step missing/failed" |
| if shutil.which("npx") is None: |
| return False, "npx binary not found on PATH" |
| return True, "node/npx present" |
|
|
|
|
| def run_probe_render() -> tuple[bool, str]: |
| """The actual tiny render attempt. Never lets an exception escape.""" |
| try: |
| with tempfile.TemporaryDirectory() as tmp: |
| html_path = os.path.join(tmp, "probe.html") |
| out_path = os.path.join(tmp, "probe.mp4") |
| Path(html_path).write_text(_PROBE_HTML, encoding="utf-8") |
|
|
| start = time.time() |
| result = subprocess.run( |
| ["npx", "hyperframes", "render", html_path, "-o", out_path], |
| capture_output=True, text=True, timeout=RENDER_TIMEOUT_S, |
| ) |
| elapsed = time.time() - start |
|
|
| if result.returncode != 0: |
| return False, f"hyperframes exited {result.returncode} after {elapsed:.1f}s: {result.stderr[:300]}" |
|
|
| if not os.path.exists(out_path): |
| return False, "hyperframes reported success but produced no output file" |
|
|
| size = os.path.getsize(out_path) |
| if size < 100: |
| return False, f"output file exists but is suspiciously small ({size} bytes) β likely corrupt" |
|
|
| return True, f"Rendered {size} bytes in {elapsed:.1f}s β pipeline confirmed working" |
|
|
| except subprocess.TimeoutExpired: |
| return False, f"Render exceeded {RENDER_TIMEOUT_S}s on a TINY probe scene β this container's CPU pool likely can't sustain real renders" |
| except FileNotFoundError as e: |
| return False, f"Binary not found during execution: {e}" |
| except Exception as e: |
| |
| |
| return False, f"Unexpected exception during probe render: {type(e).__name__}: {e}" |
|
|
|
|
| def main() -> int: |
| print("[HF_RENDER_TEST] Checking for node/npx binaries...") |
| bin_ok, bin_detail = check_binaries() |
| print(f"[HF_RENDER_TEST] {bin_detail}") |
| if not bin_ok: |
| _write_state(False, bin_detail) |
| print("[HF_RENDER_TEST] FAIL β keeping RUBRA_VIDEO_ENABLED=false") |
| return 1 |
|
|
| print("[HF_RENDER_TEST] Running tiny probe render...") |
| render_ok, render_detail = run_probe_render() |
| print(f"[HF_RENDER_TEST] {render_detail}") |
|
|
| _write_state(render_ok, render_detail) |
|
|
| if render_ok: |
| print("[HF_RENDER_TEST] PASS β safe to set RUBRA_VIDEO_ENABLED=true") |
| return 0 |
| else: |
| print("[HF_RENDER_TEST] FAIL β keeping RUBRA_VIDEO_ENABLED=false") |
| return 1 |
|
|
|
|
| if __name__ == "__main__": |
| sys.exit(main()) |