113 lines
3.1 KiB
Python
113 lines
3.1 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Repo Truth Guard for the-nexus
|
|
==============================
|
|
Machine-checkable validation that current `main` matches the canonical
|
|
deployment truth. Prevents migration-era ambiguity from re-entering audits.
|
|
|
|
Exit 0 = truth validated
|
|
Exit 1 = drift detected
|
|
"""
|
|
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
REPO_ROOT = Path(__file__).parent.parent
|
|
|
|
# Canonical truth: what main currently IS and IS NOT
|
|
CANONICAL_TRUTH = {
|
|
"description": (
|
|
"the-nexus main is a Python bridge/gateway (server.py) plus "
|
|
"infrastructure-as-code (branch protection, workflows, fleet configs). "
|
|
"It is NOT the browser-world visualization surface (not yet restored)."
|
|
),
|
|
"required_paths": [
|
|
"server.py",
|
|
"Dockerfile",
|
|
"docker-compose.yml",
|
|
"deploy.sh",
|
|
"nexus/morning_report.py",
|
|
".gitea/workflows/ci.yml",
|
|
"gitea_api/branch_protection.py",
|
|
"robots.txt",
|
|
],
|
|
"forbidden_paths": [
|
|
# Migration-era browser-world artifacts that should not be in main
|
|
"browser-world/index.html",
|
|
"src/frontend",
|
|
"vite.config.ts",
|
|
"package-lock.json",
|
|
],
|
|
"required_in_dockerfile": [
|
|
"server.py",
|
|
"nexus/",
|
|
],
|
|
"required_py_deps": [
|
|
"websockets",
|
|
],
|
|
}
|
|
|
|
|
|
def check_required_paths() -> list[str]:
|
|
failures = []
|
|
for p in CANONICAL_TRUTH["required_paths"]:
|
|
if not (REPO_ROOT / p).exists():
|
|
failures.append(f"MISSING required path: {p}")
|
|
return failures
|
|
|
|
|
|
def check_forbidden_paths() -> list[str]:
|
|
failures = []
|
|
for p in CANONICAL_TRUTH["forbidden_paths"]:
|
|
if (REPO_ROOT / p).exists():
|
|
failures.append(f"UNEXPECTED forbidden path found: {p}")
|
|
return failures
|
|
|
|
|
|
def check_dockerfile() -> list[str]:
|
|
failures = []
|
|
dockerfile = REPO_ROOT / "Dockerfile"
|
|
if not dockerfile.exists():
|
|
failures.append("MISSING Dockerfile")
|
|
return failures
|
|
content = dockerfile.read_text()
|
|
for token in CANONICAL_TRUTH["required_in_dockerfile"]:
|
|
if token not in content:
|
|
failures.append(f"Dockerfile missing required reference: {token}")
|
|
return failures
|
|
|
|
|
|
def check_py_deps() -> list[str]:
|
|
failures = []
|
|
dockerfile = REPO_ROOT / "Dockerfile"
|
|
if not dockerfile.exists():
|
|
return failures
|
|
content = dockerfile.read_text()
|
|
for dep in CANONICAL_TRUTH["required_py_deps"]:
|
|
if dep not in content:
|
|
failures.append(f"Dockerfile missing Python dependency: {dep}")
|
|
return failures
|
|
|
|
|
|
def main() -> int:
|
|
failures = []
|
|
failures.extend(check_required_paths())
|
|
failures.extend(check_forbidden_paths())
|
|
failures.extend(check_dockerfile())
|
|
failures.extend(check_py_deps())
|
|
|
|
report = {
|
|
"canonical_truth": CANONICAL_TRUTH["description"],
|
|
"repo_root": str(REPO_ROOT),
|
|
"status": "PASS" if not failures else "FAIL",
|
|
"failures": failures,
|
|
}
|
|
|
|
print(json.dumps(report, indent=2))
|
|
return 0 if not failures else 1
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|