diff --git a/scripts/browser-libs.lock.json b/scripts/browser-libs.lock.json index d72ce26..984c440 100644 --- a/scripts/browser-libs.lock.json +++ b/scripts/browser-libs.lock.json @@ -1,10 +1,10 @@ { "libraries": { "highlight": { - "integrity": "sha384-wjfDDhOPPdjtva8vWBhWeVprSpmxisEu5aYT3q1JyACqXpdKpo3PWZTMVq24MBix", + "integrity": "sha512-KvOKXODaiFmId9xaq3xc5xCL66wVLUuOngDbO9B/kewbFTqdGbn2nJxNhN3H5R1cgDTVj6R8vH0zgiNDEGjpDw==", "package": "@highlightjs/cdn-assets", "repository": "git://github.com/highlightjs/highlight.js.git", - "shasum": "", + "shasum": "19b1b064bdd3d02705a200e9fa6538d41a566a47", "tarball": "https://registry.npmjs.org/@highlightjs/cdn-assets/-/cdn-assets-11.12.0.tgz", "version": "11.12.0" }, @@ -18,4 +18,4 @@ } }, "schema": 1 -} \ No newline at end of file +} diff --git a/scripts/update_lib_hashes.py b/scripts/update_lib_hashes.py new file mode 100755 index 0000000..a372f3a --- /dev/null +++ b/scripts/update_lib_hashes.py @@ -0,0 +1,143 @@ +#!/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()) \ No newline at end of file