49 lines
1.2 KiB
Python
49 lines
1.2 KiB
Python
|
|
"""Metadata-only audit trail for what ops-mason built.
|
||
|
|
|
||
|
|
Same invariant as ops-warden's own audit trail (`warden activity`):
|
||
|
|
object names and plan references only, never secret material.
|
||
|
|
"""
|
||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import json
|
||
|
|
from dataclasses import asdict, dataclass
|
||
|
|
from datetime import UTC, datetime
|
||
|
|
from pathlib import Path
|
||
|
|
|
||
|
|
_DEFAULT_LOG = Path(__file__).resolve().parents[2] / "audit" / "build-log.jsonl"
|
||
|
|
|
||
|
|
|
||
|
|
@dataclass
|
||
|
|
class BuildRecord:
|
||
|
|
plan_id: str
|
||
|
|
plan_path: str
|
||
|
|
approved_by: str
|
||
|
|
approved_at: str
|
||
|
|
objects: dict[str, str]
|
||
|
|
built_at: str
|
||
|
|
|
||
|
|
|
||
|
|
def record_build(
|
||
|
|
*,
|
||
|
|
plan_id: str,
|
||
|
|
plan_path: str,
|
||
|
|
approved_by: str,
|
||
|
|
approved_at: str,
|
||
|
|
objects: dict[str, str],
|
||
|
|
log_path: Path | None = None,
|
||
|
|
) -> BuildRecord:
|
||
|
|
record = BuildRecord(
|
||
|
|
plan_id=plan_id,
|
||
|
|
plan_path=plan_path,
|
||
|
|
approved_by=approved_by,
|
||
|
|
approved_at=approved_at,
|
||
|
|
objects=objects,
|
||
|
|
built_at=datetime.now(UTC).isoformat(),
|
||
|
|
)
|
||
|
|
path = log_path or _DEFAULT_LOG
|
||
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
||
|
|
with path.open("a") as fh:
|
||
|
|
fh.write(json.dumps(asdict(record)) + "\n")
|
||
|
|
return record
|