""" B0.3.3 — export the API contract. Writes openapi.json at the repository root. It is committed, and CI fails when the exported document differs from the committed one, so a change to a request or response shape has to be admitted in a diff rather than discovered by the frontend at runtime. APP_ENV=rework_test python scripts/export_openapi.py # write APP_ENV=rework_test python scripts/export_openapi.py --check # verify The contract is exported now, at 31% `response_model` coverage, rather than after B0.3.1/B0.3.2 fill the remaining 166. An incomplete contract that cannot silently change is worth considerably more than a complete one that arrives in three weeks, and every endpoint that gains a `response_model` shows up as a diff here — which makes progress visible instead of merely claimed. """ import json import os import sys from pathlib import Path os.environ.setdefault("APP_ENV", "rework_test") os.environ.setdefault("PYTHONIOENCODING", "utf-8") sys.path.insert(0, str(Path(__file__).parent.parent)) OUTPUT = Path(__file__).parent.parent / "openapi.json" def build() -> str: from app.main import app schema = app.openapi() # sort_keys so the file is diffable: FastAPI does not guarantee key order, # and an unordered dump would churn on every export. return json.dumps(schema, indent=2, sort_keys=True, ensure_ascii=False) + "\n" def main() -> int: generated = build() check = "--check" in sys.argv if check: if not OUTPUT.exists(): print("openapi.json is missing. Run scripts/export_openapi.py", file=sys.stderr) return 1 committed = OUTPUT.read_text(encoding="utf-8") if committed != generated: print( "openapi.json is stale — the API has changed since it was exported.\n" "Regenerate and commit the diff:\n" " APP_ENV=rework_test python scripts/export_openapi.py", file=sys.stderr, ) return 1 print("openapi.json is current") return 0 OUTPUT.write_text(generated, encoding="utf-8") schema = json.loads(generated) print(f"wrote {OUTPUT.name}") print(f" paths {len(schema.get('paths', {}))}") print(f" schemas {len(schema.get('components', {}).get('schemas', {}))}") return 0 if __name__ == "__main__": raise SystemExit(main())