"""Atomic private JSON receipt output shared by GCP parity helpers.""" from __future__ import annotations import hashlib import json import os import uuid from pathlib import Path from typing import Any def file_sha256(path: Path) -> str: digest = hashlib.sha256() with path.open("rb") as handle: for chunk in iter(lambda: handle.read(1024 * 1024), b""): digest.update(chunk) return digest.hexdigest() def write_private_json(path: Path, payload: dict[str, Any]) -> None: """Write one JSON receipt as mode 0600 without chmodding existing parents.""" parent_preexisting = path.parent.exists() path.parent.mkdir(parents=True, exist_ok=True, mode=0o700) if not path.parent.is_dir() or path.parent.is_symlink(): raise ValueError("output parent must be a real directory") if not parent_preexisting: os.chmod(path.parent, 0o700) temporary = path.parent / f".{path.name}.{uuid.uuid4().hex}.tmp" descriptor = os.open(temporary, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600) try: with os.fdopen(descriptor, "w", encoding="utf-8") as handle: json.dump(payload, handle, indent=2, sort_keys=True) handle.write("\n") handle.flush() os.fsync(handle.fileno()) os.replace(temporary, path) os.chmod(path, 0o600) finally: try: temporary.unlink() except FileNotFoundError: pass def private_receipt_summary(path: Path, payload: dict[str, Any]) -> dict[str, Any]: """Return a stdout-safe pointer without replaying private receipt contents.""" failed_checks = payload.get("failed_checks") problems = payload.get("problems") error = payload.get("error") return { "artifact": payload.get("artifact"), "status": payload.get("status"), "output_name": path.name, "output_sha256": file_sha256(path), "failed_check_count": len(failed_checks) if isinstance(failed_checks, list) else None, "problem_count": len(problems) if isinstance(problems, list) else None, "error_type": error.get("type") if isinstance(error, dict) else (type(error).__name__ if error else None), } def print_private_receipt_summary(path: Path, payload: dict[str, Any]) -> None: print(json.dumps(private_receipt_summary(path, payload), indent=2, sort_keys=True))