{html.escape(capture['name'])}
" f'{html.escape(route)} · '
f'View code{html.escape(ttfb_text)}
#!/usr/bin/env python3 import hashlib import html import json import os import subprocess import struct import sys import tomllib from datetime import datetime, timezone from pathlib import Path from urllib.parse import urlparse REPO_DIR = Path(__file__).resolve().parents[1] CAPTURE_PROFILES = { "desktop": (1600, 1000), "tablet": (1024, 1366), "mobile": (390, 844), } REQUIRED_VIEW_NAMES = { "First-time setup wizard", "Login", "Public snapshot list", "Add URLs", "Admin dashboard", "AI agent", "Snapshots table", "Snapshots grid", "Snapshot admin detail", "Snapshot View (capture in progress)", "Snapshot View (header collapsed)", "Snapshot files", "Archive results", "Archive result detail", "Crawl detail", "Crawl schedules", "Crawl schedule detail", "Persona detail", "Machine detail", "Network interface detail", "Binary detail", "Process detail", "API tokens", "API token detail", "Webhooks", "Webhook detail", "Environment", "Configuration", "Configuration detail", "Dependencies", "Dependency detail", "Plugins", "Workers", "Worker detail", "Logs", "Log detail", } def build_provenance() -> dict[str, str]: version = tomllib.loads((REPO_DIR / "pyproject.toml").read_text(encoding="utf-8"))["project"]["version"] revision = os.environ.get("GITHUB_SHA", "").strip() if not revision: revision = subprocess.run( ["git", "rev-parse", "HEAD"], cwd=REPO_DIR, check=True, capture_output=True, text=True, ).stdout.strip() return { "version": version, "revision": revision, "generated_at": datetime.now(timezone.utc).isoformat(timespec="seconds"), } def validate_navigation(metadata_path: Path, expected_path: str) -> None: navigation = json.loads(metadata_path.read_text(encoding="utf-8")) navigation = navigation.get("checks", navigation) status = navigation.get("status") final_url = navigation.get("finalUrl") or "" actual_path = urlparse(final_url).path if status != 200: raise SystemExit(f"expected HTTP 200, got {status}: {final_url or metadata_path}") if actual_path != expected_path: raise SystemExit(f"expected final path {expected_path}, got {actual_path}: {final_url}") def append_manifest(manifest_path: Path, screenshot_path: Path) -> None: data = screenshot_path.read_bytes() if data[:8] != b"\x89PNG\r\n\x1a\n" or len(data) < 24: raise SystemExit(f"not a valid PNG: {screenshot_path}") dimensions = struct.unpack(">II", data[16:24]) profile = os.environ["UI_SCREENSHOT_PROFILE"] if profile not in CAPTURE_PROFILES: raise SystemExit(f"unknown screenshot profile: {profile}") expected_dimensions = CAPTURE_PROFILES[profile] if dimensions != expected_dimensions: raise SystemExit( f"expected {expected_dimensions[0]}x{expected_dimensions[1]} for {profile}, " f"got {dimensions[0]}x{dimensions[1]}: {screenshot_path}", ) item = { "name": os.environ["UI_SCREENSHOT_NAME"], "url": os.environ["UI_SCREENSHOT_URL"], "source": os.environ["UI_SCREENSHOT_SOURCE"], "filename": os.environ["UI_SCREENSHOT_FILENAME"], "profile": profile, "width": dimensions[0], "height": dimensions[1], } timing_report_path = os.environ.get("UI_SCREENSHOT_TIMING_REPORT", "").strip() if timing_report_path: timing_report = json.loads(Path(timing_report_path).read_text(encoding="utf-8")) ttfb_ms = timing_report.get("checks", {}).get("ttfbMs") if isinstance(ttfb_ms, int | float): item["ttfb_ms"] = round(ttfb_ms) with manifest_path.open("a", encoding="utf-8") as manifest: manifest.write(json.dumps(item) + "\n") def build_galleries(manifest_path: Path, markdown_path: Path, html_path: Path) -> None: provenance = build_provenance() source_base_url = f"https://github.com/ArchiveBox/ArchiveBox/blob/{provenance['revision']}/" captures = [json.loads(line) for line in manifest_path.read_text(encoding="utf-8").splitlines() if line] allow_partial = os.environ.get("UI_SCREENSHOT_ALLOW_PARTIAL") == "1" grouped_captures: list[dict[str, object]] = [] captures_by_name: dict[str, dict[str, object]] = {} for capture in captures: group = captures_by_name.get(capture["name"]) if group is None: group = { "name": capture["name"], "url": capture["url"], "source": capture["source"], "variants": {}, } captures_by_name[capture["name"]] = group grouped_captures.append(group) elif group["url"] != capture["url"] or group["source"] != capture["source"]: raise SystemExit(f"inconsistent manifest metadata for {capture['name']}") variants = group["variants"] assert isinstance(variants, dict) if capture["profile"] in variants: raise SystemExit(f"duplicate {capture['profile']} capture for {capture['name']}") variants[capture["profile"]] = capture complete_groups = [] for group in grouped_captures: variants = group["variants"] assert isinstance(variants, dict) missing_profiles = [profile for profile in CAPTURE_PROFILES if profile not in variants] if missing_profiles: if allow_partial: continue raise SystemExit(f"missing {', '.join(missing_profiles)} capture for {group['name']}") complete_groups.append(group) grouped_captures = complete_groups if not allow_partial: captured_names = set(captures_by_name) missing = sorted(REQUIRED_VIEW_NAMES - captured_names) if missing: raise SystemExit(f"required UI screenshot coverage is missing: {', '.join(missing)}") snapshot_output_views = [ name for name in captured_names if name.startswith("Snapshot View (") and name not in {"Snapshot View (capture in progress)", "Snapshot View (header collapsed)"} ] if len(snapshot_output_views) < 20: raise SystemExit( f"expected at least 20 rendered Sweeting.me snapshot outputs, got {len(snapshot_output_views)}", ) markdown_sections = [] html_sections = [] for capture in grouped_captures: variants = capture["variants"] assert isinstance(variants, dict) parsed_url = urlparse(str(capture["url"])) route = parsed_url.path or "/" if parsed_url.fragment: route = f"{route}#{parsed_url.fragment}" source_url = f"{source_base_url}{capture['source']}" ttfb_values = [variant["ttfb_ms"] for variant in variants.values() if isinstance(variant.get("ttfb_ms"), int | float)] ttfb_text = f" · ~{round(sum(ttfb_values) / len(ttfb_values))}ms TTFB" if ttfb_values else "" markdown_cells = [] html_figures = [] for profile, (width, height) in CAPTURE_PROFILES.items(): variant = variants[profile] label = f"{profile.title()} ({width}x{height})" filename = str(variant["filename"]) screenshot_path = markdown_path.parent / "screenshots" / filename if not screenshot_path.is_file(): raise SystemExit(f"missing screenshot for gallery: {screenshot_path}") cache_version = hashlib.sha256(screenshot_path.read_bytes()).hexdigest()[:12] versioned_filename = f"{html.escape(filename)}?v={cache_version}" markdown_cells.append( f'
| {profile.title()} | " for profile in CAPTURE_PROFILES), "
|---|
{html.escape(route)} · '
f'View code{html.escape(ttfb_text)}
Generated from ArchiveBox {html.escape(provenance['version'])} at "
f''
f"{html.escape(provenance['revision'][:12])} on "
f'. '
"Desktop, tablet, and mobile viewports are captured from the same build.