From 6ae95471452df8a77eefdb61e6990d84e37e4e35 Mon Sep 17 00:00:00 2001 From: Bezalel Date: Tue, 7 Apr 2026 14:23:56 +0000 Subject: [PATCH] fix(ci): repair JSON validation syntax, add repo-truth guard, copy robots.txt/index.html in Dockerfile --- .gitea/workflows/ci.yml | 11 ++-- Dockerfile | 2 + scripts/repo_truth_guard.py | 112 ++++++++++++++++++++++++++++++++++++ 3 files changed, 119 insertions(+), 6 deletions(-) create mode 100644 scripts/repo_truth_guard.py diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 97ebd3b..8350f6f 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -52,7 +52,7 @@ jobs: run: | FAIL=0 for f in $(find . -name '*.json' -not -path './venv/*'); do - if ! python3 -c "import json; json.load(open('$f'))"; then + if ! python3 -c "import json; json.load(open('$f'))" 2>/dev/null; then echo "FAIL: $f" FAIL=1 else @@ -60,11 +60,10 @@ jobs: fi done exit $FAIL - else - echo "OK: $f" - fi - done - exit $FAIL + + - name: Repo Truth Guard + run: | + python3 scripts/repo_truth_guard.py - name: Validate YAML run: | diff --git a/Dockerfile b/Dockerfile index a5807d3..a1d5002 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,6 +6,8 @@ WORKDIR /app COPY nexus/ nexus/ COPY server.py . COPY portals.json vision.json ./ +COPY robots.txt ./ +COPY index.html help.html ./ RUN pip install --no-cache-dir websockets diff --git a/scripts/repo_truth_guard.py b/scripts/repo_truth_guard.py new file mode 100644 index 0000000..55ea7f9 --- /dev/null +++ b/scripts/repo_truth_guard.py @@ -0,0 +1,112 @@ +#!/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())