Files
aios/scripts/autonomous-release/build_artifact.py
T

64 lines
2.0 KiB
Python

#!/usr/bin/env python3
"""Build deterministic dormant-release candidate artifacts."""
from __future__ import annotations
import argparse
import hashlib
import json
from pathlib import Path
import tarfile
def sha256(value: bytes) -> str:
return hashlib.sha256(value).hexdigest()
def build(source: Path, output: Path, source_oid: str, tree_oid: str) -> dict[str, str]:
if not source.exists() or not source.is_dir():
raise SystemExit("source directory missing")
files = []
for item in sorted(source.rglob("*")):
if item.is_file() and ".git" not in item.parts:
files.append(item)
artifact = output / "candidate.tar"
manifest = output / "manifest.json"
output.mkdir(parents=True, exist_ok=True)
with tarfile.open(artifact, "w", format=tarfile.PAX_FORMAT) as archive:
for item in files:
info = archive.gettarinfo(str(item), arcname=str(item.relative_to(source)))
info.mtime = 0
info.uid = 0
info.gid = 0
with item.open("rb") as handle:
archive.addfile(info, handle)
payload = {
"source_oid": source_oid,
"tree_oid": tree_oid,
"artifact_sha256": sha256(artifact.read_bytes()),
"files": {
str(item.relative_to(source)): sha256(item.read_bytes())
for item in files
},
}
manifest.write_text(json.dumps(payload, sort_keys=True, indent=2), encoding="utf-8")
return {
"artifact_sha256": payload["artifact_sha256"],
"manifest_sha256": sha256(manifest.read_bytes()),
}
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("source", type=Path)
parser.add_argument("output", type=Path)
parser.add_argument("source_oid")
parser.add_argument("tree_oid")
args = parser.parse_args()
print(json.dumps(build(args.source, args.output, args.source_oid, args.tree_oid)))
return 0
if __name__ == "__main__":
raise SystemExit(main())