Files
linuxcncWebWASM/scripts/validate_publish_gate.py

65 lines
2.6 KiB
Python

#!/usr/bin/env python3
"""Validate the active selected-case publication gate."""
from __future__ import annotations
import argparse
import hashlib
import json
from pathlib import Path
from typing import Any
ROOT = Path(__file__).resolve().parents[1]
GATE = Path("coverage/release-authorized-gate.report.json")
EXPECTED_SCHEMA = "release-authorized-gate.v1"
EXPECTED_SCOPE = "selected_case_web_wasm_simulation"
def canonical(value: Any) -> str:
return json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":"))
def validate(root: Path, gate_path: Path = GATE) -> dict[str, Any]:
root = root.resolve()
path = gate_path if gate_path.is_absolute() else root / gate_path
report = json.loads(path.read_text(encoding="utf-8"))
unsigned = {key: value for key, value in report.items() if key != "report_sha256"}
expected_hash = hashlib.sha256(canonical(unsigned).encode("utf-8")).hexdigest()
errors: list[str] = []
if report.get("schema") != EXPECTED_SCHEMA:
errors.append("schema_invalid")
if report.get("status") != "authorized":
errors.append("gate_not_authorized")
if report.get("implementation_status") != "verified":
errors.append("implementation_not_verified")
if report.get("release_scope") != EXPECTED_SCOPE:
errors.append("scope_invalid")
if report.get("candidate_eligible") is not True:
errors.append("candidate_not_eligible")
if report.get("release_action") != "ALLOW_RELEASE":
errors.append("release_action_not_allowed")
if report.get("remaining_blockers") != []:
errors.append("remaining_blockers_present")
if report.get("report_sha256") != expected_hash:
errors.append("report_hash_invalid")
if not all(item.get("hash_match") is True for item in report.get("evidence", [])):
errors.append("evidence_hash_invalid")
if report.get("risk_boundary", {}).get("technical_evidence_rewritten") is not False:
errors.append("technical_evidence_was_rewritten")
return {"status": "authorized" if not errors else "blocked", "release_action": "ALLOW_RELEASE" if not errors else "STOP_RELEASE", "scope": report.get("release_scope"), "errors": errors, "report_sha256": report.get("report_sha256")}
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--repo-root", type=Path, default=ROOT)
parser.add_argument("--gate", type=Path, default=GATE)
args = parser.parse_args()
result = validate(args.repo_root, args.gate)
print(json.dumps(result, ensure_ascii=False, sort_keys=True))
return 0 if result["status"] == "authorized" else 1
if __name__ == "__main__":
raise SystemExit(main())