#!/usr/bin/env bash set -euo pipefail DIR="/opt/hx-ki/com2-stack" F="$DIR/docker-compose.yml" C="hxki-postgres" TS="$(date +%Y%m%d-%H%M%S)" BK="/opt/hx-ki/backups/com2-pg-semicolon-$TS" mkdir -p "$BK" echo "=== COM2 ยท FIX POSTGRES ; + VERIFY (one-shot) ===" echo "Compose: $F" echo "Backup: $BK" echo [ -f "$F" ] || { echo "FAIL: missing $F"; exit 1; } cp -a "$F" "$BK/docker-compose.yml.pre" echo "[1] Show offending lines (with trailing ';')" grep -nE 'POSTGRES_(USER|PASSWORD|DB)=.*;$' "$F" || echo "OK: no trailing ';' in compose" echo echo "[2] Patch ONLY trailing ';' for POSTGRES_USER/POSTGRES_PASSWORD/POSTGRES_DB (compose is authority)" # supports both list-form " - POSTGRES_USER=..." and mapping-form "POSTGRES_USER: ..." sed -i -E \ -e 's/^(\s*-\s*POSTGRES_USER=[^;]*);$/\1/' \ -e 's/^(\s*-\s*POSTGRES_PASSWORD=[^;]*);$/\1/' \ -e 's/^(\s*-\s*POSTGRES_DB=[^;]*);$/\1/' \ -e 's/^(\s*POSTGRES_USER:\s*[^;]*);$/\1/' \ -e 's/^(\s*POSTGRES_PASSWORD:\s*[^;]*);$/\1/' \ -e 's/^(\s*POSTGRES_DB:\s*[^;]*);$/\1/' \ "$F" echo echo "[3] Validate compose" docker compose -f "$F" config >/dev/null echo "OK: compose valid" echo echo "[4] Recreate postgres container to re-inject ENV" cd "$DIR" docker compose up -d --force-recreate --no-deps "$C" >/dev/null echo echo "[5] Read ENV actually inside container (ground truth)" docker inspect "$C" --format '{{range .Config.Env}}{{println .}}{{end}}' | egrep '^POSTGRES_(USER|PASSWORD|DB)=' || true PG_USER="$(docker inspect "$C" --format '{{range .Config.Env}}{{println .}}{{end}}' | awk -F= '/^POSTGRES_USER=/{print $2}' | tail -n1)" PG_DB="$(docker inspect "$C" --format '{{range .Config.Env}}{{println .}}{{end}}' | awk -F= '/^POSTGRES_DB=/{print $2}' | tail -n1)" PG_PW="$(docker inspect "$C" --format '{{range .Config.Env}}{{println .}}{{end}}' | awk -F= '/^POSTGRES_PASSWORD=/{print $2}' | tail -n1)" echo echo "[6] Wait until Postgres accepts connections (max 30s)" for i in $(seq 1 30); do if docker exec -e PGPASSWORD="$PG_PW" "$C" psql -U "$PG_USER" -d "$PG_DB" -c "SELECT 1;" >/dev/null 2>&1; then echo "OK: login works with CURRENT ENV" OK_MODE="CURRENT_ENV" break fi sleep 1 done if [ "${OK_MODE:-}" != "CURRENT_ENV" ]; then echo "WARN: login failed with CURRENT ENV. Testing legacy ';' variants deterministically." # Variant B: user+db with ';' and password with ';' if docker exec -e PGPASSWORD="${PG_PW};" "$C" psql -U "${PG_USER};" -d "${PG_DB};" -c "SELECT 1;" >/dev/null 2>&1; then echo "OK: legacy semicolon credentials work (user/db/pw had ';' at init time)" OK_MODE="LEGACY_SEMICOLON" else echo "FAIL: Neither CURRENT_ENV nor LEGACY_SEMICOLON credentials can authenticate." echo "=> Then the only deterministic path is an auth-reset inside the data dir (no guessing), because plaintext can't be recovered." exit 2 fi fi echo echo "=== RESULT ===" echo "MODE=$OK_MODE" echo "POSTGRES_USER=$PG_USER" echo "POSTGRES_DB=$PG_DB" echo "POSTGRES_PASSWORD=$PG_PW" echo "Backup: $BK" echo "=== DONE ==="