#!/usr/bin/env bash set -euo pipefail DIR="/opt/hx-ki/com2-stack" COMPOSE="$DIR/docker-compose.yml" ENVF="$DIR/.env" [ -f "$COMPOSE" ] || { echo "FAIL: missing $COMPOSE"; exit 1; } [ -f "$ENVF" ] || { echo "FAIL: missing $ENVF"; exit 1; } cd "$DIR" echo "=== COM2 ยท MAUTIC INSTALL (ONE-SHOT, NO REBUILD, NO GUESSING) ===" # Load .env as authority (only simple KEY=VALUE lines) set -a . "$ENVF" set +a # Resolve service names from compose (authority: compose) MAUTIC_SVC="$(python3 - <<'PY' import re, pathlib p=pathlib.Path("/opt/hx-ki/com2-stack/docker-compose.yml") s=p.read_text() m=re.search(r'(?ms)^services:\s*\n(.*?)(?=^\S|\Z)', s) if not m: raise SystemExit("FAIL: no services: block") blk=m.group(1) # find service with container_name: hxki-mautic cur=None for line in blk.splitlines(): mm=re.match(r'^\s{2}([A-Za-z0-9_.-]+):\s*$', line) if mm: cur=mm.group(1); continue if cur and re.match(r'^\s{4}container_name:\s*hxki-mautic\s*$', line): print(cur); raise SystemExit(0) raise SystemExit("FAIL: could not find service for container_name: hxki-mautic") PY )" DB_SVC="$(python3 - <<'PY' import re, pathlib p=pathlib.Path("/opt/hx-ki/com2-stack/docker-compose.yml") s=p.read_text() m=re.search(r'(?ms)^services:\s*\n(.*?)(?=^\S|\Z)', s) if not m: raise SystemExit("FAIL: no services: block") blk=m.group(1) # find service with container_name: hxki-mariadb cur=None for line in blk.splitlines(): mm=re.match(r'^\s{2}([A-Za-z0-9_.-]+):\s*$', line) if mm: cur=mm.group(1); continue if cur and re.match(r'^\s{4}container_name:\s*hxki-mariadb\s*$', line): print(cur); raise SystemExit(0) raise SystemExit("FAIL: could not find service for container_name: hxki-mariadb") PY )" MA="hxki-mautic" MY="hxki-mariadb" CA="hx-caddy" echo "[1] Bring up DB + Mautic (no orphans change)" docker compose up -d "$DB_SVC" "$MAUTIC_SVC" >/dev/null echo "[2] Hard requirements from .env (NO guessing)" req() { local k="$1"; [ -n "${!k:-}" ] || { echo "FAIL: missing $k in $ENVF"; exit 1; }; } req MAUTIC_DB_HOST req MAUTIC_DB_NAME req MAUTIC_DB_USER req MAUTIC_DB_PASSWORD req MAUTIC_ADMIN_USERNAME req MAUTIC_ADMIN_PASSWORD req MAUTIC_ADMIN_EMAIL echo " DB_HOST=$MAUTIC_DB_HOST DB_NAME=$MAUTIC_DB_NAME DB_USER=$MAUTIC_DB_USER DB_PASS=***" echo " ADMIN_USER=$MAUTIC_ADMIN_USERNAME ADMIN_EMAIL=$MAUTIC_ADMIN_EMAIL ADMIN_PASS=***" echo "[3] Read MariaDB root password (NO guessing: container env)" ROOTPW="$(docker inspect "$MY" --format '{{range .Config.Env}}{{println .}}{{end}}' \ | awk -F= '/^(MARIADB_ROOT_PASSWORD|MYSQL_ROOT_PASSWORD)=/{print $2}' | tail -n1 || true)" [ -n "${ROOTPW:-}" ] || { echo "FAIL: MariaDB root password not found in container env"; exit 1; } echo "[4] Wait MariaDB ready (max 60s) + ensure DB exists (no data loss)" for i in {1..60}; do if docker exec -i "$MY" sh -lc "mariadb -uroot -p\"$ROOTPW\" -e 'SELECT 1' >/dev/null 2>&1"; then break fi sleep 1 [ "$i" -eq 60 ] && { echo "FAIL: MariaDB not ready"; docker logs --tail=120 "$MY" || true; exit 1; } done docker exec -i "$MY" sh -lc "mariadb -uroot -p\"$ROOTPW\" </dev/null # If Mautic uses non-root user, ensure it can log in + has grants (still no data loss) if [ "$MAUTIC_DB_USER" != "root" ]; then docker exec -i "$MY" sh -lc "mariadb -uroot -p\"$ROOTPW\" </dev/null fi echo "[5] If already installed -> stop here (local.php present)" if docker exec -i "$MA" sh -lc "test -f app/config/local.php"; then echo "OK: already installed (app/config/local.php exists)" else echo "[6] Run Mautic installer inside container (deterministic CLI)" # detect console path CONSOLE="" if docker exec -i "$MA" sh -lc "test -x bin/console"; then CONSOLE="bin/console" elif docker exec -i "$MA" sh -lc "test -x app/console"; then CONSOLE="app/console" else echo "FAIL: cannot find bin/console or app/console inside $MA" docker exec -i "$MA" sh -lc "ls -la" || true exit 1 fi # Run installer (no interaction) docker exec -i "$MA" sh -lc " php $CONSOLE mautic:install \ --db_driver=pdo_mysql \ --db_host=\"$MAUTIC_DB_HOST\" \ --db_port=3306 \ --db_name=\"$MAUTIC_DB_NAME\" \ --db_user=\"$MAUTIC_DB_USER\" \ --db_password=\"$MAUTIC_DB_PASSWORD\" \ --admin_firstname=HXKI \ --admin_lastname=Admin \ --admin_username=\"$MAUTIC_ADMIN_USERNAME\" \ --admin_password=\"$MAUTIC_ADMIN_PASSWORD\" \ --admin_email=\"$MAUTIC_ADMIN_EMAIL\" \ --no-interaction " || { echo "FAIL: mautic:install failed"; docker logs --tail=200 "$MA" || true; exit 1; } echo "[7] Verify local.php created" docker exec -i "$MA" sh -lc "test -f app/config/local.php" || { echo "FAIL: install did not create app/config/local.php"; exit 1; } echo "OK: installed (local.php present)" fi echo "[8] Restart Mautic (clean) and verify Caddy can reach it" docker compose up -d --force-recreate "$MAUTIC_SVC" >/dev/null # wait for http from caddy for i in {1..60}; do if docker exec -i "$CA" sh -lc "wget -qO- http://$MA/ >/dev/null"; then echo "OK_CADDY_TO_MAUTIC" echo "=== DONE ===" exit 0 fi sleep 1 done echo "FAIL: Caddy still cannot reach Mautic after install" docker logs --tail=200 "$MA" || true exit 1