63 lines
2.3 KiB
Python
63 lines
2.3 KiB
Python
"""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))
|