#!/usr/bin/env python3 """Export the API schema without depending on operator configuration or network I/O.""" from __future__ import annotations import argparse import asyncio import json import os import tempfile from pathlib import Path from backup_tool.api.app import create_app from backup_tool.config import Settings def export_schema() -> dict[str, object]: """Build the FastAPI schema with disposable, valid settings.""" with tempfile.TemporaryDirectory(prefix="backup-tool-openapi-") as temporary: root = Path(temporary) key = root / "master.key" key.write_bytes(b"openapi-export-key-material-must-be-at-least-32-bytes") key.chmod(0o600) for name in ("data", "repositories", "sources", "restores"): (root / name).mkdir() settings = Settings( data_dir=root / "data", database_url=f"sqlite+aiosqlite:///{root / 'data' / 'metadata.db'}", repository_roots=(root / "repositories",), local_source_roots=(root / "sources",), restore_roots=(root / "restores",), master_key_file=key, ) app = create_app(settings) schema = app.openapi() # Engine construction is lazy, but dispose defensively if that changes. asyncio.run(app.state.engine.dispose()) return schema def main() -> int: parser = argparse.ArgumentParser(description="Export the deterministic v2 OpenAPI document.") parser.add_argument("--output", type=Path, default=Path("openapi/v2.json")) parser.add_argument("--check", type=Path, metavar="PATH", help="fail when PATH differs") arguments = parser.parse_args() output = arguments.check or arguments.output rendered = json.dumps(export_schema(), indent=2, sort_keys=True, ensure_ascii=False) + "\n" if arguments.check: if not output.is_file() or output.read_text(encoding="utf-8") != rendered: print(f"OpenAPI drift: regenerate {output}") return 1 print(f"OpenAPI is current: {output}") return 0 output.parent.mkdir(parents=True, exist_ok=True) output.write_text(rendered, encoding="utf-8") # The schema is public source, not executable configuration. os.chmod(output, 0o644) print(f"Exported OpenAPI: {output}") return 0 if __name__ == "__main__": raise SystemExit(main())