#!/usr/bin/env python3 """Install or update a packwiz pack into a folder with packwiz-installer. packwiz-installer wants a URL, so the pack folder is served on 127.0.0.1 for the duration of the sync. It downloads Modrinth and CurseForge files from their own CDNs and removes files that left the pack since the last sync. Usage: tools/packwiz_sync.py [pack_dir] (pack_dir defaults to build/stage/full) """ import functools import http.server import shutil import subprocess import sys import threading import urllib.request from pathlib import Path ROOT = Path(__file__).resolve().parent.parent CACHE = ROOT / "build" / ".cache" # Pinned; the bootstrap runs with --bootstrap-no-update so neither self-updates. TOOLS = { "packwiz-installer-0.5.14.jar": "https://github.com/packwiz/packwiz-installer/releases/download/v0.5.14/packwiz-installer.jar", "packwiz-installer-bootstrap-0.0.3.jar": "https://github.com/packwiz/packwiz-installer-bootstrap/releases/download/v0.0.3/packwiz-installer-bootstrap.jar", } def cached(name): jar = CACHE / name if not jar.exists(): CACHE.mkdir(parents=True, exist_ok=True) with urllib.request.urlopen(TOOLS[name], timeout=120) as r, open(jar, "wb") as f: shutil.copyfileobj(r, f) return jar def java(): for candidate in (Path.home() / ".var/app/org.prismlauncher.PrismLauncher/data/PrismLauncher/java/java-runtime-delta/bin/java",): if candidate.exists(): return str(candidate) return shutil.which("java") class QuietHandler(http.server.SimpleHTTPRequestHandler): def log_message(self, *args): pass def sync(side, target, pack_dir): target.mkdir(parents=True, exist_ok=True) handler = functools.partial(QuietHandler, directory=str(pack_dir)) server = http.server.ThreadingHTTPServer(("127.0.0.1", 0), handler) threading.Thread(target=server.serve_forever, daemon=True).start() try: url = f"http://127.0.0.1:{server.server_port}/pack.toml" subprocess.run([java(), "-jar", str(cached("packwiz-installer-bootstrap-0.0.3.jar")), "--bootstrap-no-update", "--bootstrap-main-jar", str(cached("packwiz-installer-0.5.14.jar")), "-g", "-s", side, url], cwd=target, check=True) finally: server.shutdown() def main(): if len(sys.argv) < 3 or sys.argv[1] not in ("client", "server"): sys.exit(__doc__) pack_dir = Path(sys.argv[3]) if len(sys.argv) > 3 else ROOT / "build" / "stage" / "full" sync(sys.argv[1], Path(sys.argv[2]).resolve(), pack_dir.resolve()) if __name__ == "__main__": main()