fix: patch Create Ore Excavation kubejs_create wrapper race + validate remote URLs
All checks were successful
Build & Release Pack / build (push) Successful in 1m50s
All checks were successful
Build & Release Pack / build (push) Successful in 1m50s
- COE 1.6.8 races kubejs-create for the ProcessingOutput rhino wrapper (guard flag set in @Mod ctor, KubeJS plugin callbacks run during its own construction) → nondeterministic 'Wrapper already exists!' boot crash. Ship ASM-patched jar (tools/patches/build_patched_coe.sh, Patcher.java). - validate_pack.py: new 'remote' command — fetches every direct URL and byte-verifies non-CDN hashes; catches unpushed/ drifted patched jars. - docs/server.md: stale-metadata failure row (-g is GUI-only), patched-jars table; docs/mods.md compat notes.
This commit is contained in:
parent
a88a0c2421
commit
be6fa51900
12 changed files with 220 additions and 13 deletions
|
|
@ -352,7 +352,7 @@ metafile = true
|
||||||
|
|
||||||
[[files]]
|
[[files]]
|
||||||
file = "mods/create-ore-excavation.pw.toml"
|
file = "mods/create-ore-excavation.pw.toml"
|
||||||
hash = "10d3389f72e8e8f78518f5b6786ccec4e62dbad117491d2970e8e1f75e390251"
|
hash = "d95f9869c9566617f24e06921a0809d94b87d4d9137aeb5b3f0f387ccb085441"
|
||||||
metafile = true
|
metafile = true
|
||||||
|
|
||||||
[[files]]
|
[[files]]
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
name = "Create Ore Excavation"
|
name = "Create Ore Excavation (patched - no kubejs_create race wrapper)"
|
||||||
filename = "createoreexcavation-1.21-1.6.8.jar"
|
filename = "createoreexcavation-1.21-1.6.8-patched.jar"
|
||||||
side = "both"
|
side = "both"
|
||||||
|
|
||||||
[download]
|
[download]
|
||||||
url = "https://cdn.modrinth.com/data/ResbpANg/versions/tivxiPTo/createoreexcavation-1.21-1.6.8.jar"
|
url = "https://git.mithraic.cloud/ad3laid3/SciCraft/raw/branch/main/tools/patches/createoreexcavation-1.21-1.6.8-patched.jar"
|
||||||
hash-format = "sha512"
|
hash-format = "sha512"
|
||||||
hash = "844ae32ff0d22f4d3c83db3bae2f6edc0f74cd1a530f900bf18829838d5a354b1e439e52688416130d9fd9a52c3310458bbdbc7458f4c1c1d8ffb088ae802d32"
|
hash = "0d2ca0631318639e46b5d6faf61c4905a385af29079e30780941ea14f9eaf85132acac7cdc1c42b15c7a46930aa69b33db4a80a981172af1293c4d1f8b4401b4"
|
||||||
|
|
||||||
[update]
|
[update]
|
||||||
[update.modrinth]
|
[update.modrinth]
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ pack-format = "packwiz:1.1.0"
|
||||||
[index]
|
[index]
|
||||||
file = "index.toml"
|
file = "index.toml"
|
||||||
hash-format = "sha256"
|
hash-format = "sha256"
|
||||||
hash = "f854fe332229eab4108b858886c9773f021f831ce78b4d7c74193bc9ddb65d2c"
|
hash = "e00761ce338bdd5fe09338b04cb68660f8ccbe6d3c646dda8ea96e751f4d5b5a"
|
||||||
|
|
||||||
[versions]
|
[versions]
|
||||||
minecraft = "1.21.1"
|
minecraft = "1.21.1"
|
||||||
|
|
|
||||||
|
|
@ -89,6 +89,14 @@ config needed there.
|
||||||
- **The Void Dimension 0.0.1** hard-requires **World Portal** (and Dimension
|
- **The Void Dimension 0.0.1** hard-requires **World Portal** (and Dimension
|
||||||
Fixer). World Portal (`side = "server"`) is bundled in both packs so the
|
Fixer). World Portal (`side = "server"`) is bundled in both packs so the
|
||||||
dimension boots and is reachable in SP and MP.
|
dimension boots and is reachable in SP and MP.
|
||||||
|
- **World Portal ships patched** (EntityLMixin stripped — it hard-conflicts
|
||||||
|
with Sable's entity mixins on dedicated servers). See `tools/patches/` and
|
||||||
|
the "Patched jars" table in `docs/server.md`.
|
||||||
|
- **Create Ore Excavation ships patched**: its KubeJS plugin races
|
||||||
|
kubejs-create to register a `ProcessingOutput` type-wrapper (guard flag is
|
||||||
|
set in the @Mod constructor while KubeJS runs plugin callbacks during its own
|
||||||
|
construction → nondeterministic boot crash). The patch removes COE's guarded
|
||||||
|
registration; kubejs-create remains the wrapper provider.
|
||||||
- **Create addon libs** (all present): RPL (Big Cannons), Kotlin for Forge
|
- **Create addon libs** (all present): RPL (Big Cannons), Kotlin for Forge
|
||||||
(Slice & Dice, Jetpack), Mechanicals Lib (Mechanical Extruder), Architectury
|
(Slice & Dice, Jetpack), Mechanicals Lib (Mechanical Extruder), Architectury
|
||||||
(Goggles). Flywheel is bundled inside Create — do **not** add a standalone
|
(Goggles). Flywheel is bundled inside Create — do **not** add a standalone
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,24 @@ Notes:
|
||||||
| Crash on world gen (Deeper and Darker) | Ensure CF 659011 + its lithostitched data load; update both. |
|
| Crash on world gen (Deeper and Darker) | Ensure CF 659011 + its lithostitched data load; update both. |
|
||||||
| TPS lag from many AE2 cells | Check FTB chunk claims; lower `simulation-distance`. |
|
| TPS lag from many AE2 cells | Check FTB chunk claims; lower `simulation-distance`. |
|
||||||
| Immersive Portals won't load | Must be the Fabric jar added via `url add --force` (Modrinth blocks loader mismatch). |
|
| Immersive Portals won't load | Must be the Fabric jar added via `url add --force` (Modrinth blocks loader mismatch). |
|
||||||
|
| Server boots with OLD mod set after a pack update | `packwiz-installer -g` only disables the GUI — it does NOT refresh metadata. Freshness comes from which `pack.toml` it reads: if the panel cloned the repo, `git pull` (or wipe + re-clone) before restart; the start script prefers a **local** `server_pack/pack.toml` over the remote URL. Verify with `python3 tools/validate_pack.py server_pack remote`. |
|
||||||
|
| `EntityLMixin` / worldportal crash on boot | The pack ships a patched World Portal (`tools/patches/`); make sure `mods/worldportal-neoforge-0.0.10+1.21-patched.jar` is what actually landed (see stale-metadata row above). |
|
||||||
|
| KubeJS `Wrapper for class ...ProcessingOutput already exists!` | Pack ships a patched Create Ore Excavation (`tools/patches/build_patched_coe.sh`) that removes its duplicate rhino wrapper; upstream race between COE and kubejs-create mod-construction order. Verify the `-patched.jar` landed (see stale-metadata row above). |
|
||||||
|
|
||||||
|
### Patched jars in this pack
|
||||||
|
|
||||||
|
Two mods ship as locally patched jars under `tools/patches/`; both are
|
||||||
|
referenced by `mods/*.pw.toml` via raw git URLs, so **the jars must be pushed
|
||||||
|
for any fresh install to work**:
|
||||||
|
|
||||||
|
| Jar | Patch | Reproduce with |
|
||||||
|
|-----|-------|----------------|
|
||||||
|
| `worldportal-neoforge-0.0.10+1.21-patched.jar` | Removes `EntityLMixin` (conflicts with Sable mixins) | hand-patched; see commit a88a0c2 |
|
||||||
|
| `createoreexcavation-1.21-1.6.8-patched.jar` | Strips COE's guarded `ProcessingOutput` wrapper registration (race vs kubejs-create) | `bash tools/patches/build_patched_coe.sh` |
|
||||||
|
|
||||||
|
After changing either: update the hash in **both** packs'
|
||||||
|
`mods/<name>.pw.toml`, run `packwiz refresh`, then
|
||||||
|
`python3 tools/validate_pack.py <pack> remote`.
|
||||||
|
|
||||||
## Backups
|
## Backups
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -293,7 +293,7 @@ metafile = true
|
||||||
|
|
||||||
[[files]]
|
[[files]]
|
||||||
file = "mods/create-ore-excavation.pw.toml"
|
file = "mods/create-ore-excavation.pw.toml"
|
||||||
hash = "10d3389f72e8e8f78518f5b6786ccec4e62dbad117491d2970e8e1f75e390251"
|
hash = "d95f9869c9566617f24e06921a0809d94b87d4d9137aeb5b3f0f387ccb085441"
|
||||||
metafile = true
|
metafile = true
|
||||||
|
|
||||||
[[files]]
|
[[files]]
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
name = "Create Ore Excavation"
|
name = "Create Ore Excavation (patched - no kubejs_create race wrapper)"
|
||||||
filename = "createoreexcavation-1.21-1.6.8.jar"
|
filename = "createoreexcavation-1.21-1.6.8-patched.jar"
|
||||||
side = "both"
|
side = "both"
|
||||||
|
|
||||||
[download]
|
[download]
|
||||||
url = "https://cdn.modrinth.com/data/ResbpANg/versions/tivxiPTo/createoreexcavation-1.21-1.6.8.jar"
|
url = "https://git.mithraic.cloud/ad3laid3/SciCraft/raw/branch/main/tools/patches/createoreexcavation-1.21-1.6.8-patched.jar"
|
||||||
hash-format = "sha512"
|
hash-format = "sha512"
|
||||||
hash = "844ae32ff0d22f4d3c83db3bae2f6edc0f74cd1a530f900bf18829838d5a354b1e439e52688416130d9fd9a52c3310458bbdbc7458f4c1c1d8ffb088ae802d32"
|
hash = "0d2ca0631318639e46b5d6faf61c4905a385af29079e30780941ea14f9eaf85132acac7cdc1c42b15c7a46930aa69b33db4a80a981172af1293c4d1f8b4401b4"
|
||||||
|
|
||||||
[update]
|
[update]
|
||||||
[update.modrinth]
|
[update.modrinth]
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ pack-format = "packwiz:1.1.0"
|
||||||
[index]
|
[index]
|
||||||
file = "index.toml"
|
file = "index.toml"
|
||||||
hash-format = "sha256"
|
hash-format = "sha256"
|
||||||
hash = "458e176e4de0ac679ee808a312155a3376039ebb3b20f42c827270fd183e5f25"
|
hash = "e71c2f015f60a250c965cb6592bc1c5e00ef657b54e2cbe70afc2bbce62cec98"
|
||||||
|
|
||||||
[versions]
|
[versions]
|
||||||
minecraft = "1.21.1"
|
minecraft = "1.21.1"
|
||||||
|
|
|
||||||
78
tools/patches/Patcher.java
Normal file
78
tools/patches/Patcher.java
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
import org.objectweb.asm.ClassReader;
|
||||||
|
import org.objectweb.asm.ClassWriter;
|
||||||
|
import org.objectweb.asm.Opcodes;
|
||||||
|
import org.objectweb.asm.tree.AbstractInsnNode;
|
||||||
|
import org.objectweb.asm.tree.ClassNode;
|
||||||
|
import org.objectweb.asm.tree.FieldInsnNode;
|
||||||
|
import org.objectweb.asm.tree.JumpInsnNode;
|
||||||
|
import org.objectweb.asm.tree.LabelNode;
|
||||||
|
import org.objectweb.asm.tree.MethodNode;
|
||||||
|
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Strips Create Ore Excavation's guarded ProcessingOutput type-wrapper registration
|
||||||
|
* from KubeJSExcavation.registerTypeWrappers.
|
||||||
|
*
|
||||||
|
* Upstream guard: `if (!CreateOreExcavation.kubeJSCreate) register(ProcessingOutput, ...)`
|
||||||
|
* — but kubeJSCreate is assigned in the @Mod constructor while KubeJS invokes plugin
|
||||||
|
* callbacks during KubeJS's own construction. FML constructs independent mods on
|
||||||
|
* parallel workers, so the flag may still be false when the callback runs → duplicate
|
||||||
|
* wrapper vs kubejs-create → "Wrapper already exists!" crash (nondeterministic).
|
||||||
|
*
|
||||||
|
* This pack always ships kubejs-create, so COE never needs its own wrapper: remove
|
||||||
|
* the whole guarded block (getstatic/ifne/…/invokevirtual) up to its branch-target
|
||||||
|
* label. Later frames stay valid because operand stack at that label is unchanged.
|
||||||
|
*/
|
||||||
|
public class Patcher {
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
if (args.length != 2) {
|
||||||
|
System.err.println("usage: Patcher <KubeJSExcavation.class-in> <class-out>");
|
||||||
|
System.exit(2);
|
||||||
|
}
|
||||||
|
byte[] bytes = Files.readAllBytes(Paths.get(args[0]));
|
||||||
|
ClassNode cn = new ClassNode();
|
||||||
|
new ClassReader(bytes).accept(cn, 0);
|
||||||
|
|
||||||
|
boolean found = false;
|
||||||
|
for (MethodNode mn : cn.methods) {
|
||||||
|
if (!"registerTypeWrappers".equals(mn.name)) continue;
|
||||||
|
found = true;
|
||||||
|
|
||||||
|
AbstractInsnNode start = null;
|
||||||
|
for (AbstractInsnNode n : mn.instructions) {
|
||||||
|
if (n instanceof FieldInsnNode f
|
||||||
|
&& n.getOpcode() == Opcodes.GETSTATIC
|
||||||
|
&& "kubeJSCreate".equals(f.name)) {
|
||||||
|
start = n;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (start == null) throw new IllegalStateException("kubeJSCreate guard not found");
|
||||||
|
|
||||||
|
AbstractInsnNode jump = start.getNext();
|
||||||
|
if (!(jump instanceof JumpInsnNode) || jump.getOpcode() != Opcodes.IFNE) {
|
||||||
|
throw new IllegalStateException("expected IFNE after getstatic");
|
||||||
|
}
|
||||||
|
LabelNode target = ((JumpInsnNode) jump).label;
|
||||||
|
|
||||||
|
int removed = 0;
|
||||||
|
AbstractInsnNode n = start;
|
||||||
|
while (n != target) {
|
||||||
|
AbstractInsnNode next = n.getNext();
|
||||||
|
mn.instructions.remove(n);
|
||||||
|
n = next;
|
||||||
|
removed++;
|
||||||
|
if (n == null) throw new IllegalStateException("branch target not reached");
|
||||||
|
}
|
||||||
|
System.out.println("patched " + cn.name + "." + mn.name
|
||||||
|
+ ": removed " + removed + " instructions up to branch target");
|
||||||
|
}
|
||||||
|
if (!found) throw new IllegalStateException("registerTypeWrappers not found");
|
||||||
|
|
||||||
|
ClassWriter cw = new ClassWriter(0);
|
||||||
|
cn.accept(cw);
|
||||||
|
Files.write(Paths.get(args[1]), cw.toByteArray());
|
||||||
|
}
|
||||||
|
}
|
||||||
49
tools/patches/build_patched_coe.sh
Normal file
49
tools/patches/build_patched_coe.sh
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# SCICRAFT — rebuild createoreexcavation with the kubejs-create wrapper race fixed.
|
||||||
|
#
|
||||||
|
# Why: COE 1.21.1-1.6.8 decides in KubeJSExcavation.registerTypeWrappers whether to
|
||||||
|
# register its own ProcessingOutput rhino wrapper, based on a flag set in the @Mod
|
||||||
|
# constructor. KubeJS runs plugin callbacks during ITS construction; FML constructs
|
||||||
|
# independent mods on parallel workers → race. If the callback wins, both COE and
|
||||||
|
# kubejs-create register ProcessingOutput → "Wrapper already exists!" boot crash.
|
||||||
|
#
|
||||||
|
# Fix: this pack always ships kubejs-create (scripts use event.recipes.create.*),
|
||||||
|
# so strip COE's guarded registration entirely via ASM (see Patcher.java).
|
||||||
|
#
|
||||||
|
# Usage: tools/patches/build_patched_coe.sh (from repo root)
|
||||||
|
set -euo pipefail
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
|
COE_URL="https://cdn.modrinth.com/data/ResbpANg/versions/tivxiPTo/createoreexcavation-1.21-1.6.8.jar"
|
||||||
|
OUT_JAR="createoreexcavation-1.21-1.6.8-patched.jar"
|
||||||
|
WORK="$(mktemp -d)"
|
||||||
|
trap 'rm -rf "$WORK"' EXIT
|
||||||
|
|
||||||
|
echo "[patch] downloading original..."
|
||||||
|
curl -fsSL -o "$WORK/coe.jar" "$COE_URL"
|
||||||
|
unzip -q "$WORK/coe.jar" "com/tom/createores/kubejs/KubeJSExcavation.class" -d "$WORK/x"
|
||||||
|
|
||||||
|
if [ ! -f asm.jar ]; then
|
||||||
|
echo "[patch] fetching ASM..."
|
||||||
|
curl -fsSL -o asm.jar https://repo1.maven.org/maven2/org/ow2/asm/asm/9.7.1/asm-9.7.1.jar
|
||||||
|
curl -fsSL -o asm-tree.jar https://repo1.maven.org/maven2/org/ow2/asm/asm-tree/9.7.1/asm-tree-9.7.1.jar
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[patch] compiling + running patcher..."
|
||||||
|
javac -cp asm.jar:asm-tree.jar Patcher.java
|
||||||
|
java -cp .:asm.jar:asm-tree.jar Patcher \
|
||||||
|
"$WORK/x/com/tom/createores/kubejs/KubeJSExcavation.class" \
|
||||||
|
"$WORK/KubeJSExcavation.class"
|
||||||
|
|
||||||
|
echo "[patch] rebuilding jar..."
|
||||||
|
cp "$WORK/coe.jar" "$OUT_JAR"
|
||||||
|
mkdir -p "$WORK/stage/com/tom/createores/kubejs"
|
||||||
|
cp "$WORK/KubeJSExcavation.class" "$WORK/stage/com/tom/createores/kubejs/"
|
||||||
|
(cd "$WORK/stage" && jar uf "$OLDPWD/$OUT_JAR" com)
|
||||||
|
|
||||||
|
echo "[patch] verifying: patched class inside jar differs, rest untouched..."
|
||||||
|
unzip -p "$OUT_JAR" com/tom/createores/kubejs/KubeJSExcavation.class > "$WORK/after.class"
|
||||||
|
cmp -s "$WORK/x/com/tom/createores/kubejs/KubeJSExcavation.class" "$WORK/after.class" \
|
||||||
|
&& { echo "[patch] ERROR: class unchanged"; exit 1; }
|
||||||
|
sha512sum "$OUT_JAR"
|
||||||
|
echo "[patch] done → $OUT_JAR"
|
||||||
BIN
tools/patches/createoreexcavation-1.21-1.6.8-patched.jar
Normal file
BIN
tools/patches/createoreexcavation-1.21-1.6.8-patched.jar
Normal file
Binary file not shown.
|
|
@ -6,15 +6,21 @@ Parses the limited, machine-generated TOML subset used by packwiz metadata.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import glob
|
import glob
|
||||||
|
import hashlib
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
import urllib.request
|
||||||
import zipfile
|
import zipfile
|
||||||
|
|
||||||
PW_TOML = re.compile(r"^(\w[\w.-]*)\s*=\s*(?:\"([^\"]*)\"|([0-9]+))", re.M)
|
PW_TOML = re.compile(r"^(\w[\w.-]*)\s*=\s*(?:\"([^\"]*)\"|([0-9]+))", re.M)
|
||||||
SECTION = re.compile(r"^\[\[?([\w.\-]+)\]\]?$", re.M)
|
SECTION = re.compile(r"^\[\[?([\w.\-]+)\]\]?$", re.M)
|
||||||
|
|
||||||
|
# Content-addressed CDNs are immutable; full-hash them opportunistically only.
|
||||||
|
# Everything else (git raw links, GitHub releases) can drift — verify bytes fully.
|
||||||
|
FULL_HASH_HOSTS = ()
|
||||||
|
|
||||||
|
|
||||||
def parse_toml(path):
|
def parse_toml(path):
|
||||||
"""Parse the minimal TOML subset packwiz emits: scalars + sections + array-of-tables."""
|
"""Parse the minimal TOML subset packwiz emits: scalars + sections + array-of-tables."""
|
||||||
|
|
@ -128,9 +134,55 @@ def check_cf(pack_dir, zip_path):
|
||||||
print("OK: CurseForge export complete (%d manifest files + %d overrides)" % (len(pids), len(ov)))
|
print("OK: CurseForge export complete (%d manifest files + %d overrides)" % (len(pids), len(ov)))
|
||||||
|
|
||||||
|
|
||||||
|
def _fetch(url, timeout=30):
|
||||||
|
req = urllib.request.Request(url, method="GET", headers={"User-Agent": "scicraft-validate"})
|
||||||
|
with urllib.request.urlopen(req, timeout=timeout) as resp:
|
||||||
|
return resp.read()
|
||||||
|
|
||||||
|
|
||||||
|
def check_remote(pack_dir):
|
||||||
|
"""Verify every direct-URL mod is reachable; fully hash-verify non-CDN URLs.
|
||||||
|
|
||||||
|
Catches the two deployment failure modes seen in practice:
|
||||||
|
1. patched jars whose raw git link 404s (never pushed / branch renamed)
|
||||||
|
2. remote drift: served bytes no longer match the index hash
|
||||||
|
"""
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
|
errors = 0
|
||||||
|
checked = hashed = 0
|
||||||
|
for f in sorted(glob.glob(os.path.join(pack_dir, "mods", "*.pw.toml"))):
|
||||||
|
d = parse_toml(f).get("download", {})
|
||||||
|
url = d.get("url")
|
||||||
|
if not url or d.get("mode", "").startswith("metadata:"):
|
||||||
|
continue
|
||||||
|
name = os.path.basename(f)
|
||||||
|
host = urlparse(url).netloc
|
||||||
|
try:
|
||||||
|
body = _fetch(url)
|
||||||
|
except Exception as e:
|
||||||
|
print("ERROR: %s unreachable (%s): %s" % (name, host, e))
|
||||||
|
errors += 1
|
||||||
|
continue
|
||||||
|
checked += 1
|
||||||
|
if host not in FULL_HASH_HOSTS:
|
||||||
|
digest = hashlib.sha512(body).hexdigest()
|
||||||
|
expected = d.get("hash", "")
|
||||||
|
if d.get("hash-format") == "sha512" and digest != expected:
|
||||||
|
print("ERROR: %s remote hash mismatch (%s)" % (name, host))
|
||||||
|
print(" expected %s" % expected[:32] + "...")
|
||||||
|
print(" remote %s..." % digest[:32])
|
||||||
|
errors += 1
|
||||||
|
else:
|
||||||
|
hashed += 1
|
||||||
|
if errors:
|
||||||
|
sys.exit(1)
|
||||||
|
print("OK: %d direct URLs reachable, %d byte-for-byte hash-verified" % (checked, hashed))
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
if len(sys.argv) < 2:
|
if len(sys.argv) < 2:
|
||||||
print("usage: validate_pack.py <pack_dir> [cf <zip>]", file=sys.stderr)
|
print("usage: validate_pack.py <pack_dir> [cf <zip>] [remote]", file=sys.stderr)
|
||||||
sys.exit(2)
|
sys.exit(2)
|
||||||
pack_dir = sys.argv[1]
|
pack_dir = sys.argv[1]
|
||||||
cmd = sys.argv[2] if len(sys.argv) > 2 else "integrity"
|
cmd = sys.argv[2] if len(sys.argv) > 2 else "integrity"
|
||||||
|
|
@ -138,6 +190,8 @@ def main():
|
||||||
check_cf(pack_dir, sys.argv[3])
|
check_cf(pack_dir, sys.argv[3])
|
||||||
elif cmd == "sanity":
|
elif cmd == "sanity":
|
||||||
check_sanity(pack_dir)
|
check_sanity(pack_dir)
|
||||||
|
elif cmd == "remote":
|
||||||
|
check_remote(pack_dir)
|
||||||
else:
|
else:
|
||||||
check_integrity(pack_dir)
|
check_integrity(pack_dir)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue