#!/usr/bin/env python3 """Check the private pack as a launcher would install it: every link really downloads the pinned file, every mod of the pack is listed exactly once with the right side, and no third-party jar sits inside. Downloads each file fresh, so it needs the network and takes a minute or two. Usage: tools/check_private_pack.py """ import hashlib import json import sys import tomllib import urllib.request import zipfile from pathlib import Path, PurePosixPath import lock ROOT = lock.ROOT PACK = ROOT / "pack" def main(): version = tomllib.loads((PACK / "pack.toml").read_text())["version"] archive = ROOT / f"build/cobblemon-survival-pack-{version}-private.mrpack" if not archive.exists(): sys.exit(f"{archive} is missing: run tools/build.py first") problems = [] with zipfile.ZipFile(archive) as z: names = z.namelist() index = json.loads(z.read("modrinth.index.json")) jars = [n for n in names if n.endswith(".jar")] if [PurePosixPath(n).name for n in jars] != ["cobblesurvival-0.0.1.jar"]: problems.append(f"jars inside the pack: {jars}") if "overrides/CREDITS.md" not in names: problems.append("no credits in the pack") entries = {PurePosixPath(e["path"]).name: e for e in index["files"]} if len(entries) != len(index["files"]): problems.append("a file is listed twice") metas = [tomllib.loads(p.read_text()) for p in sorted([*(PACK / "mods").glob("*.pw.toml"), *(PACK / "shaderpacks").glob("*.pw.toml")])] wanted = {m["filename"]: m for m in metas} if set(wanted) != set(entries): problems.append(f"missing {sorted(set(wanted) - set(entries))}, unexpected {sorted(set(entries) - set(wanted))}") total = 0 for name, entry in sorted(entries.items()): request = urllib.request.Request(entry["downloads"][0], headers={"User-Agent": "cobblemon-survival-pack check"}) try: with urllib.request.urlopen(request, timeout=300) as response: content = response.read() except OSError as e: problems.append(f"{name}: {e}") continue total += len(content) if hashlib.sha512(content).hexdigest() != entry["hashes"]["sha512"] or hashlib.sha1(content).hexdigest() != entry["hashes"]["sha1"]: problems.append(f"{name}: the download doesn't match its hash") if len(content) != entry["fileSize"]: problems.append(f"{name}: size {len(content)}, listed {entry['fileSize']}") side = wanted.get(name, {}).get("side", "both") expected = {"client": side != "server", "server": side != "client"} if {k: v == "required" for k, v in entry["env"].items()} != expected: problems.append(f"{name}: env {entry['env']} but side {side}") print(f"{len(entries)} files, {total / 1e6:.0f} MB downloaded and checked; {archive.name} is {archive.stat().st_size / 1e6:.1f} MB") if problems: print("\n".join(f" - {p}" for p in problems)) sys.exit("PRIVATE PACK CHECK FAILED") print("PRIVATE PACK CHECK PASSED") if __name__ == "__main__": main()