Fix: stack-deploy.sh now picks export_raw/export_raw_merged for folder+extras stacks (docker compose config path) vs export/export_merged for single-file stacks, matching envparse.py's dual-escaping fix
This commit is contained in:
+30
-8
@@ -28,6 +28,16 @@
|
|||||||
# source path exists, and flags suspicious-looking empty Postgres data
|
# source path exists, and flags suspicious-looking empty Postgres data
|
||||||
# dirs, before anything touches Swarm.
|
# dirs, before anything touches Swarm.
|
||||||
# 3. docker stack deploy -c <tempfile> <stack>
|
# 3. docker stack deploy -c <tempfile> <stack>
|
||||||
|
#
|
||||||
|
# Dollar-escaping (2026-08-26, see envparse.py comments for full detail):
|
||||||
|
# - Single-file path (no extras): envsubst does no '$' escaping of its
|
||||||
|
# own, and only Swarm's `docker stack deploy` interpolation pass runs
|
||||||
|
# downstream -> use export/export_merged (escapes '$' -> '$$' once).
|
||||||
|
# - Folder+extras path: `docker compose config` ALSO does its own '$'
|
||||||
|
# escaping on top of Swarm's -> use export_raw/export_raw_merged (no
|
||||||
|
# pre-escaping) or values get doubled twice. Getting this wrong
|
||||||
|
# silently corrupts any secret/hash containing '$' (confirmed impact:
|
||||||
|
# LITELLM keys truncated, IMMICH_KIOSK_BASICAUTH bcrypt hash mismatched).
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
@@ -94,6 +104,12 @@ for f in "${EXTRAS[@]:-}"; do
|
|||||||
[ -n "$f" ] && F_FLAGS+=(-f "$f")
|
[ -n "$f" ] && F_FLAGS+=(-f "$f")
|
||||||
done
|
done
|
||||||
|
|
||||||
|
# Whether the render will go through `docker compose config` (folder mode
|
||||||
|
# with 1+ extras). This determines which escaping mode is correct — see
|
||||||
|
# header comment and envparse.py for why these must differ.
|
||||||
|
USES_COMPOSE_CONFIG=0
|
||||||
|
[ "${#F_FLAGS[@]}" -gt 2 ] && USES_COMPOSE_CONFIG=1
|
||||||
|
|
||||||
# ── Load env (global base + optional stack override) ─────────────────────────
|
# ── Load env (global base + optional stack override) ─────────────────────────
|
||||||
|
|
||||||
HINT=" Hint: 'secret not found' means Woodpecker hasn't provisioned secrets yet.\n Trigger the pipeline: https://woodpecker.bryanmail.net\n"
|
HINT=" Hint: 'secret not found' means Woodpecker hasn't provisioned secrets yet.\n Trigger the pipeline: https://woodpecker.bryanmail.net\n"
|
||||||
@@ -103,19 +119,25 @@ STACK_EXISTS=0
|
|||||||
[ -f "$GLOBAL_ENV" ] && GLOBAL_EXISTS=1
|
[ -f "$GLOBAL_ENV" ] && GLOBAL_EXISTS=1
|
||||||
[ -f "$ENVFILE" ] && STACK_EXISTS=1
|
[ -f "$ENVFILE" ] && STACK_EXISTS=1
|
||||||
|
|
||||||
|
if [ "$USES_COMPOSE_CONFIG" -eq 1 ]; then
|
||||||
|
EXPORT_MODE="export_raw"; EXPORT_MERGED_MODE="export_raw_merged"
|
||||||
|
else
|
||||||
|
EXPORT_MODE="export"; EXPORT_MERGED_MODE="export_merged"
|
||||||
|
fi
|
||||||
|
|
||||||
if [ "$GLOBAL_EXISTS" -eq 1 ] && [ "$STACK_EXISTS" -eq 1 ]; then
|
if [ "$GLOBAL_EXISTS" -eq 1 ] && [ "$STACK_EXISTS" -eq 1 ]; then
|
||||||
echo " Env: $GLOBAL_ENV + $ENVFILE (stack overrides global)"
|
echo " Env: $GLOBAL_ENV + $ENVFILE (stack overrides global) [$EXPORT_MERGED_MODE]"
|
||||||
eval "$(python3 "$PY" export_merged "$GLOBAL_ENV" "$ENVFILE")"
|
eval "$(python3 "$PY" "$EXPORT_MERGED_MODE" "$GLOBAL_ENV" "$ENVFILE")"
|
||||||
VARS="$(python3 "$PY" vars_merged "$GLOBAL_ENV" "$ENVFILE")"
|
VARS="$(python3 "$PY" vars_merged "$GLOBAL_ENV" "$ENVFILE")"
|
||||||
|
|
||||||
elif [ "$GLOBAL_EXISTS" -eq 1 ]; then
|
elif [ "$GLOBAL_EXISTS" -eq 1 ]; then
|
||||||
echo " Env: $GLOBAL_ENV (no stack env)"
|
echo " Env: $GLOBAL_ENV (no stack env) [$EXPORT_MODE]"
|
||||||
eval "$(python3 "$PY" export "$GLOBAL_ENV")"
|
eval "$(python3 "$PY" "$EXPORT_MODE" "$GLOBAL_ENV")"
|
||||||
VARS="$(python3 "$PY" vars "$GLOBAL_ENV")"
|
VARS="$(python3 "$PY" vars "$GLOBAL_ENV")"
|
||||||
|
|
||||||
elif [ "$STACK_EXISTS" -eq 1 ]; then
|
elif [ "$STACK_EXISTS" -eq 1 ]; then
|
||||||
echo " Env: $ENVFILE (no global env)"
|
echo " Env: $ENVFILE (no global env) [$EXPORT_MODE]"
|
||||||
eval "$(python3 "$PY" export "$ENVFILE")"
|
eval "$(python3 "$PY" "$EXPORT_MODE" "$ENVFILE")"
|
||||||
VARS="$(python3 "$PY" vars "$ENVFILE")"
|
VARS="$(python3 "$PY" vars "$ENVFILE")"
|
||||||
|
|
||||||
else
|
else
|
||||||
@@ -131,7 +153,7 @@ RENDERED="$(mktemp /tmp/stack-deploy.XXXXXX.yml)"
|
|||||||
trap 'rm -f "$RENDERED"' EXIT
|
trap 'rm -f "$RENDERED"' EXIT
|
||||||
|
|
||||||
if [ -n "$VARS" ]; then
|
if [ -n "$VARS" ]; then
|
||||||
if [ "${#F_FLAGS[@]}" -gt 2 ]; then
|
if [ "$USES_COMPOSE_CONFIG" -eq 1 ]; then
|
||||||
# Folder mode with extras: merge via docker compose config
|
# Folder mode with extras: merge via docker compose config
|
||||||
docker compose "${F_FLAGS[@]}" config \
|
docker compose "${F_FLAGS[@]}" config \
|
||||||
| python3 "$PY" strip \
|
| python3 "$PY" strip \
|
||||||
@@ -144,7 +166,7 @@ if [ -n "$VARS" ]; then
|
|||||||
> "$RENDERED"
|
> "$RENDERED"
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
if [ "${#F_FLAGS[@]}" -gt 2 ]; then
|
if [ "$USES_COMPOSE_CONFIG" -eq 1 ]; then
|
||||||
docker compose "${F_FLAGS[@]}" config \
|
docker compose "${F_FLAGS[@]}" config \
|
||||||
| python3 "$PY" strip \
|
| python3 "$PY" strip \
|
||||||
> "$RENDERED"
|
> "$RENDERED"
|
||||||
|
|||||||
Reference in New Issue
Block a user