From b4320303d3e45c94a6dac7fcd0868feef90389ff Mon Sep 17 00:00:00 2001 From: The_miro Date: Wed, 5 Aug 2026 23:02:46 +0200 Subject: [PATCH] Make deploy.sh safely re-runnable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Re-running previously died at step 2, because packwiz-setup.sh refused to overwrite an existing pack.toml — so a run that failed partway, or any later change to port or heap, could not be applied by simply running it again. deploy.sh now snapshots the dataset, stops the server if it was running, redeploys, and restarts it only if it had been up. packwiz-setup.sh gains --force, which moves an existing pack to .bak. rather than deleting it, since the directory may hold hand-edited .pw.toml files or git history. deploy.sh passes --force unless --keep-pack is given. World data and the server's mods directory are untouched throughout. Also replaces the hardcoded sed line ranges in every usage() with an awk that prints the leading comment block, so help text stops drifting out of sync when the header changes. Co-Authored-By: Claude Opus 5 --- README.md | 21 +++++++++++++++++ build.sh | 2 +- deploy.sh | 55 +++++++++++++++++++++++++++++++++++++++++---- mc-service-setup.sh | 2 +- packwiz-setup.sh | 18 ++++++++++++--- 5 files changed, 89 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index f16762b..e7d18a9 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,27 @@ Send the players `https:///setup-windows-packwiz.html` or `setup-linux-packwiz.html`. Those are generated with your real pack URL and server address already filled in. +## Re-running + +`deploy.sh` is safe to run repeatedly, and re-running is the intended way to +apply changed settings — a port change, a different heap size, or a run that +failed partway and left things half-applied. + +Each run: + +1. Snapshots the dataset (`@predeploy-`) +2. Stops the server if it was running +3. Rewrites `server.properties`, `user_jvm_args.txt` and both unit files +4. Moves any existing pack to `.bak.` and rebuilds it +5. Regenerates the player guides +6. Starts the server again if it had been running + +**The world and `/minecraft/mods` are never touched.** The pack is moved aside +rather than deleted, so hand-edited `.pw.toml` files and any git history in the +pack directory survive. + +`--keep-pack` skips the pack rebuild; `--no-snapshot` skips step 1. + ## What's in here | Script | Runs as | Does | diff --git a/build.sh b/build.sh index 31c778b..87d2a88 100755 --- a/build.sh +++ b/build.sh @@ -29,7 +29,7 @@ FILES=( die() { echo "error: $*" >&2; exit 1; } info() { echo ">>> $*"; } -usage() { sed -n '2,11p' "$0" | sed 's/^# \?//'; exit "${1:-0}"; } +usage() { awk 'NR>1 && /^#/ {sub(/^# ?/,""); print; next} NR>1 {exit}' "$0"; exit "${1:-0}"; } while [ $# -gt 0 ]; do case "$1" in diff --git a/deploy.sh b/deploy.sh index 3d3f04e..38a0f1c 100755 --- a/deploy.sh +++ b/deploy.sh @@ -9,17 +9,25 @@ # 1. mc-service-setup.sh (root) — minecraft user, NeoForge, systemd units # 2. packwiz-setup.sh (you) — pack, mod index from the mirror, guides # +# Safe to re-run. Each run snapshots the dataset, stops the server if it was +# running, rebuilds config and the pack from scratch, and starts it back up. +# The world and $SHARE/mods are never touched; an existing pack is moved aside +# to .bak. rather than deleted. +# +# --keep-pack keep the existing pack instead of rebuilding it +# --no-snapshot skip the pre-deploy ZFS snapshot +# # Run as your normal user; it calls sudo for the parts that need it. set -euo pipefail NAME=""; AUTHOR=""; BASEURL=""; NFVER="latest"; MCVER="1.21.1" -SHARE="/minecraft"; XMX="10G"; XMS="10G"; ACCEPT_EULA=""; PORT="25565" +SHARE="/minecraft"; XMX="10G"; XMS="10G"; ACCEPT_EULA=""; PORT="25565"; KEEP_PACK=0; SNAPSHOT=1 HERE="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" die() { echo "error: $*" >&2; exit 1; } step() { printf '\n\033[1m=== %s ===\033[0m\n\n' "$*"; } -usage() { sed -n '2,13p' "$0" | sed 's/^# \?//'; exit "${1:-0}"; } +usage() { awk 'NR>1 && /^#/ {sub(/^# ?/,""); print; next} NR>1 {exit}' "$0"; exit "${1:-0}"; } while [ $# -gt 0 ]; do case "$1" in @@ -33,6 +41,8 @@ while [ $# -gt 0 ]; do -X|--xmx) XMX="${2:-}"; shift 2 ;; -x|--xms) XMS="${2:-}"; shift 2 ;; --accept-eula) ACCEPT_EULA="--accept-eula"; shift ;; + --keep-pack) KEEP_PACK=1; shift ;; + --no-snapshot) SNAPSHOT=0; shift ;; -h|--help) usage 0 ;; *) echo "unknown option: $1" >&2; usage 1 ;; esac @@ -50,20 +60,57 @@ done # Fail early rather than halfway through, since step 1 makes system changes. sudo -v || die "sudo is required" +# ------------------------------------------------------------ re-run prep --- + +# Re-running is expected: a run that failed partway leaves a half-applied state, +# and later runs change settings that only take effect on a fresh start. So take +# a snapshot, stop the server, redeploy, and put it back how it was. +# +# World data and $SHARE/mods are never touched by any of this. + +WAS_ACTIVE=0 +if systemctl is-active --quiet minecraft.service 2>/dev/null; then + WAS_ACTIVE=1 +fi + +if (( SNAPSHOT )) && mountpoint -q "$SHARE" && command -v zfs >/dev/null; then + DATASET="$(findmnt -no SOURCE "$SHARE" 2>/dev/null || true)" + if [ -n "$DATASET" ] && [ "$(findmnt -no FSTYPE "$SHARE")" = zfs ]; then + SNAP="$DATASET@predeploy-$(date +%Y%m%d%H%M%S)" + step "Snapshotting $SNAP" + sudo zfs snapshot "$SNAP" \ + && echo "roll back with: sudo zfs rollback $SNAP" \ + || die "zfs snapshot failed — pass --no-snapshot to deploy anyway" + fi +fi + +if (( WAS_ACTIVE )); then + step "Stopping minecraft.service for redeploy" + sudo systemctl stop minecraft.service +fi + step "1/2 Minecraft systemd service" sudo "$HERE/mc-service-setup.sh" \ -s "$SHARE" -A "$USER" -X "$XMX" -x "$XMS" -m "$MCVER" -p "$PORT" \ ${NFVER:+-N "$NFVER"} ${ACCEPT_EULA} +PACK_FORCE="" +(( KEEP_PACK )) || PACK_FORCE="--force" + step "2/2 packwiz pack and player guides" # The admin group membership from step 1 isn't active in this shell yet, so run # the pack setup under the new group explicitly rather than telling you to log # out and back in. if id -nG "$USER" | tr ' ' '\n' | grep -qx minecraft; then - "$HERE/packwiz-setup.sh" -n "$NAME" -a "$AUTHOR" -m "$MCVER" -u "$BASEURL" -s "$SHARE" -p "$PORT" + "$HERE/packwiz-setup.sh" -n "$NAME" -a "$AUTHOR" -m "$MCVER" -u "$BASEURL" -s "$SHARE" -p "$PORT" ${PACK_FORCE} else sg minecraft -c "$(printf '%q ' "$HERE/packwiz-setup.sh" -n "$NAME" -a "$AUTHOR" \ - -m "$MCVER" -u "$BASEURL" -s "$SHARE" -p "$PORT")" + -m "$MCVER" -u "$BASEURL" -s "$SHARE" -p "$PORT" ${PACK_FORCE})" +fi + +if (( WAS_ACTIVE )); then + step "Restarting minecraft.service" + sudo systemctl start minecraft.service fi step "Done" diff --git a/mc-service-setup.sh b/mc-service-setup.sh index 955153e..dbe6acd 100755 --- a/mc-service-setup.sh +++ b/mc-service-setup.sh @@ -31,7 +31,7 @@ FIFO="/run/minecraft-console" die() { echo "error: $*" >&2; exit 1; } info() { echo ">>> $*"; } warn() { echo " warning: $*" >&2; } -usage() { sed -n '2,17p' "$0" | sed 's/^# \?//'; exit "${1:-0}"; } +usage() { awk 'NR>1 && /^#/ {sub(/^# ?/,""); print; next} NR>1 {exit}' "$0"; exit "${1:-0}"; } while [ $# -gt 0 ]; do case "$1" in diff --git a/packwiz-setup.sh b/packwiz-setup.sh index b28b6af..c87b12b 100755 --- a/packwiz-setup.sh +++ b/packwiz-setup.sh @@ -30,11 +30,12 @@ PACK_URL="" # defaults to $SITE_URL/packs/ SERVER_ADDR="" # defaults to the hostname of $SITE_URL MCPORT="25565" # Minecraft port, shown in the guides GUIDES=1 # write the player setup guides +FORCE=0 # move an existing pack aside and rebuild it die() { echo "error: $*" >&2; exit 1; } info() { echo ">>> $*"; } warn() { echo " warning: $*" >&2; } -usage() { sed -n '2,15p' "$0" | sed 's/^# \?//'; exit "${1:-0}"; } +usage() { awk 'NR>1 && /^#/ {sub(/^# ?/,""); print; next} NR>1 {exit}' "$0"; exit "${1:-0}"; } while [ $# -gt 0 ]; do case "$1" in @@ -54,6 +55,7 @@ while [ $# -gt 0 ]; do -A|--server) SERVER_ADDR="${2:-}"; shift 2 ;; -p|--port) MCPORT="${2:-}"; shift 2 ;; --no-guides) GUIDES=0; shift ;; + --force) FORCE=1; shift ;; -h|--help) usage 0 ;; *) echo "unknown option: $1" >&2; usage 1 ;; esac @@ -165,8 +167,18 @@ fi # ------------------------------------------------------------------ init ---- -[ -e "$DIR/pack.toml" ] && die "$DIR/pack.toml already exists — refusing to overwrite - (run 'packwiz init --reinit' there if that's what you want)" +if [ -e "$DIR/pack.toml" ]; then + if (( FORCE )); then + # Moved aside rather than deleted: the pack dir may hold hand-edited + # .pw.toml files or a git history, and a rebuild is not worth losing them. + bak="$DIR.bak.$(date +%Y%m%d%H%M%S)" + info "existing pack found — moving it to $bak" + mv "$DIR" "$bak" + else + die "$DIR/pack.toml already exists — refusing to overwrite + Pass --force to move it aside and rebuild, or 'packwiz init --reinit' there." + fi +fi mkdir -p "$DIR" cd "$DIR"