fix(webfinger): apply values passed to a re-run instead of discarding them
.env was written only on the first run. All four values reach Caddy solely
through compose interpolating .env into the container environment -- the
Caddyfile is installed verbatim every run and carries Caddy's own
{$ISSUER_URL} / {$BASE_DOMAIN} / {$REDIRECT_URL} / {$ACME_EMAIL} placeholders,
resolved at config load. So .env is the only thing that decides what is served.
Both paths were wrong. Interactively, the prompts are required and have no
default, so a re-run made the operator retype all four -- and then discarded
every one, because a value read into a nameref is not exported and .env was
not rewritten. Via automations.sh the values ARE exported, so compose
preferred them for that run only and the documented later
`docker compose up -d` reverted to stale .env.
Either way the run exits 0 and the DEPLOYED banner prints this shell's
values, not what was deployed. Moving the IdP from auth.example.com to
id.example.com leaves the endpoint serving "href": the old issuer, pointing
every discovering client at a decommissioned issuer, with the deploy that was
meant to fix it reporting success.
Same shape as d654299: record which keys arrived in the environment before the
":=" defaults, pre-load .env into keys not passed so the prompts stop
demanding a full retype (and SKIP_PROMPTS=1 no longer dies over values .env
already has), then write only the passed keys through with set_env.
Verified: a corrected ISSUER_URL containing a & query string lands in .env
intact (the old escaping would have corrupted it); a re-run passing nothing
leaves .env byte-identical and does not die.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -9,7 +9,9 @@
|
||||
# What this does:
|
||||
# 1. Installs docker + docker-cli-compose if missing.
|
||||
# 2. Lays down docker-compose.yml, Caddyfile, .env.example in $STACK_DIR.
|
||||
# 3. Generates .env on first run; existing .env is never overwritten.
|
||||
# 3. Generates .env on first run. On a re-run an existing .env is kept,
|
||||
# except for values passed explicitly that run, which are written
|
||||
# through -- .env is what compose interpolates into the container.
|
||||
# 4. Prompts for required values not preset (BASE_DOMAIN, ISSUER_URL,
|
||||
# REDIRECT_URL, ACME_EMAIL).
|
||||
# 5. Opens TCP 80/443 in UFW if active.
|
||||
@@ -36,6 +38,18 @@ set -euo pipefail
|
||||
: "${FORCE:=0}"
|
||||
: "${SKIP_PROMPTS:=0}" # non-interactive: require values via env, no prompts
|
||||
[[ "$SKIP_PROMPTS" == "1" ]] && FORCE=1
|
||||
ENV_FILE="$STACK_DIR/.env"
|
||||
|
||||
# All four values reach Caddy only by compose interpolating .env, so .env has to absorb
|
||||
# what this run passed or the run deploys the old values while reporting the
|
||||
# new ones. Which keys arrived in the ENVIRONMENT must be recorded BEFORE the
|
||||
# ":=" defaults below, which make an unset variable look like an empty one.
|
||||
RUNTIME_KEYS=(BASE_DOMAIN ISSUER_URL REDIRECT_URL ACME_EMAIL)
|
||||
KEYS_FROM_ENV=()
|
||||
for _k in "${RUNTIME_KEYS[@]}"; do
|
||||
if [[ -n "${!_k+x}" ]]; then KEYS_FROM_ENV+=("$_k"); fi
|
||||
done
|
||||
|
||||
: "${BASE_DOMAIN:=}"
|
||||
: "${ISSUER_URL:=}"
|
||||
: "${REDIRECT_URL:=}"
|
||||
@@ -148,6 +162,24 @@ prompt() {
|
||||
fi
|
||||
}
|
||||
|
||||
# On a re-run the deployed values live in .env. Load them into any key NOT
|
||||
# passed this run, so the prompts below do not force the operator to retype
|
||||
# everything (and so SKIP_PROMPTS=1 does not die demanding values .env has).
|
||||
if [[ -f "$ENV_FILE" ]]; then
|
||||
_preloaded=()
|
||||
for _k in "${RUNTIME_KEYS[@]}"; do
|
||||
if [[ " ${KEYS_FROM_ENV[*]-} " == *" ${_k} "* ]]; then continue; fi
|
||||
_v=$(sed -n "s/^${_k}=//p" "$ENV_FILE" | tail -n1)
|
||||
if [[ -z "$_v" ]]; then continue; fi
|
||||
printf -v "$_k" '%s' "$_v"
|
||||
_preloaded+=("$_k")
|
||||
done
|
||||
if (( ${#_preloaded[@]} > 0 )); then
|
||||
log "Reusing ${ENV_FILE} values for: ${_preloaded[*]}"
|
||||
log " (pass VAR=... to change one; it is then written back to .env)"
|
||||
fi
|
||||
fi
|
||||
|
||||
prompt BASE_DOMAIN "Apex domain to serve from (e.g. example.com)"
|
||||
prompt ISSUER_URL "OIDC issuer URL (e.g. https://auth.example.com)"
|
||||
prompt REDIRECT_URL "Where to redirect non-webfinger traffic (e.g. https://example.org)"
|
||||
@@ -171,6 +203,28 @@ install -d -m 0750 "$STACK_DIR"
|
||||
install -m 0640 "$SCRIPT_DIR/docker-compose.yml" "$STACK_DIR/docker-compose.yml"
|
||||
install -m 0640 "$SCRIPT_DIR/Caddyfile" "$STACK_DIR/Caddyfile"
|
||||
|
||||
set_env() { # <KEY> <value>: update KEY in .env, or append if absent
|
||||
# The value goes through the ENVIRONMENT, never interpolated into a sed
|
||||
# script. Interpolating it corrupts any value containing & (sed expands it to
|
||||
# the whole match) and aborts the run on one containing the s||| delimiter --
|
||||
# which is reachable for an OIDC issuer URL or a redirect query string.
|
||||
local key="$1" val="$2" tmp
|
||||
if [[ ! -f "$ENV_FILE" ]]; then
|
||||
printf '%s=%s
|
||||
' "$key" "$val" >> "$ENV_FILE"
|
||||
return 0
|
||||
fi
|
||||
tmp="$(mktemp)"
|
||||
_SE_KEY="$key" _SE_VAL="$val" awk '
|
||||
BEGIN { k = ENVIRON["_SE_KEY"]; v = ENVIRON["_SE_VAL"]; seen = 0 }
|
||||
!seen && index($0, k "=") == 1 { print k "=" v; seen = 1; next }
|
||||
{ print }
|
||||
END { if (!seen) print k "=" v }
|
||||
' "$ENV_FILE" > "$tmp"
|
||||
cat "$tmp" > "$ENV_FILE" # rewrite in place: keeps the original mode/owner
|
||||
rm -f "$tmp"
|
||||
}
|
||||
|
||||
ENV_FILE="$STACK_DIR/.env"
|
||||
if [[ ! -f "$ENV_FILE" ]]; then
|
||||
log "Seeding $ENV_FILE..."
|
||||
@@ -182,7 +236,16 @@ if [[ ! -f "$ENV_FILE" ]]; then
|
||||
-e "s|^ACME_EMAIL=.*|ACME_EMAIL=${ACME_EMAIL}|" \
|
||||
"$ENV_FILE"
|
||||
else
|
||||
log ".env exists; leaving it alone."
|
||||
# Keep the file, but absorb what was passed this run: compose reads .env,
|
||||
# so letting it drift means deploying the old values while the banner
|
||||
# prints the new ones. Keys not passed are left untouched.
|
||||
log ".env exists; keeping it (only values passed this run are updated)."
|
||||
for _k in ${KEYS_FROM_ENV[@]+"${KEYS_FROM_ENV[@]}"}; do
|
||||
_cur=$(sed -n "s/^${_k}=//p" "$ENV_FILE" | tail -n1)
|
||||
if [[ "$_cur" == "${!_k}" ]]; then continue; fi
|
||||
set_env "$_k" "${!_k}"
|
||||
log " ${_k}: ${_cur:-<unset>} -> ${!_k:-<empty>}"
|
||||
done
|
||||
fi
|
||||
|
||||
# Validate required values are present.
|
||||
|
||||
Reference in New Issue
Block a user