#!/usr/bin/env python3 """Minimal owner-report driver. Consumes one pending owner report, calls an external send command, and only moves it to sent/ after the send command succeeds. This is a deliberately small manual driver for debugging the owner-report chain. It does not watch directories, retry, or send anything by itself. """ from __future__ import annotations import argparse import json import os import subprocess from pathlib import Path from owner_report_consumer import OWNER_REPORT_ROOT, PENDING_DIR, parse_pending_report, resolve_input SENT_DIR = OWNER_REPORT_ROOT / "sent" def _build_send_env(payload: dict) -> dict[str, str]: env = os.environ.copy() env.update( { "OWNER_REPORT_JSON": json.dumps(payload, ensure_ascii=False), "OWNER_REPORT_ID": str(payload.get("report_id") or ""), "OWNER_REPORT_TEAM": str(payload.get("team") or ""), "OWNER_REPORT_SOURCE": str(payload.get("source") or ""), "OWNER_REPORT_KIND": str(payload.get("report_kind") or "checkpoint"), "OWNER_REPORT_CREATED_AT": str(payload.get("created_at") or ""), "OWNER_REPORT_MESSAGE": str(payload.get("message") or ""), "OWNER_REPORT_PATH": str(payload.get("path") or ""), } ) return env def _sent_path(src: Path) -> Path: SENT_DIR.mkdir(parents=True, exist_ok=True) return SENT_DIR / src.name def _finalize_successful_send(src: Path) -> dict[str, object]: dest = _sent_path(src) if src.exists(): src.rename(dest) return {"moved": True, "already_archived": False, "final_path": str(dest)} if dest.exists(): return {"moved": False, "already_archived": True, "final_path": str(dest)} raise FileNotFoundError( f"successful send completed but pending report disappeared before archiving: pending={src} sent={dest}" ) def main() -> int: ap = argparse.ArgumentParser(description="Send one pending owner report via external command") ap.add_argument("report", help="Pending report path, filename, or report_id") ap.add_argument( "--send-cmd", help="Shell command used to send the report. Can also come from OWNER_REPORT_SEND_CMD.", ) ap.add_argument("--dry-run", action="store_true", help="Print what would be sent and do not move files") args = ap.parse_args() src = resolve_input(args.report) payload = parse_pending_report(src) send_cmd = args.send_cmd or os.environ.get("OWNER_REPORT_SEND_CMD") if not send_cmd and not args.dry_run: raise SystemExit("missing send command: use --send-cmd or OWNER_REPORT_SEND_CMD") if args.dry_run: print(json.dumps({ "ok": True, "dry_run": True, "action": "would_send", "pending_path": str(src), "sent_path": str(_sent_path(src)), "payload": payload, "send_cmd": send_cmd, }, ensure_ascii=False, indent=2)) return 0 proc = subprocess.run( ["bash", "-lc", send_cmd], text=True, capture_output=True, env=_build_send_env(payload), ) result = { "ok": proc.returncode == 0, "dry_run": False, "pending_path": str(src), "sent_path": str(_sent_path(src)), "send_cmd": send_cmd, "exit_code": proc.returncode, "stdout": proc.stdout, "stderr": proc.stderr, "payload": payload, } if proc.returncode != 0: print(json.dumps(result, ensure_ascii=False, indent=2)) return proc.returncode result.update(_finalize_successful_send(src)) print(json.dumps(result, ensure_ascii=False, indent=2)) return 0 if __name__ == "__main__": raise SystemExit(main())