73 lines
2.8 KiB
Python
73 lines
2.8 KiB
Python
"""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())
|