"""Download one pinned immich-go release and verify it before unpacking (US08-02). Run at image build time by the `uploader` stage, with the interpreter that is already in the base image: no curl, no wget, and no download tooling in the layer that ships. The checksum is not advisory — a release asset that does not match the pinned digest is a failed build, not a warning, because the uploader's flags and report format are what the upload parser is written against (concept §15). """ from __future__ import annotations import argparse import hashlib import platform import tarfile import tempfile import urllib.request from pathlib import Path RELEASE_URL = "https://github.com/simulot/immich-go/releases/download/v{version}/{asset}" # Debian/BuildKit architecture as the interpreter sees it → release asset name. ASSETS = { "x86_64": ("immich-go_Linux_x86_64.tar.gz", "amd64"), "amd64": ("immich-go_Linux_x86_64.tar.gz", "amd64"), "aarch64": ("immich-go_Linux_arm64.tar.gz", "arm64"), "arm64": ("immich-go_Linux_arm64.tar.gz", "arm64"), } TIMEOUT_SECONDS = 300 def main() -> int: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--version", required=True, help="immich-go release, without the v") parser.add_argument("--sha256-amd64", required=True) parser.add_argument("--sha256-arm64", required=True) parser.add_argument("--into", default="/usr/local/bin") args = parser.parse_args() machine = platform.machine().lower() if machine not in ASSETS: raise SystemExit(f"unsupported architecture: {machine}") asset, arch = ASSETS[machine] expected = {"amd64": args.sha256_amd64, "arm64": args.sha256_arm64}[arch] url = RELEASE_URL.format(version=args.version, asset=asset) with urllib.request.urlopen(url, timeout=TIMEOUT_SECONDS) as response: # noqa: S310 payload = response.read() digest = hashlib.sha256(payload).hexdigest() if digest != expected: raise SystemExit(f"checksum mismatch for {url}: {digest} != {expected}") target = Path(args.into) target.mkdir(parents=True, exist_ok=True) with tempfile.TemporaryDirectory() as work: archive = Path(work) / asset archive.write_bytes(payload) with tarfile.open(archive) as tar: member = tar.getmember("immich-go") # Extract exactly the one file this pin is about, by name, so nothing # else in the archive can decide where it lands. extracted = tar.extractfile(member) if extracted is None: raise SystemExit("release archive contains no immich-go binary") binary = target / "immich-go" binary.write_bytes(extracted.read()) binary.chmod(0o755) print(f"immich-go {args.version} ({arch}) verified {digest}") return 0 if __name__ == "__main__": raise SystemExit(main())