#!/usr/bin/env python3 """Install the pinned Hugo release after verifying its published SHA-256.""" from __future__ import annotations import argparse import hashlib import io from pathlib import Path import sys import tarfile import urllib.request VERSION = "0.150.1" URL = ( f"https://github.com/gohugoio/hugo/releases/download/v{VERSION}/" f"hugo_extended_{VERSION}_linux-amd64.tar.gz" ) SHA256 = "e1248fa077d99232794e38df5ec533494993aafafca1ae3e331a4ed629079ed6" MAX_BYTES = 128 * 1024 * 1024 class FetchError(RuntimeError): pass def extract_hugo(archive: bytes, expected: str, destination: Path) -> Path: actual = hashlib.sha256(archive).hexdigest() if actual != expected: raise FetchError(f"Hugo archive digest mismatch: {actual}") with tarfile.open(fileobj=io.BytesIO(archive), mode="r:gz") as tar: member = tar.getmember("hugo") if not member.isfile(): raise FetchError("Hugo archive member is not a regular file") source = tar.extractfile(member) if source is None: raise FetchError("cannot read the Hugo binary") destination.mkdir(parents=True, exist_ok=True) binary = destination / "hugo" binary.write_bytes(source.read()) binary.chmod(0o755) return binary def main() -> int: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--destination", type=Path, required=True) args = parser.parse_args() try: with urllib.request.urlopen(URL, timeout=60) as response: archive = response.read(MAX_BYTES + 1) if len(archive) > MAX_BYTES: raise FetchError("Hugo archive exceeds the size limit") print(extract_hugo(archive, SHA256, args.destination)) except (OSError, KeyError, tarfile.TarError, FetchError) as exc: print(f"error: {exc}", file=sys.stderr) return 1 return 0 if __name__ == "__main__": raise SystemExit(main())