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"