Files
blog/delivery/scripts/fetch_hugo.py
T

64 lines
1.9 KiB
Python

#!/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())