#!/usr/bin/env bash set -euo pipefail # Bootstrap / re-provision a TEST (staging) stack — e.g. test.escapepage.com. # Same job as docker/setup.sh, but: # - refuses to run unless docker/.env marks this as a non-prod stack # - scopes every compose call to COMPOSE_PROJECT_NAME # - runs DB / cache / console steps as www-data and repairs var/ ownership at # the end, so php-fpm (www-data) can actually read the compiled container # (bare `docker exec` runs as root and would leave var/cache root-owned) # - NEVER touches var/volumes/db (MySQL owns that) # # Usage: # ./docker/setup.test.sh # full setup (build images) # ./docker/setup.test.sh --no-build # skip image rebuild # ./docker/setup.test.sh --recreate # force-recreate containers # ./docker/setup.test.sh --down # stop and remove this stack's containers DOCKER_DIR=$(cd "$(dirname "$0")" && pwd) ROOT_DIR=$(cd "$DOCKER_DIR/.." && pwd) env_val() { [ -f "$DOCKER_DIR/.env" ] && grep -E "^$1=" "$DOCKER_DIR/.env" | tail -n1 | cut -d= -f2- | tr -d "\"' " || true; } # --- safety gate: never let a *.test.sh script act on the production stack ---- if [ ! -f "$DOCKER_DIR/.env" ]; then echo "Error: $DOCKER_DIR/.env not found. Copy docker/.env.test.example to docker/.env and fill it in." >&2 exit 1 fi PROJECT="$(env_val COMPOSE_PROJECT_NAME)" STACK="$(env_val STACK_NAME)"; STACK="${STACK:-$PROJECT}" case "${PROJECT:-}" in ""|escapepage|docker) echo "Error: COMPOSE_PROJECT_NAME in docker/.env is '${PROJECT:-}'." >&2 echo "Refusing to run a test script against the production stack." >&2 echo "Set COMPOSE_PROJECT_NAME and STACK_NAME to e.g. 'escapepage-test' in docker/.env." >&2 exit 1 ;; esac # --- compose command ------------------------------------------------------- if docker compose version >/dev/null 2>&1; then DOCKER_COMPOSE="docker compose" elif command -v docker-compose >/dev/null 2>&1; then DOCKER_COMPOSE="docker-compose" else echo "Error: neither 'docker compose' nor 'docker-compose' is available." >&2 exit 1 fi command -v docker >/dev/null 2>&1 || { echo "Error: docker is required." >&2; exit 1; } dc() { (cd "$DOCKER_DIR" && $DOCKER_COMPOSE -p "$PROJECT" -f compose.yaml -f compose.override.yaml "$@"); } pexec() { dc exec -T php "$@"; } # as root (composer / npm) pexecwww() { dc exec -T -u www-data php "$@"; } # as www-data (console / DB / cache) REBUILD=1; RECREATE=0; DOWN_ONLY=0 for arg in "$@"; do case "$arg" in --no-build) REBUILD=0 ;; --recreate) RECREATE=1 ;; --down) DOWN_ONLY=1 ;; *) echo "Unknown option: $arg" >&2; exit 1 ;; esac done echo "Test stack: $STACK (compose project: $PROJECT)" if [ "$DOWN_ONLY" -eq 1 ]; then dc down --remove-orphans exit 0 fi BUILD_ARGS=() [ "$REBUILD" -eq 1 ] && BUILD_ARGS+=("--build") [ "$RECREATE" -eq 1 ] && BUILD_ARGS+=("--force-recreate") dc up -d "${BUILD_ARGS[@]}" # --- wait for the database ------------------------------------------------ printf "Waiting for database to be healthy..." for i in $(seq 1 60); do DB_ID=$(dc ps -q database 2>/dev/null || true) if [ -n "$DB_ID" ] && [ "$(docker inspect -f '{{.State.Health.Status}}' "$DB_ID" 2>/dev/null || true)" = "healthy" ]; then echo " OK"; break fi printf "."; sleep 2 [ "$i" -eq 60 ] && echo -e "\nWarning: database not healthy yet, continuing anyway." done # --- dependencies (root: writes vendor/ and node_modules/) -------------- pexec composer install --no-interaction # --- APP_SECRET: generate into .env.local if it's set nowhere ----------- if ! grep -qsE '^APP_SECRET=.+' "$ROOT_DIR/.env" "$ROOT_DIR/.env.local"; then echo "Generating APP_SECRET in .env.local..." printf 'APP_SECRET=%s\n' "$(openssl rand -hex 16)" >> "$ROOT_DIR/.env.local" fi # --- database (www-data: keeps var/ writable by php-fpm) --------------- echo "Creating database if it doesn't exist..." pexecwww php bin/console doctrine:database:create --if-not-exists || { echo "Error: database creation failed." >&2; dc logs database | tail -n 40; exit 1; } echo "Running migrations..." pexecwww php bin/console doctrine:migrations:migrate -n || { echo "Error: migrations failed." >&2; exit 1; } [ -f "$ROOT_DIR/importmap.php" ] && pexec php bin/console importmap:install || true if [ -f "$ROOT_DIR/package.json" ]; then echo "Installing npm dependencies..."; pexec npm ci || pexec npm install echo "Building assets..."; pexec npm run build fi # --- repair var/ ownership for php-fpm (www-data), never var/volumes ---- echo "Fixing var/ ownership for php-fpm..." pexec sh -lc 'mkdir -p var/cache var/log/php var/log/cron var/sessions && chown -R www-data:www-data var/cache var/log var/sessions' pexecwww php bin/console cache:clear NGINX_HTTPS="$(env_val NGINX_HTTPS_PORT)" MAILPIT_UI="$(env_val MAILPIT_UI_PORT)" cat <