#!/usr/bin/env python3 import argparse import base64 import hashlib import json import os import sys import tempfile import urllib.request def download(url: str) -> bytes: req = urllib.request.Request( url, headers={ "User-Agent": "rustpad-browser-libs-hash-updater/1.0" }, ) with urllib.request.urlopen(req, timeout=120) as response: if response.status != 200: raise RuntimeError( f"HTTP {response.status} while downloading {url}" ) return response.read() def calculate_hashes(data: bytes) -> tuple[str, str]: sha1 = hashlib.sha1(data).hexdigest() sha512_digest = hashlib.sha512(data).digest() sha512_b64 = base64.b64encode(sha512_digest).decode("ascii") integrity = f"sha512-{sha512_b64}" return integrity, sha1 def atomic_save(path: str, data: dict) -> None: directory = os.path.dirname(os.path.abspath(path)) fd, tmp_path = tempfile.mkstemp( prefix=".browser-libs.", suffix=".json", dir=directory, ) try: with os.fdopen(fd, "w", encoding="utf-8") as f: json.dump( data, f, indent=2, ensure_ascii=False, ) f.write("\n") os.replace(tmp_path, path) except Exception: try: os.unlink(tmp_path) except FileNotFoundError: pass raise def main() -> int: parser = argparse.ArgumentParser( description="Update integrity and shasum in browser-libs.lock.json" ) parser.add_argument( "lockfile", nargs="?", default="scripts/browser-libs.lock.json", help="Path to browser-libs.lock.json", ) args = parser.parse_args() try: with open(args.lockfile, "r", encoding="utf-8") as f: lock = json.load(f) except Exception as exc: print(f"error: cannot read {args.lockfile}: {exc}", file=sys.stderr) return 1 libraries = lock.get("libraries") if not isinstance(libraries, dict): print("error: missing 'libraries' object", file=sys.stderr) return 1 changed = False for name, library in libraries.items(): url = library.get("tarball") if not url: print(f"{name}: no tarball URL, skipping") continue version = library.get("version", "unknown") print(f"{name}: downloading {version}") print(f" {url}") try: data = download(url) integrity, shasum = calculate_hashes(data) except Exception as exc: print(f"error: {name}: {exc}", file=sys.stderr) return 1 old_integrity = library.get("integrity", "") old_shasum = library.get("shasum", "") library["integrity"] = integrity library["shasum"] = shasum print(f" integrity: {integrity}") print(f" shasum: {shasum}") if old_integrity != integrity or old_shasum != shasum: changed = True if changed: try: atomic_save(args.lockfile, lock) except Exception as exc: print(f"error: cannot save lockfile: {exc}", file=sys.stderr) return 1 print(f"\nUpdated: {args.lockfile}") else: print("\nNo changes required.") return 0 if __name__ == "__main__": raise SystemExit(main())