#!/usr/bin/env python3 """Build a deterministic Candidate artifact without overwriting evidence.""" from __future__ import annotations import argparse import json import os from pathlib import Path from aios_autonomous_release_candidate.artifact import ArtifactBuilder def exclusive_write(path: Path, content: bytes, mode: int) -> None: if path.is_symlink(): raise RuntimeError("artifact output symlink rejected") flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL if hasattr(os, "O_NOFOLLOW"): flags |= os.O_NOFOLLOW descriptor = os.open(path, flags, mode) try: os.write(descriptor, content) os.fsync(descriptor) finally: os.close(descriptor) def build_parser() -> argparse.ArgumentParser: parser = argparse.ArgumentParser() parser.add_argument("--source", required=True, type=Path) parser.add_argument("--source-oid", required=True) parser.add_argument("--tree-oid", required=True) parser.add_argument("--output-directory", required=True, type=Path) parser.add_argument("--name", default="aios-autonomous-release-candidate") return parser def main(argv: list[str] | None = None) -> int: args = build_parser().parse_args(argv) output = args.output_directory output.mkdir(parents=True, exist_ok=True, mode=0o750) if output.is_symlink(): raise RuntimeError("artifact output directory symlink rejected") os.chmod(output, output.stat().st_mode & 0o750) builder = ArtifactBuilder() built = builder.build_from_directory(args.source_oid, args.tree_oid, args.source) builder.verify(built) artifact_path = output / f"{args.name}.tar" manifest_path = output / f"{args.name}.manifest.json" exclusive_write(artifact_path, built.content, 0o640) exclusive_write(manifest_path, built.manifest.as_json_bytes(), 0o640) print( json.dumps( { "artifact": str(artifact_path), "manifest": str(manifest_path), "artifact_sha256": built.manifest.artifact_sha256, "manifest_sha256": built.manifest.manifest_sha256, "package_closure_sha256": built.manifest.package_closure_sha256, "secrets_included": False, "production_side_effects": False, }, sort_keys=True, ) ) return 0 if __name__ == "__main__": raise SystemExit(main())