fix(docker): harden image and automate safe VPS updates
- Use floating node:alpine that tracks the latest supported LTS; pnpm bootstrap follows package.json's packageManager pin. - Drop corepack (removed from node:26), install pnpm via npm global. - Add pnpm fetch + offline install for stable dependency-layer caching. - Run as non-root nextjs (UID/GID 33 = host www-data) with tini as PID 1 for correct signal handling. - Open node engines to >=20.9.0 so patches/minors float automatically. - Add docker-preflight.sh (per-VPS checks incl. --fix) and gate docker-update.sh so Node major upgrades require explicit review while patches deploy silently.
This commit is contained in:
1 parent
7033d65846
commit
539e6d3fad
5 files changed
+199
-84
No files matched your search
+29
-73
File diff suppressed because it is too large.
Load diff
+2
-5
@@ -7,11 +7,6 @@ services:
|
||||
# build containers on the bridge network have no outbound NAT/DNS. Build on
|
||||
# the host network instead so pnpm/npm/yarn can reach the registry.
|
||||
network: host
|
||||
args:
|
||||
# Run as the host owner (www-data = UID/GID 33, already present in the
|
||||
# node base image) of /var/www/Gamedata so the container can read + write
|
||||
# the shared gamedata directory.
|
||||
RUN_USER: "www-data"
|
||||
container_name: epicnext-cms
|
||||
# Runs on the host network so existing 127.0.0.1 refs in .env keep working:
|
||||
# MariaDB (3306), DragonflyDB/Redis (6379), emulator RCON (3003) + API (3001),
|
||||
@@ -22,6 +17,8 @@ services:
|
||||
restart: unless-stopped
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
- HOSTNAME=0.0.0.0
|
||||
volumes:
|
||||
# ── Write targets (runtime imports/uploads, persistent on the host) ──
|
||||
# The CMS writes imported furni/figures/pets/effects here (see
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"engines": {
|
||||
"node": ">=26.8.1 <27"
|
||||
"node": ">=20.9.0"
|
||||
},
|
||||
"packageManager": "[email protected]+sha512.5cde925b4f075f725eb71fbae18a42ffe784524789f19b61c731cb8721ec28aaee160e01a8d5af4fedb2a42cdbf300efe23db356b0d4a17b4d63e11f8ab7c956",
|
||||
"scripts": {
|
||||
|
||||
Executable
+139
File diff suppressed because it is too large.
Load diff
@@ -32,7 +32,13 @@ touch "$LOG_FILE"
|
||||
|
||||
log "=== Start docker-update ==="
|
||||
|
||||
# --- 0. Guard: uncommitted changes would break git pull / taint deploys ---
|
||||
# --- 0. Preflight: verify this VPS is ready (permissions, ports, deps) ---
|
||||
if ! "$DIR/scripts/docker-preflight.sh"; then
|
||||
die "preflight failed — fix issues first (see '--fix' flag)" 1
|
||||
fi
|
||||
log "preflight OK"
|
||||
|
||||
# --- 0b. Guard: uncommitted changes would break git pull / taint deploys ---
|
||||
if ! { git diff --quiet --exit-code && git diff --cached --quiet --exit-code; }; then
|
||||
die "working tree has uncommitted changes; commit or stash first" 1
|
||||
fi
|
||||
@@ -55,15 +61,32 @@ else
|
||||
fi
|
||||
log "db:migrate OK"
|
||||
|
||||
# --- 3. Rebuild the image ---
|
||||
# --- 3. Node major gate (patches auto, major upgrades need review) ---
|
||||
# `node:alpine` floats within, then across, Node majors. Patches/minors are
|
||||
# safe to apply silently; a NEW major (e.g. 26 -> 27) is a breaking risk for
|
||||
# native addons / Next compatibility, so require an explicit review before it
|
||||
# goes live. Compare the major of the deployed runtime image vs the floating
|
||||
# tag; abort (not deploy) when they differ.
|
||||
deployed_major="$(docker inspect --format '{{.Config.Image}}' epicnext-cms 2>/dev/null || true)"
|
||||
# Resolve the currently-deployed Node major from its image.
|
||||
if [ -n "$deployed_major" ] && docker image inspect "$deployed_major" >/dev/null 2>&1; then
|
||||
deployed_major="$(docker run --rm --entrypoint sh "$deployed_major" -c 'node -p "process.versions.node.split(\".\")[0]"' 2>/dev/null || true)"
|
||||
fi
|
||||
float_major="$(docker run --rm --entrypoint sh node:alpine -c 'node -p "process.versions.node.split(\".\")[0]"' 2>/dev/null || true)"
|
||||
if [ -n "$deployed_major" ] && [ -n "$float_major" ] && [ "$deployed_major" != "$float_major" ]; then
|
||||
die "Node major change detected (deployed v$deployed_major, floating tag v$float_major). Major upgrades require review; update engines/Dockerfile deliberately first." 1
|
||||
fi
|
||||
log "Node major gate OK (major=${float_major:-?})"
|
||||
|
||||
# --- 4. Rebuild the image ---
|
||||
docker compose build >>"$LOG_FILE" 2>&1 || die "docker compose build failed" 2
|
||||
log "docker compose build OK"
|
||||
|
||||
# --- 4. Recreate the container ---
|
||||
# --- 5. Recreate the container ---
|
||||
docker compose up -d >>"$LOG_FILE" 2>&1 || die "docker compose up failed" 2
|
||||
log "docker compose up OK"
|
||||
|
||||
# --- 5. Wait for health (up to ~4 min) ---
|
||||
# --- 6. Wait for health (up to ~4 min) ---
|
||||
healthy=0
|
||||
for i in $(seq 1 16); do
|
||||
status="$(docker inspect --format='{{.State.Health.Status}}' epicnext-cms 2>/dev/null || true)"
|
||||
@@ -82,7 +105,7 @@ else
|
||||
exit 3
|
||||
fi
|
||||
|
||||
# --- 6. Make sure the stale host-side PM2 CMS stays stopped ---
|
||||
# --- 7. Make sure the stale host-side PM2 CMS stays stopped ---
|
||||
if command -v pm2 >/dev/null 2>&1 && pm2 jlist >/dev/null 2>&1; then
|
||||
if pm2 list 2>/dev/null | grep -q "${PM2_APP}"; then
|
||||
pm2 stop "$PM2_APP" >/dev/null 2>&1 && log "pm2 '${PM2_APP}' kept stopped (avoids port 3002 clash)"
|
||||
|
||||
Reference in new issue
Block a user