Refactor: unify stack-deploy.sh render paths into a single temp-file render step, then run mount-guard.py before docker stack deploy
ci/woodpecker/push/deploy Pipeline was successful

This commit is contained in:
2026-08-26 23:04:11 -07:00
parent f696efef11
commit a557d9fb07
+29 -9
View File
@@ -20,12 +20,21 @@
# All files in the folder matching *.yml or *.yaml are included. # All files in the folder matching *.yml or *.yaml are included.
# Main file (<stack>.yml or <stack>.yaml) is always passed FIRST. # Main file (<stack>.yml or <stack>.yaml) is always passed FIRST.
# Remaining files are sorted and appended. # 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 <tempfile> <stack>
set -euo pipefail set -euo pipefail
STACK="${1:?Usage: stack-deploy.sh <stack-name>}" STACK="${1:?Usage: stack-deploy.sh <stack-name>}"
DIR="/volume1/docker/compose-files" DIR="/volume1/docker/compose-files"
PY="$DIR/deploy/envparse.py" PY="$DIR/deploy/envparse.py"
MOUNT_GUARD="$DIR/deploy/mount-guard.py"
GLOBAL_ENV="$DIR/deploy/global.env" GLOBAL_ENV="$DIR/deploy/global.env"
# ── Pre-flight: ensure local checkout is in sync with Gitea ───────────────── # ── Pre-flight: ensure local checkout is in sync with Gitea ─────────────────
@@ -116,7 +125,10 @@ fi
echo " Vars: $VARS" 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 [ -n "$VARS" ]; then
if [ "${#F_FLAGS[@]}" -gt 2 ]; then if [ "${#F_FLAGS[@]}" -gt 2 ]; then
@@ -124,25 +136,33 @@ if [ -n "$VARS" ]; then
docker compose "${F_FLAGS[@]}" config \ docker compose "${F_FLAGS[@]}" config \
| python3 "$PY" strip \ | python3 "$PY" strip \
| envsubst "$VARS" \ | envsubst "$VARS" \
| docker stack deploy -c - "$STACK" \ > "$RENDERED"
|| { printf "\n%b" "$HINT"; exit 1; }
else else
# Single file # Single file
envsubst "$VARS" < "$MAIN" \ envsubst "$VARS" < "$MAIN" \
| python3 "$PY" strip \ | python3 "$PY" strip \
| docker stack deploy -c - "$STACK" \ > "$RENDERED"
|| { printf "\n%b" "$HINT"; exit 1; }
fi fi
else else
if [ "${#F_FLAGS[@]}" -gt 2 ]; then if [ "${#F_FLAGS[@]}" -gt 2 ]; then
docker compose "${F_FLAGS[@]}" config \ docker compose "${F_FLAGS[@]}" config \
| python3 "$PY" strip \ | python3 "$PY" strip \
| docker stack deploy -c - "$STACK" \ > "$RENDERED"
|| { printf "\n%b" "$HINT"; exit 1; }
else else
docker stack deploy -c "$MAIN" "$STACK" \ python3 "$PY" strip < "$MAIN" > "$RENDERED"
|| { printf "\n%b" "$HINT"; exit 1; }
fi fi
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" echo "==> Done: $STACK"