#!/usr/bin/env python3 """Compiler-independent source-size and test-isolation contracts for non-Rust modules.""" from __future__ import annotations from pathlib import Path import os import re import shutil import subprocess import tempfile ROOT = Path(__file__).resolve().parents[1] MAX_LINES = 500 def lines(path: Path) -> int: return len(path.read_text(encoding="utf-8").splitlines()) # Browser implementation modules are bounded. Test harnesses under scripts/ are checked separately. for path in sorted((ROOT / "src/admin/web").rglob("*.js")): assert lines(path) <= MAX_LINES, f"{path.relative_to(ROOT)} has {lines(path)} lines (max {MAX_LINES})" # Docker Agent implementation and Go tests are separate files and all remain bounded. for path in sorted((ROOT / "docker-agent").glob("*.go")): assert lines(path) <= MAX_LINES, f"{path.relative_to(ROOT)} has {lines(path)} lines (max {MAX_LINES})" if not path.name.endswith("_test.go"): text = path.read_text(encoding="utf-8") assert not re.search(r"(?m)^func\s+Test[A-Z]", text), f"Go test remains in implementation file: {path.relative_to(ROOT)}" # Operational Python entry points are split as product modules; large QA harnesses are intentionally exempt. for pattern in ("deploy*.py", "migrate_npm*.py"): for path in sorted((ROOT / "scripts").glob(pattern)): assert lines(path) <= MAX_LINES, f"{path.relative_to(ROOT)} has {lines(path)} lines (max {MAX_LINES})" for old in (ROOT / "src/admin/web/app.js", ROOT / "src/admin/web/vhosts.js"): assert not old.exists(), f"oversized browser monolith returned: {old.relative_to(ROOT)}" for required in ( "src/admin/web/app/core.js", "src/admin/web/app/config-history.js", "src/admin/web/app/runtime.js", "src/admin/web/app/settings.js", "src/admin/web/vhosts/core.js", "src/admin/web/vhosts/editor.js", "src/admin/web/vhosts/render.js", "docker-agent/model.go", "docker-agent/docker.go", "docker-agent/network.go", "docker-agent/certificate.go", "docker-agent/server.go", "scripts/deploy_core.py", "scripts/deploy_runtime.py", "scripts/migrate_npm_core.py", "scripts/migrate_npm_api.py", ): assert (ROOT / required).is_file(), f"missing split source module: {required}" # When Node is available, parse every standalone browser module and both assembled public bundles. node = shutil.which("node") if node: env = dict(os.environ, TERM=os.environ.get("TERM", "xterm")) standalone = list((ROOT / "src/admin/web").rglob("*.js")) for path in sorted(standalone): subprocess.run([node, "--check", str(path)], check=True, env=env, stdout=subprocess.DEVNULL) from web_assets import read_app_js, read_vhosts_js with tempfile.TemporaryDirectory(prefix="rustproxy-js-contract-") as directory: temporary = Path(directory) for name, content in (("app.js", read_app_js(ROOT)), ("vhosts.js", read_vhosts_js(ROOT))): target = temporary / name target.write_text(content, encoding="utf-8") subprocess.run([node, "--check", str(target)], check=True, env=env, stdout=subprocess.DEVNULL) # The public vhosts asset wraps three individually valid source modules in one private IIFE. # Every source part is parsed above; this additional check keeps the assembled browser asset valid. print(f"PASS: browser, Docker Agent and operational Python implementation modules are <= {MAX_LINES} lines, tests are isolated, and browser source boundaries parse cleanly.")