From a557d9fb07836a19cecdda8f2562941b48164806 Mon Sep 17 00:00:00 2001 From: admin Date: Wed, 26 Aug 2026 23:04:11 -0700 Subject: [PATCH] Refactor: unify stack-deploy.sh render paths into a single temp-file render step, then run mount-guard.py before docker stack deploy --- deploy/stack-deploy.sh | 38 +++++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/deploy/stack-deploy.sh b/deploy/stack-deploy.sh index 0b79188..9ebc571 100755 --- a/deploy/stack-deploy.sh +++ b/deploy/stack-deploy.sh @@ -20,12 +20,21 @@ # All files in the folder matching *.yml or *.yaml are included. # Main file (.yml or .yaml) is always passed FIRST. # Remaining files are sorted and appended. +# +# Render pipeline (unified for all modes as of 2026-08-26): +# 1. Render full compose YAML (with extras merged + env substituted) to a +# temp file. +# 2. Run mount-guard.py against that temp file — checks every bind mount +# source path exists, and flags suspicious-looking empty Postgres data +# dirs, before anything touches Swarm. +# 3. docker stack deploy -c set -euo pipefail STACK="${1:?Usage: stack-deploy.sh }" DIR="/volume1/docker/compose-files" PY="$DIR/deploy/envparse.py" +MOUNT_GUARD="$DIR/deploy/mount-guard.py" GLOBAL_ENV="$DIR/deploy/global.env" # ── Pre-flight: ensure local checkout is in sync with Gitea ───────────────── @@ -116,7 +125,10 @@ fi echo " Vars: $VARS" -# ── Deploy ─────────────────────────────────────────────────────────────────── +# ── Render final compose YAML to a temp file ────────────────────────────────── + +RENDERED="$(mktemp /tmp/stack-deploy.XXXXXX.yml)" +trap 'rm -f "$RENDERED"' EXIT if [ -n "$VARS" ]; then if [ "${#F_FLAGS[@]}" -gt 2 ]; then @@ -124,25 +136,33 @@ if [ -n "$VARS" ]; then docker compose "${F_FLAGS[@]}" config \ | python3 "$PY" strip \ | envsubst "$VARS" \ - | docker stack deploy -c - "$STACK" \ - || { printf "\n%b" "$HINT"; exit 1; } + > "$RENDERED" else # Single file envsubst "$VARS" < "$MAIN" \ | python3 "$PY" strip \ - | docker stack deploy -c - "$STACK" \ - || { printf "\n%b" "$HINT"; exit 1; } + > "$RENDERED" fi else if [ "${#F_FLAGS[@]}" -gt 2 ]; then docker compose "${F_FLAGS[@]}" config \ | python3 "$PY" strip \ - | docker stack deploy -c - "$STACK" \ - || { printf "\n%b" "$HINT"; exit 1; } + > "$RENDERED" else - docker stack deploy -c "$MAIN" "$STACK" \ - || { printf "\n%b" "$HINT"; exit 1; } + python3 "$PY" strip < "$MAIN" > "$RENDERED" fi fi +# ── Pre-flight: bind mount paths exist + Postgres-data sanity ─────────────── +# See deploy/mount-guard.py for details. Blocks on missing paths or +# suspicious-looking empty/uninitialized Postgres data directories (see +# incident 2026-08-26: a wrong-but-existing empty bind path would have let +# Postgres silently init a fresh DB while real data sat orphaned elsewhere). +python3 "$MOUNT_GUARD" "$RENDERED" || { echo "ERROR: mount-guard check failed. Deploy aborted."; exit 1; } + +# ── Deploy ─────────────────────────────────────────────────────────────────── + +docker stack deploy -c "$RENDERED" "$STACK" \ + || { printf "\n%b" "$HINT"; exit 1; } + echo "==> Done: $STACK"