feat(tooling): add-custom-mod.sh folder mode
Accept a directory: process every *.jar inside and run packwiz refresh once at the end. --name rejected with a folder (names derived per-jar); halts on an empty folder. Single-jar path unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,23 +1,26 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# add-custom-mod.sh — add a LOCAL jar to the packwiz pack as an "external file".
|
# add-custom-mod.sh — add LOCAL jar(s) to the packwiz pack as "external files".
|
||||||
#
|
#
|
||||||
# Implements the "Other external files" flow from
|
# Implements the "Other external files" flow from
|
||||||
# https://packwiz.infra.link/tutorials/creating/adding-mods/ : a hand-written
|
# https://packwiz.infra.link/tutorials/creating/adding-mods/ : a hand-written
|
||||||
# <slug>.pw.toml that points at a stable download URL + sha256, then refresh.
|
# <slug>.pw.toml that points at a stable download URL + sha256, then refresh.
|
||||||
#
|
#
|
||||||
# Since the jar is local (no public URL), we HOST it on the caddy pack server:
|
# Since the jars are local (no public URL), we HOST them on the caddy pack
|
||||||
# the jar is copied under pack/custom/ — caddy serves pack/ at
|
# server: each jar is copied under pack/custom/ — caddy serves pack/ at
|
||||||
# http://pack.${BASE_DOMAIN}/ — so the metadata's download url is
|
# http://pack.${BASE_DOMAIN}/ — so the metadata's download url is
|
||||||
# http://pack.${BASE_DOMAIN}/custom/<filename>
|
# http://pack.${BASE_DOMAIN}/custom/<filename>
|
||||||
# which is exactly what minecraft fetches via PACKWIZ_URL on install.
|
# which is exactly what minecraft fetches via PACKWIZ_URL on install.
|
||||||
#
|
#
|
||||||
# Usage:
|
# Usage:
|
||||||
# tooling/add-custom-mod.sh <path/to/mod.jar> [--name "Nice Name"]
|
# tooling/add-custom-mod.sh <mod.jar> [--name "Nice Name"] [--side S] [--force]
|
||||||
# [--side both|client|server] [--force]
|
# tooling/add-custom-mod.sh <dir/> [--side S] [--force] # all *.jar in dir
|
||||||
|
# # (--name not allowed)
|
||||||
|
# S = both|client|server (default both)
|
||||||
#
|
#
|
||||||
# Defaults: name = jar filename without .jar, side = both.
|
# Single-jar name defaults to the filename without .jar; folder mode derives
|
||||||
# Cautious: halts on missing tool/var, bad side, whitespace in filename, or an
|
# each name from its filename. packwiz refresh runs ONCE at the end.
|
||||||
# existing metadata/custom jar (unless --force).
|
# Cautious: halts on missing tool/var, bad side, whitespace in a filename, an
|
||||||
|
# empty folder, or an existing metadata/custom jar (unless --force).
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
cd "$(dirname "$0")/.." # repo root
|
cd "$(dirname "$0")/.." # repo root
|
||||||
@@ -33,24 +36,24 @@ slugify() { # lowercase, non-alnum -> hyphen, squeeze + trim hyphens
|
|||||||
}
|
}
|
||||||
|
|
||||||
# ---- args ------------------------------------------------------------------
|
# ---- args ------------------------------------------------------------------
|
||||||
JAR=""; NAME=""; SIDE="both"; FORCE=0
|
TARGET=""; NAME=""; SIDE="both"; FORCE=0
|
||||||
while [ $# -gt 0 ]; do
|
while [ $# -gt 0 ]; do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
--name) NAME="${2:?--name needs a value}"; shift 2 ;;
|
--name) NAME="${2:?--name needs a value}"; shift 2 ;;
|
||||||
--side) SIDE="${2:?--side needs a value}"; shift 2 ;;
|
--side) SIDE="${2:?--side needs a value}"; shift 2 ;;
|
||||||
--force) FORCE=1; shift ;;
|
--force) FORCE=1; shift ;;
|
||||||
-h|--help) sed -n '2,18p' "$0"; exit 0 ;;
|
-h|--help) sed -n '2,24p' "$0"; exit 0 ;;
|
||||||
-*) die "unknown flag: $1" ;;
|
-*) die "unknown flag: $1" ;;
|
||||||
*) [ -z "$JAR" ] || die "only one jar at a time (got extra: $1)"; JAR="$1"; shift ;;
|
*) [ -z "$TARGET" ] || die "only one jar or dir at a time (got extra: $1)"; TARGET="$1"; shift ;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
[ -n "$JAR" ] || die "no jar given. usage: tooling/add-custom-mod.sh <mod.jar> [--name N] [--side both|client|server]"
|
[ -n "$TARGET" ] || die "no jar/dir given. usage: tooling/add-custom-mod.sh <mod.jar|dir/> [--side both|client|server]"
|
||||||
[ -f "$JAR" ] || die "jar not found: $JAR"
|
[ -e "$TARGET" ] || die "path not found: $TARGET"
|
||||||
case "$SIDE" in both|client|server) ;; *) die "invalid --side '$SIDE' (both|client|server)" ;; esac
|
case "$SIDE" in both|client|server) ;; *) die "invalid --side '$SIDE' (both|client|server)" ;; esac
|
||||||
|
|
||||||
# ---- preflight -------------------------------------------------------------
|
# ---- preflight -------------------------------------------------------------
|
||||||
command -v packwiz >/dev/null 2>&1 || die "missing required tool: packwiz (https://packwiz.infra.link)"
|
command -v packwiz >/dev/null 2>&1 || die "missing required tool: packwiz (https://packwiz.infra.link)"
|
||||||
command -v sha256sum >/dev/null 2>&1 || die "missing required tool: sha256sum"
|
command -v sha256sum >/dev/null 2>&1 || die "missing required tool: sha256sum"
|
||||||
[ -f .env ] || die ".env not found (copy .env.example and fill it)"
|
[ -f .env ] || die ".env not found (copy .env.example and fill it)"
|
||||||
# shellcheck disable=SC1091
|
# shellcheck disable=SC1091
|
||||||
@@ -58,48 +61,62 @@ set -a; . ./.env; set +a
|
|||||||
: "${BASE_DOMAIN:?BASE_DOMAIN unset in .env}"
|
: "${BASE_DOMAIN:?BASE_DOMAIN unset in .env}"
|
||||||
[ -f pack/pack.toml ] || die "pack/pack.toml not found — run from a packwiz pack (see plan/04-packwiz.md)"
|
[ -f pack/pack.toml ] || die "pack/pack.toml not found — run from a packwiz pack (see plan/04-packwiz.md)"
|
||||||
|
|
||||||
FILENAME="$(basename -- "$JAR")"
|
# ---- collect jars ----------------------------------------------------------
|
||||||
case "$FILENAME" in *.jar) ;; *) die "not a .jar: $FILENAME" ;; esac
|
declare -a JARS=()
|
||||||
case "$FILENAME" in *[[:space:]]*) die "filename has whitespace: '$FILENAME' — rename it (URL-unsafe)" ;; esac
|
if [ -d "$TARGET" ]; then
|
||||||
|
[ -z "$NAME" ] || die "--name not allowed with a folder (names are derived per-jar)"
|
||||||
[ -n "$NAME" ] || NAME="${FILENAME%.jar}"
|
shopt -s nullglob
|
||||||
SLUG="$(slugify "$NAME")"
|
for f in "$TARGET"/*.jar; do JARS+=("$f"); done
|
||||||
[ -n "$SLUG" ] || die "could not derive a slug from name '$NAME'"
|
shopt -u nullglob
|
||||||
|
[ "${#JARS[@]}" -gt 0 ] || die "no .jar files in folder: $TARGET"
|
||||||
META="pack/mods/${SLUG}.pw.toml"
|
else
|
||||||
DEST="pack/custom/${FILENAME}"
|
JARS=("$TARGET")
|
||||||
URL="http://pack.${BASE_DOMAIN}/custom/${FILENAME}"
|
|
||||||
|
|
||||||
if [ "$FORCE" -ne 1 ]; then
|
|
||||||
[ -e "$META" ] && die "metadata exists: $META (use --force to overwrite)"
|
|
||||||
[ -e "$DEST" ] && die "hosted jar exists: $DEST (use --force to overwrite)"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ---- write -----------------------------------------------------------------
|
|
||||||
HASH="$(sha256sum -- "$JAR" | cut -d' ' -f1)"
|
|
||||||
|
|
||||||
mkdir -p pack/custom pack/mods
|
mkdir -p pack/custom pack/mods
|
||||||
cp -- "$JAR" "$DEST"
|
|
||||||
|
|
||||||
cat > "$META" <<EOF
|
# ---- per-jar processing (no refresh; refresh once at the end) --------------
|
||||||
name = "${NAME}"
|
add_one() { # $1 = jar path. Uses NAME (single-jar only), SIDE, FORCE, BASE_DOMAIN.
|
||||||
filename = "${FILENAME}"
|
local jar="$1" filename name slug meta dest url hash
|
||||||
|
[ -f "$jar" ] || die "not a file: $jar"
|
||||||
|
filename="$(basename -- "$jar")"
|
||||||
|
case "$filename" in *.jar) ;; *) die "not a .jar: $filename" ;; esac
|
||||||
|
case "$filename" in *[[:space:]]*) die "filename has whitespace: '$filename' — rename it (URL-unsafe)" ;; esac
|
||||||
|
|
||||||
|
name="$NAME"; [ -n "$name" ] || name="${filename%.jar}"
|
||||||
|
slug="$(slugify "$name")"
|
||||||
|
[ -n "$slug" ] || die "could not derive a slug from name '$name'"
|
||||||
|
|
||||||
|
meta="pack/mods/${slug}.pw.toml"
|
||||||
|
dest="pack/custom/${filename}"
|
||||||
|
url="http://pack.${BASE_DOMAIN}/custom/${filename}"
|
||||||
|
|
||||||
|
if [ "$FORCE" -ne 1 ]; then
|
||||||
|
[ -e "$meta" ] && die "metadata exists: $meta (use --force to overwrite)"
|
||||||
|
[ -e "$dest" ] && die "hosted jar exists: $dest (use --force to overwrite)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
hash="$(sha256sum -- "$jar" | cut -d' ' -f1)"
|
||||||
|
cp -- "$jar" "$dest"
|
||||||
|
cat > "$meta" <<EOF
|
||||||
|
name = "${name}"
|
||||||
|
filename = "${filename}"
|
||||||
side = "${SIDE}"
|
side = "${SIDE}"
|
||||||
|
|
||||||
[download]
|
[download]
|
||||||
url = "${URL}"
|
url = "${url}"
|
||||||
hash-format = "sha256"
|
hash-format = "sha256"
|
||||||
hash = "${HASH}"
|
hash = "${hash}"
|
||||||
EOF
|
EOF
|
||||||
|
echo " + ${filename} -> ${url} (${SIDE}, sha256 ${hash:0:12}…)"
|
||||||
|
}
|
||||||
|
|
||||||
log "wrote $META"
|
log "adding ${#JARS[@]} jar(s), side=${SIDE}"
|
||||||
echo " hosted jar : $DEST -> $URL"
|
for j in "${JARS[@]}"; do add_one "$j"; done
|
||||||
echo " side : $SIDE"
|
|
||||||
echo " sha256 : $HASH"
|
|
||||||
|
|
||||||
# ---- refresh ---------------------------------------------------------------
|
# ---- refresh once ----------------------------------------------------------
|
||||||
# packwiz refresh rebuilds index.toml (and pack.toml's index hash) so the new
|
# packwiz refresh rebuilds index.toml (and pack.toml's index hash) so the new
|
||||||
# .pw.toml is picked up. Must run inside the pack dir.
|
# .pw.toml files are picked up. Must run inside the pack dir.
|
||||||
( cd pack && packwiz refresh )
|
( cd pack && packwiz refresh )
|
||||||
log "packwiz refresh done — '${NAME}' added to the pack"
|
log "packwiz refresh done — ${#JARS[@]} mod(s) added to the pack"
|
||||||
echo " commit pack/custom/${FILENAME}, ${META}, pack/index.toml, pack/pack.toml"
|
echo " commit pack/custom/*.jar, pack/mods/*.pw.toml, pack/index.toml, pack/pack.toml"
|
||||||
|
|||||||
Reference in New Issue
Block a user