Files
metin-release-cli/src/metin_release/result.py
Jan Nedbal e70fc300e2 cli: scaffold phase 1 asset release commands
Add metin-release CLI with argparse dispatcher, result envelope and
error hierarchy, and the full Phase 1 release subcommand set:

  release inspect         - scan source root
  release build-manifest  - wraps make-manifest.py
  release sign            - wraps sign-manifest.py, enforces mode 600
  release diff-remote     - HEAD each blob hash against a base URL
  release upload-blobs    - rsync release dir minus manifest
  release promote         - rsync manifest.json + signature
  release verify-public   - GET + Ed25519 verify, optional blob sampling
  release publish         - composite of the above with per-stage timings

Respects --json / --verbose / --quiet. Exit codes follow the plan
(1 validation, 2 remote, 3 integrity, 4 reserved for ERP).
2026-04-14 18:59:50 +02:00

54 lines
1.5 KiB
Python

"""Result envelope and JSON/human output writer."""
from __future__ import annotations
import json
import sys
from dataclasses import dataclass, field
from typing import Any
@dataclass
class Result:
"""Structured result for a CLI command."""
command: str
ok: bool = True
status: str = "ok"
version: str | None = None
data: dict[str, Any] = field(default_factory=dict)
error_code: str | None = None
error_message: str | None = None
def to_envelope(self) -> dict[str, Any]:
env: dict[str, Any] = {
"ok": self.ok,
"command": self.command,
}
if self.version is not None:
env["version"] = self.version
env["status"] = self.status
if self.ok:
for k, v in self.data.items():
env[k] = v
else:
env["error"] = {
"code": self.error_code or "error",
"message": self.error_message or "",
}
return env
def write_result(result: Result, *, json_mode: bool, human_summary: str | None = None) -> None:
"""Emit the result.
When json_mode is True: only JSON on stdout.
When json_mode is False: human summary on stderr, JSON on stdout.
"""
envelope = result.to_envelope()
if not json_mode and human_summary:
print(human_summary, file=sys.stderr)
json.dump(envelope, sys.stdout, indent=2, sort_keys=False)
sys.stdout.write("\n")
sys.stdout.flush()