Phase 9: the private pack to hand out

build/...-private.mrpack lists every mod by download link, CurseForge-only ones
included, with the credits and non-affiliation notice inside and no third-party jar.
tools/check_private_pack.py downloads all of it and checks the hashes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
claudeandClaude Opus 5.5 committed 2026-11-09 16:49:46 +01:00
1 parent 4083791944
commit 6ace26e012
8 files changed
+139 -6

No files matched your search

+38
View File
@@ -6,6 +6,7 @@ Outputs, all under build/:
instance and the server smoke test install this
<slug>-<ver>.mrpack Modrinth pack
<slug>-<ver>-curseforge.zip CurseForge pack
<slug>-<ver>-private.mrpack every mod, by download link: the pack to hand out
SHA256SUMS what the build produced
Reproducible: the same commit gives the same bytes, whatever the machine has
@@ -142,6 +143,38 @@ def normalise_export(archive):
z.writestr(info, files[name])
def write_private_export(modrinth_pack, out):
"""The pack Adrian hands out himself (2026-11-09: no public release): the Modrinth export plus the mods only
CurseForge hosts, by their CurseForge download links. Still no third-party jar inside. Prism installs any link;
Modrinth's own site and app only accept their listed hosts, which is why this is separate. The credits go in too."""
with zipfile.ZipFile(modrinth_pack) as z:
files = {i.filename: z.read(i.filename) for i in z.infolist() if not i.is_dir()}
index = json.loads(files["modrinth.index.json"])
listed = {Path(entry["path"]).name for entry in index["files"]}
for path in sorted((PACK / "mods").glob("*.pw.toml")):
meta = tomllib.loads(path.read_text())
if meta["filename"] in listed:
continue
jar = lock.fetch_mod(meta)
side = meta.get("side", "both")
index["files"].append({
"path": f"mods/{meta['filename']}",
"hashes": {"sha1": lock.digest(jar, "sha1"), "sha512": lock.digest(jar, "sha512")},
"env": {"client": "unsupported" if side == "server" else "required", "server": "unsupported" if side == "client" else "required"},
"downloads": [lock.download_url(meta)],
"fileSize": jar.stat().st_size,
})
index["files"].sort(key=lambda entry: entry["path"])
index["summary"] = "Unofficial fan-made modpack, not affiliated with or endorsed by Nintendo, Game Freak, Creatures Inc., " \
"The Pokémon Company, the Cobblemon team, Mojang Studios or Microsoft. Credits: CREDITS.md."
files["modrinth.index.json"] = (json.dumps(index, indent=1, ensure_ascii=False) + "\n").encode()
files["overrides/CREDITS.md"] = (ROOT / "docs/licenses.md").read_bytes()
with zipfile.ZipFile(out, "w", zipfile.ZIP_DEFLATED) as z:
for name, content in files.items():
z.writestr(name, content)
normalise_export(out)
def write_checksums(paths):
(BUILD / "SHA256SUMS").write_text("".join(f"{lock.digest(p)} {p.relative_to(ROOT).as_posix()}\n" for p in sorted(paths)))
@@ -176,6 +209,11 @@ def main():
check_overrides(out, jar)
report[out.name] = left_out
private = BUILD / f"{base}-private.mrpack"
write_private_export(BUILD / f"{base}.mrpack", private)
check_overrides(private, jar)
report[private.name] = []
write_checksums([jar, BUILD / "stage/full/pack.toml", BUILD / "stage/full/index.toml", *(BUILD / name for name in report)])
print("\nExports (no third-party jars in overrides):")
for name, left_out in report.items():