#!/usr/bin/env python3 """Assemble the exact multi-scope PRE_UPLOAD_PRIVACY receipt, failing incomplete.""" from __future__ import annotations import argparse import json import subprocess import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src")) from repro_control.hashing import atomic_write_json, compact_json_bytes, sha256_bytes, sha256_file from repro_control.privacy import SCANNER_VERSION, scan_paths def main() -> int: parser = argparse.ArgumentParser() parser.add_argument("--checkout", type=Path, required=True) parser.add_argument("--package-manifest", type=Path, required=True) parser.add_argument("--image-contract", type=Path, required=True) parser.add_argument("--intended-remote-map", type=Path, required=True) parser.add_argument("--output", type=Path, default=Path("PRE_UPLOAD_PRIVACY.json")) args = parser.parse_args() checkout = args.checkout.resolve() tracked = subprocess.run( ["git", "ls-files", "-z"], cwd=checkout, check=True, capture_output=True, ).stdout.split(b"\0") tracked_paths = [checkout / raw.decode() for raw in tracked if raw] source = scan_paths( tracked_paths, relative_to=checkout, intended_remote_map=json.loads(args.intended_remote_map.read_text()), ) object_lines = subprocess.run( ["git", "rev-list", "--objects", "--all"], cwd=checkout, check=True, capture_output=True, text=True, ).stdout.splitlines() git_objects = sorted(line.split()[0] for line in object_lines if line) trackio = checkout / ".trackio" trackio_scan = ( scan_paths([trackio], relative_to=checkout, intended_remote_map={}) if trackio.is_dir() else None ) image = json.loads(args.image_contract.read_text()) package_manifest = json.loads(args.package_manifest.read_text()) blockers = [] if trackio_scan is None: blockers.append(".trackio scaffold does not exist") if not image.get("resolved_and_read_back"): blockers.append("immutable image config/history/layers are unresolved") if source["findings"]: blockers.append("source privacy findings exist") if trackio_scan and trackio_scan["findings"]: blockers.append(".trackio privacy findings exist") receipt = { "format": 1, "scanner_version": SCANNER_VERSION, "rules": source["rules"], "source": source, "git_object_ids": git_objects, "git_object_set_sha256": sha256_bytes(compact_json_bytes(git_objects)), "trackio": trackio_scan, "image_contract_sha256": sha256_file(args.image_contract), "image": image, "private_input_manifest_sha256": sha256_file(args.package_manifest), "private_input_payload_root_sha256": package_manifest["payload_root_sha256"], "intended_remote_map_sha256": sha256_file(args.intended_remote_map), "complete": not blockers, "blockers": blockers, "exit_code": 0 if not blockers else 2, } atomic_write_json(args.output, receipt) print(json.dumps({"complete": receipt["complete"], "blockers": blockers})) return receipt["exit_code"] if __name__ == "__main__": raise SystemExit(main())