#!/usr/bin/env python3 """Fail-closed validation for a multiplayer promotion evidence report.""" import json import sys from typing import Any STAGES = ("development", "internal", "casual-canary", "casual", "provisional-ranked", "ranked") def _required_true(report: dict[str, Any], key: str) -> None: value: Any = report for part in key.split("."): if not isinstance(value, dict) or part not in value: raise ValueError(f"missing gate: {key}") value = value[part] if value is not True: raise ValueError(f"gate did not pass: {key}") def validate_release_report(report: dict[str, Any]) -> tuple[str, str]: if not isinstance(report, dict): raise ValueError("release report must be an object") source = report.get("from_stage") target = report.get("to_stage") if source not in STAGES or target not in STAGES: raise ValueError("from_stage and to_stage must be known release stages") if STAGES.index(target) != STAGES.index(source) + 1: raise ValueError(f"promotion must advance exactly one stage: {source!r} -> {target!r}") if not isinstance(report.get("release_id"), str) or not report["release_id"].strip(): raise ValueError("release_id is required") for gate in ( "slo_passed", "security_passed", "cost_passed", "rollback_rehearsed", "playtests.eu_passed", "playtests.na_passed", "legacy.phase6_passed", "legacy.enet_passed", ): _required_true(report, gate) return source, target def main() -> int: if len(sys.argv) != 2: print(f"usage: {sys.argv[0]} report.json", file=sys.stderr) return 2 try: with open(sys.argv[1], encoding="utf-8") as handle: source, target = validate_release_report(json.load(handle)) except (OSError, ValueError, json.JSONDecodeError) as error: print(f"release gate failed: {error}", file=sys.stderr) return 1 print(f"release gate passed: {source} -> {target}") return 0 if __name__ == "__main__": raise SystemExit(main())