Add install.sh, deploy.conf and ISO output
Deploying from removable media needed a single entry point that does not require remembering a long flag line at a console. install.sh reads deploy.conf, installs missing prerequisites with dnf, checks that the share is actually mounted, optionally caps the ZFS ARC, runs the deploy and can start the server. It copies itself off read-only media to the home directory first, since the deploy needs somewhere writable. --check validates the machine and changes nothing. ACCEPT_EULA must be set to yes in the config; the installer will not infer it. build.sh --iso produces a bootable-media image via xorrisofs/genisoimage/ mkisofs, carrying a ready-to-edit deploy.conf and a README.FIRST.txt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>main
parent
6c85e30cc8
commit
c36b54c7d3
28
README.md
28
README.md
|
|
@ -34,6 +34,34 @@ Send the players `https://<your-host>/setup-windows-packwiz.html` or
|
||||||
`setup-linux-packwiz.html`. Those are generated with your real pack URL and
|
`setup-linux-packwiz.html`. Those are generated with your real pack URL and
|
||||||
server address already filled in.
|
server address already filled in.
|
||||||
|
|
||||||
|
## Deploying from an ISO or USB stick
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./build.sh --iso # -> dist/mc-packwiz-deploy-<version>.iso
|
||||||
|
```
|
||||||
|
|
||||||
|
Burn or `dd` it, mount it on the target, then:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p ~/mc-packwiz-deploy
|
||||||
|
cp -r /run/media/*/mc-packwiz-deploy/. ~/mc-packwiz-deploy/
|
||||||
|
cd ~/mc-packwiz-deploy && chmod +x *.sh *.py
|
||||||
|
$EDITOR deploy.conf # set MIRROR_URL and ACCEPT_EULA at minimum
|
||||||
|
./install.sh --check # verify the machine is ready
|
||||||
|
./install.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
`install.sh` reads everything from `deploy.conf`, so there are no flags to type
|
||||||
|
at a console. It installs missing prerequisites with `dnf`, optionally caps the
|
||||||
|
ZFS ARC, runs the deploy, and can start the server. `--check` validates
|
||||||
|
prerequisites and the mount without changing anything.
|
||||||
|
|
||||||
|
The one thing it will not do is guess about the EULA: `ACCEPT_EULA` must be set
|
||||||
|
to `yes` in the config, or it stops and points at the licence.
|
||||||
|
|
||||||
|
It also refuses to run when the share is not a mountpoint, since deploying onto
|
||||||
|
an unmounted path would fill the root disk instead of the pool.
|
||||||
|
|
||||||
## Re-running
|
## Re-running
|
||||||
|
|
||||||
`deploy.sh` is safe to run repeatedly, and re-running is the intended way to
|
`deploy.sh` is safe to run repeatedly, and re-running is the intended way to
|
||||||
|
|
|
||||||
56
build.sh
56
build.sh
|
|
@ -3,6 +3,7 @@
|
||||||
# Build the release zip.
|
# Build the release zip.
|
||||||
#
|
#
|
||||||
# ./build.sh -> dist/mc-packwiz-deploy-<version>.zip
|
# ./build.sh -> dist/mc-packwiz-deploy-<version>.zip
|
||||||
|
# ./build.sh --iso -> also dist/mc-packwiz-deploy-<version>.iso
|
||||||
# ./build.sh -o /tmp -> writes there instead
|
# ./build.sh -o /tmp -> writes there instead
|
||||||
# ./build.sh -v 1.2.0 -> override the version
|
# ./build.sh -v 1.2.0 -> override the version
|
||||||
#
|
#
|
||||||
|
|
@ -15,12 +16,15 @@ set -euo pipefail
|
||||||
VERSION="0.1.0"
|
VERSION="0.1.0"
|
||||||
NAME="mc-packwiz-deploy"
|
NAME="mc-packwiz-deploy"
|
||||||
OUTDIR=""
|
OUTDIR=""
|
||||||
|
MAKE_ISO=0
|
||||||
HERE="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
HERE="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
|
||||||
# Everything the zip ships. Anything not listed here stays out, so stray notes
|
# Everything the zip ships. Anything not listed here stays out, so stray notes
|
||||||
# or test output in the working tree can't leak into a release.
|
# or test output in the working tree can't leak into a release.
|
||||||
FILES=(
|
FILES=(
|
||||||
README.md
|
README.md
|
||||||
|
install.sh
|
||||||
|
deploy.conf.example
|
||||||
deploy.sh
|
deploy.sh
|
||||||
mc-service-setup.sh
|
mc-service-setup.sh
|
||||||
packwiz-setup.sh
|
packwiz-setup.sh
|
||||||
|
|
@ -35,6 +39,7 @@ usage() { awk 'NR>1 && /^#/ {sub(/^# ?/,""); print; next} NR>1 {exit}' "$0"; exi
|
||||||
while [ $# -gt 0 ]; do
|
while [ $# -gt 0 ]; do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
-o|--outdir) OUTDIR="${2:-}"; shift 2 ;;
|
-o|--outdir) OUTDIR="${2:-}"; shift 2 ;;
|
||||||
|
--iso) MAKE_ISO=1; shift ;;
|
||||||
-v|--version) VERSION="${2:-}"; VERSION_FORCED=1; shift 2 ;;
|
-v|--version) VERSION="${2:-}"; VERSION_FORCED=1; shift 2 ;;
|
||||||
-h|--help) usage 0 ;;
|
-h|--help) usage 0 ;;
|
||||||
*) echo "unknown option: $1" >&2; usage 1 ;;
|
*) echo "unknown option: $1" >&2; usage 1 ;;
|
||||||
|
|
@ -104,3 +109,54 @@ unzip -l "$ZIP" | sed 's/^/ /'
|
||||||
if command -v sha256sum >/dev/null; then
|
if command -v sha256sum >/dev/null; then
|
||||||
sha256sum "$ZIP" | tee "$ZIP.sha256" | sed 's/^/ /'
|
sha256sum "$ZIP" | tee "$ZIP.sha256" | sed 's/^/ /'
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------- iso ---
|
||||||
|
|
||||||
|
if (( MAKE_ISO )); then
|
||||||
|
ISO="$OUTDIR/${NAME}-${VERSION}.iso"
|
||||||
|
|
||||||
|
mkisofs=""
|
||||||
|
for c in xorrisofs genisoimage mkisofs; do
|
||||||
|
command -v "$c" >/dev/null && { mkisofs="$c"; break; }
|
||||||
|
done
|
||||||
|
[ -n "$mkisofs" ] || die "no ISO tool found — sudo dnf install xorriso"
|
||||||
|
|
||||||
|
# A ready-to-edit config goes on the media so the target host only needs
|
||||||
|
# the file opened and one command run.
|
||||||
|
cp "$HERE/deploy.conf.example" "$DEST/deploy.conf"
|
||||||
|
|
||||||
|
cat > "$DEST/README.FIRST.txt" <<'TXT'
|
||||||
|
Minecraft + packwiz hosting kit
|
||||||
|
===============================
|
||||||
|
|
||||||
|
The media is read-only, so install.sh copies itself to your home directory
|
||||||
|
before doing anything.
|
||||||
|
|
||||||
|
1. Mount this media, then copy the kit somewhere writable:
|
||||||
|
|
||||||
|
mkdir -p ~/mc-packwiz-deploy
|
||||||
|
cp -r /run/media/*/mc-packwiz-deploy/. ~/mc-packwiz-deploy/
|
||||||
|
cd ~/mc-packwiz-deploy && chmod +x *.sh *.py
|
||||||
|
|
||||||
|
2. Edit deploy.conf. At minimum set MIRROR_URL and ACCEPT_EULA.
|
||||||
|
|
||||||
|
3. Check the machine is ready:
|
||||||
|
|
||||||
|
./install.sh --check
|
||||||
|
|
||||||
|
4. Install:
|
||||||
|
|
||||||
|
./install.sh
|
||||||
|
|
||||||
|
Requires a ZFS dataset already mounted at the SHARE path in deploy.conf.
|
||||||
|
install.sh refuses to run otherwise, rather than filling the root disk.
|
||||||
|
TXT
|
||||||
|
|
||||||
|
rm -f "$ISO"
|
||||||
|
"$mkisofs" -quiet -o "$ISO" -V "MCPACKWIZ" -J -r "$STAGE" \
|
||||||
|
|| die "$mkisofs failed"
|
||||||
|
|
||||||
|
info "built $ISO"
|
||||||
|
ls -lh "$ISO" | sed 's/^/ /'
|
||||||
|
command -v sha256sum >/dev/null && sha256sum "$ISO" | tee "$ISO.sha256" | sed 's/^/ /'
|
||||||
|
fi
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
# Deployment settings. Copy to deploy.conf next to install.sh and edit.
|
||||||
|
# install.sh reads this, so there are no flags to remember on the target host.
|
||||||
|
|
||||||
|
# --- identity ---------------------------------------------------------------
|
||||||
|
PACK_NAME="AbdelpakDelta" # display name; the directory slug is derived
|
||||||
|
AUTHOR="themiro"
|
||||||
|
MC_VERSION="1.21.1"
|
||||||
|
NEOFORGE="latest" # or pin e.g. 21.1.248
|
||||||
|
|
||||||
|
# --- where things live ------------------------------------------------------
|
||||||
|
SHARE="/minecraft" # ZFS dataset mountpoint
|
||||||
|
|
||||||
|
# --- how clients reach it ---------------------------------------------------
|
||||||
|
# The public URL of the mods directory. Everything else is derived from it:
|
||||||
|
# the pack URL, the guide URLs, and the address shown to players.
|
||||||
|
MIRROR_URL="https://games.abdelbaki.eu/abdelpakDelta/mods"
|
||||||
|
|
||||||
|
# --- ports ------------------------------------------------------------------
|
||||||
|
MC_PORT="27960" # Minecraft
|
||||||
|
HTTP_PORT="18080" # pack/mod HTTP server, behind your proxy
|
||||||
|
HTTP_BIND="0.0.0.0" # 127.0.0.1 if the proxy is on this host
|
||||||
|
|
||||||
|
# --- memory -----------------------------------------------------------------
|
||||||
|
XMX="10G"
|
||||||
|
XMS="10G"
|
||||||
|
|
||||||
|
# --- Minecraft EULA ---------------------------------------------------------
|
||||||
|
# Setting this to yes writes eula=true and is your acceptance of
|
||||||
|
# https://aka.ms/MinecraftEULA. Leave it as no and the install stops there.
|
||||||
|
ACCEPT_EULA="no"
|
||||||
|
|
||||||
|
# --- optional ---------------------------------------------------------------
|
||||||
|
START_SERVER="no" # yes to start Minecraft at the end
|
||||||
|
CAP_ZFS_ARC="2147483648" # bytes; empty to leave ARC alone
|
||||||
|
|
@ -0,0 +1,196 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# Single entry point for deploying from removable media.
|
||||||
|
#
|
||||||
|
# ./install.sh reads deploy.conf next to this script
|
||||||
|
# ./install.sh -c other.conf reads a different config
|
||||||
|
# ./install.sh --check verify prerequisites and exit
|
||||||
|
#
|
||||||
|
# Copies itself off read-only media, installs prerequisites, and runs the
|
||||||
|
# deploy. Everything is configured in deploy.conf, so there are no flags to
|
||||||
|
# remember at the console.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
HERE="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
CONF="$HERE/deploy.conf"
|
||||||
|
CHECK_ONLY=0
|
||||||
|
WORKDIR=""
|
||||||
|
|
||||||
|
die() { printf '\n\033[1;31merror:\033[0m %s\n\n' "$*" >&2; exit 1; }
|
||||||
|
step() { printf '\n\033[1m=== %s ===\033[0m\n\n' "$*"; }
|
||||||
|
info() { echo ">>> $*"; }
|
||||||
|
warn() { echo " warning: $*" >&2; }
|
||||||
|
|
||||||
|
while [ $# -gt 0 ]; do
|
||||||
|
case "$1" in
|
||||||
|
-c|--config) CONF="${2:-}"; shift 2 ;;
|
||||||
|
--check) CHECK_ONLY=1; shift ;;
|
||||||
|
-w|--workdir) WORKDIR="${2:-}"; shift 2 ;;
|
||||||
|
-h|--help) awk 'NR>1 && /^#/ {sub(/^# ?/,""); print; next} NR>1 {exit}' "$0"; exit 0 ;;
|
||||||
|
*) die "unknown option: $1" ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
[ "$(id -u)" -ne 0 ] || die "run this as your normal user, not root — it calls sudo itself"
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------ config ---
|
||||||
|
|
||||||
|
[ -f "$CONF" ] || die "no config at $CONF
|
||||||
|
Copy deploy.conf.example to deploy.conf and edit it first."
|
||||||
|
|
||||||
|
# shellcheck source=/dev/null
|
||||||
|
. "$CONF"
|
||||||
|
|
||||||
|
for v in PACK_NAME AUTHOR MIRROR_URL SHARE; do
|
||||||
|
[ -n "${!v:-}" ] || die "$v is not set in $CONF"
|
||||||
|
done
|
||||||
|
|
||||||
|
MC_VERSION="${MC_VERSION:-1.21.1}"
|
||||||
|
NEOFORGE="${NEOFORGE:-latest}"
|
||||||
|
MC_PORT="${MC_PORT:-25565}"
|
||||||
|
HTTP_PORT="${HTTP_PORT:-18080}"
|
||||||
|
HTTP_BIND="${HTTP_BIND:-0.0.0.0}"
|
||||||
|
XMX="${XMX:-10G}"
|
||||||
|
XMS="${XMS:-$XMX}"
|
||||||
|
ACCEPT_EULA="${ACCEPT_EULA:-no}"
|
||||||
|
START_SERVER="${START_SERVER:-no}"
|
||||||
|
CAP_ZFS_ARC="${CAP_ZFS_ARC:-}"
|
||||||
|
|
||||||
|
# ------------------------------------------------------------- prequisites ---
|
||||||
|
|
||||||
|
step "Checking prerequisites"
|
||||||
|
|
||||||
|
MISSING=()
|
||||||
|
need() { command -v "$1" >/dev/null || MISSING+=("$2"); }
|
||||||
|
need java java-21-openjdk-headless
|
||||||
|
need curl curl
|
||||||
|
need go golang
|
||||||
|
need python3 python3
|
||||||
|
need zfs zfs
|
||||||
|
need findmnt util-linux
|
||||||
|
need mkfifo coreutils
|
||||||
|
|
||||||
|
if [ "${#MISSING[@]}" -gt 0 ]; then
|
||||||
|
info "missing: ${MISSING[*]}"
|
||||||
|
if (( CHECK_ONLY )); then
|
||||||
|
die "prerequisites missing (see above)"
|
||||||
|
fi
|
||||||
|
info "installing with dnf"
|
||||||
|
sudo dnf install -y "${MISSING[@]}" \
|
||||||
|
|| die "could not install: ${MISSING[*]}
|
||||||
|
Install them by hand and re-run."
|
||||||
|
else
|
||||||
|
info "all present"
|
||||||
|
fi
|
||||||
|
|
||||||
|
jver="$(java -version 2>&1 | head -1 | grep -oE '"[0-9]+' | tr -d '"' || echo 0)"
|
||||||
|
[ "${jver:-0}" -ge 21 ] || warn "java is $jver; NeoForge for 1.21.x wants 21"
|
||||||
|
|
||||||
|
if ! mountpoint -q "$SHARE"; then
|
||||||
|
die "$SHARE is not a mountpoint — the ZFS dataset is not mounted.
|
||||||
|
Import and mount the pool first:
|
||||||
|
sudo zpool import -a && sudo zfs mount -a
|
||||||
|
Then re-run. Deploying onto an unmounted path would fill the root disk."
|
||||||
|
fi
|
||||||
|
info "$SHARE is mounted ($(findmnt -no SOURCE "$SHARE"))"
|
||||||
|
|
||||||
|
if (( CHECK_ONLY )); then
|
||||||
|
step "Prerequisites OK"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --------------------------------------------------------------- workspace ---
|
||||||
|
|
||||||
|
# The media is very likely read-only, and the deploy needs somewhere writable
|
||||||
|
# for the go build and the pack. Copy the kit onto the host first.
|
||||||
|
if [ -z "$WORKDIR" ]; then
|
||||||
|
if [ -w "$HERE" ]; then
|
||||||
|
WORKDIR="$HERE"
|
||||||
|
else
|
||||||
|
WORKDIR="$HOME/mc-packwiz-deploy"
|
||||||
|
info "media is read-only — copying the kit to $WORKDIR"
|
||||||
|
mkdir -p "$WORKDIR"
|
||||||
|
cp -f "$HERE"/*.sh "$HERE"/*.py "$WORKDIR"/ 2>/dev/null || true
|
||||||
|
cp -f "$CONF" "$WORKDIR/deploy.conf"
|
||||||
|
chmod +x "$WORKDIR"/*.sh "$WORKDIR"/*.py 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
cd "$WORKDIR"
|
||||||
|
[ -x ./deploy.sh ] || die "deploy.sh not found in $WORKDIR"
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------- zfs arc ---
|
||||||
|
|
||||||
|
if [ -n "$CAP_ZFS_ARC" ]; then
|
||||||
|
if [ ! -f /etc/modprobe.d/zfs.conf ] || ! grep -q zfs_arc_max /etc/modprobe.d/zfs.conf; then
|
||||||
|
step "Capping the ZFS ARC at $CAP_ZFS_ARC bytes"
|
||||||
|
echo "options zfs zfs_arc_max=$CAP_ZFS_ARC" | sudo tee /etc/modprobe.d/zfs.conf >/dev/null
|
||||||
|
warn "the ARC cap needs a reboot (and 'sudo dracut --force') to take effect"
|
||||||
|
else
|
||||||
|
info "ZFS ARC already capped in /etc/modprobe.d/zfs.conf"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------ deploy ---
|
||||||
|
|
||||||
|
EULA_FLAG=""
|
||||||
|
case "${ACCEPT_EULA,,}" in
|
||||||
|
yes|true|1) EULA_FLAG="--accept-eula" ;;
|
||||||
|
*) die "ACCEPT_EULA is not set to yes in $CONF.
|
||||||
|
Read https://aka.ms/MinecraftEULA, then set ACCEPT_EULA=\"yes\" to accept it." ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
step "Deploying $PACK_NAME"
|
||||||
|
|
||||||
|
./deploy.sh \
|
||||||
|
-n "$PACK_NAME" \
|
||||||
|
-a "$AUTHOR" \
|
||||||
|
-u "$MIRROR_URL" \
|
||||||
|
-m "$MC_VERSION" \
|
||||||
|
-N "$NEOFORGE" \
|
||||||
|
-s "$SHARE" \
|
||||||
|
-p "$MC_PORT" \
|
||||||
|
-H "$HTTP_PORT" \
|
||||||
|
-B "$HTTP_BIND" \
|
||||||
|
-X "$XMX" \
|
||||||
|
-x "$XMS" \
|
||||||
|
$EULA_FLAG
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------- start ---
|
||||||
|
|
||||||
|
case "${START_SERVER,,}" in
|
||||||
|
yes|true|1)
|
||||||
|
step "Starting Minecraft"
|
||||||
|
sudo systemctl start minecraft
|
||||||
|
sleep 3
|
||||||
|
if systemctl is-active --quiet minecraft; then
|
||||||
|
info "minecraft.service is running"
|
||||||
|
else
|
||||||
|
warn "minecraft.service did not come up — journalctl -u minecraft -n 50"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------- done ----
|
||||||
|
|
||||||
|
SITE="${MIRROR_URL%/*}"
|
||||||
|
HOSTONLY="$(sed -e 's#^[a-z]*://##' -e 's#/.*##' -e 's#:.*##' <<<"$SITE")"
|
||||||
|
[ "$MC_PORT" = "25565" ] && SHOWN="$HOSTONLY" || SHOWN="$HOSTONLY:$MC_PORT"
|
||||||
|
|
||||||
|
step "Done"
|
||||||
|
cat <<EOF
|
||||||
|
Kit installed in: $WORKDIR
|
||||||
|
Pack HTTP server: ${HTTP_BIND}:${HTTP_PORT} (proxy / -> this)
|
||||||
|
Players connect to: $SHOWN
|
||||||
|
Setup guides: $SITE/setup-windows-packwiz.html
|
||||||
|
$SITE/setup-linux-packwiz.html
|
||||||
|
|
||||||
|
Still to do by hand:
|
||||||
|
1. Point your reverse proxy at ${HTTP_BIND}:${HTTP_PORT}
|
||||||
|
2. Drop mod jars into $SHARE/mods
|
||||||
|
3. Run $WORKDIR/mc-refresh-restart.sh to register them
|
||||||
|
|
||||||
|
sudo systemctl start minecraft
|
||||||
|
journalctl -u minecraft -f
|
||||||
|
echo "op YourName" | sudo tee /run/minecraft/console
|
||||||
|
EOF
|
||||||
Loading…
Reference in New Issue