88 lines
3.2 KiB
Python
88 lines
3.2 KiB
Python
"""Command-line interface for contract imports and inspection."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import asyncio
|
|
import json
|
|
from datetime import UTC, datetime
|
|
from pathlib import Path
|
|
|
|
from app.contracts.diff import (
|
|
compare_snapshots,
|
|
has_breaking_changes,
|
|
render_html,
|
|
render_json,
|
|
render_markdown,
|
|
render_text,
|
|
)
|
|
from app.contracts.importer import RemoteSourceImporter
|
|
from app.contracts.model import Snapshot
|
|
from app.contracts.normalize import normalize_snapshot
|
|
from app.contracts.source import ApiViewerParser, LocalFileImporter, SourceImporter
|
|
from app.contracts.store import RevisionStore
|
|
|
|
|
|
def parser() -> argparse.ArgumentParser:
|
|
root = argparse.ArgumentParser(prog="proxmox-api-contract")
|
|
root.add_argument("--store", type=Path, default=Path("contracts"))
|
|
commands = root.add_subparsers(dest="command", required=True)
|
|
import_command = commands.add_parser("import")
|
|
source = import_command.add_mutually_exclusive_group(required=True)
|
|
source.add_argument("--file", type=Path)
|
|
source.add_argument("--url")
|
|
import_command.add_argument("--version", required=True)
|
|
validate = commands.add_parser("validate")
|
|
validate.add_argument("file", type=Path)
|
|
commands.add_parser("list")
|
|
show = commands.add_parser("show")
|
|
show.add_argument("revision")
|
|
diff = commands.add_parser("diff")
|
|
diff.add_argument("before", type=Path)
|
|
diff.add_argument("after", type=Path)
|
|
diff.add_argument("--format", choices=("text", "json", "markdown", "html"), default="text")
|
|
return root
|
|
|
|
|
|
async def run(arguments: argparse.Namespace) -> int:
|
|
store = RevisionStore(arguments.store)
|
|
if arguments.command == "list":
|
|
for revision in store.list():
|
|
print(revision)
|
|
return 0
|
|
if arguments.command == "show":
|
|
print(json.dumps(store.manifest(arguments.revision).model_dump(mode="json"), indent=2))
|
|
return 0
|
|
if arguments.command == "diff":
|
|
before = Snapshot.model_validate_json(arguments.before.read_bytes())
|
|
after = Snapshot.model_validate_json(arguments.after.read_bytes())
|
|
changes = compare_snapshots(before, after)
|
|
renderers = {
|
|
"text": render_text,
|
|
"json": render_json,
|
|
"markdown": render_markdown,
|
|
"html": render_html,
|
|
}
|
|
print(renderers[arguments.format](changes))
|
|
return 1 if has_breaking_changes(changes) else 0
|
|
if arguments.command == "validate":
|
|
parsed = ApiViewerParser().parse(arguments.file.read_bytes())
|
|
print(json.dumps({"nodes": len(parsed.nodes), "warnings": len(parsed.warnings)}))
|
|
return 0
|
|
importer: SourceImporter
|
|
if arguments.file is not None:
|
|
importer = LocalFileImporter(arguments.file)
|
|
else:
|
|
importer = RemoteSourceImporter(arguments.url)
|
|
raw = await importer.load()
|
|
parsed = ApiViewerParser().parse(raw)
|
|
snapshot, manifest = normalize_snapshot(
|
|
parsed, raw=raw, source_version=arguments.version, retrieved_at=datetime.now(UTC)
|
|
)
|
|
print(store.save(raw, snapshot, manifest))
|
|
return 0
|
|
|
|
|
|
def main() -> None:
|
|
raise SystemExit(asyncio.run(run(parser().parse_args())))
|