fix(beszel): apply BESZEL_DOMAIN passed to a re-run
Same defect as webfinger, and the typo case makes it concrete. A first deploy
with monitroing.example.com never gets an ACME certificate. The operator spots
it and re-runs with the correct hostname -- .env still holds the typo, so
Caddy still has only that site block, still fails ACME, and the endpoint still
does not work, while the script exits 0 printing
"URL: https://monitoring.example.com".
The two sources disagree and nothing compares them: validation greps .env for
non-empty (a typo passes), while the confirmation prompt and the DEPLOYED
banner expand this shell's variable. Interactively the prompted value is not
exported, so compose produces a byte-identical config and does not even
recreate the container -- nothing happens at all.
Fixed with the same idiom as webfinger and d654299.
Verified: a corrected BESZEL_DOMAIN lands in .env and is logged; a re-run
passing nothing leaves .env untouched.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -7,7 +7,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 (BESZEL_DOMAIN, ACME_EMAIL).
|
||||
# 5. Opens TCP 80/443 in UFW if active.
|
||||
# 6. Pulls images, brings the stack up, waits for healthchecks.
|
||||
@@ -32,6 +34,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"
|
||||
|
||||
# Both 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=(BESZEL_DOMAIN ACME_EMAIL)
|
||||
KEYS_FROM_ENV=()
|
||||
for _k in "${RUNTIME_KEYS[@]}"; do
|
||||
if [[ -n "${!_k+x}" ]]; then KEYS_FROM_ENV+=("$_k"); fi
|
||||
done
|
||||
|
||||
: "${BESZEL_DOMAIN:=}"
|
||||
: "${ACME_EMAIL:=}"
|
||||
|
||||
@@ -142,6 +156,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 BESZEL_DOMAIN "Public hostname for beszel (e.g. monitoring.example.com)"
|
||||
prompt ACME_EMAIL "Let's Encrypt email"
|
||||
|
||||
@@ -163,6 +195,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..."
|
||||
@@ -172,7 +226,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
|
||||
|
||||
Reference in New Issue
Block a user