--- # Development environment override services: php: image: ${IMAGES_PREFIX:-}app-php-dev build: context: . target: frankenphp_dev volumes: - ./:/app - ./frankenphp/Caddyfile:/etc/frankenphp/Caddyfile:ro - ./frankenphp/conf.d/20-app.dev.ini:/usr/local/etc/php/app.conf.d/20-app.dev.ini:ro # Keep var/ off the bind-mount for faster I/O on Mac/Windows; comment to inspect from the host. - /app/var # If you develop on Mac or Windows you can remove the vendor/ directory # from the bind-mount for better performance by enabling the next line: #- /app/vendor ports: # HTTP-only in dev — no 443 mapping (SERVER_NAME ":80" means nothing listens there). - "${HTTP_PORT:-8100}:80" environment: # HTTP-only in dev (no TLS, no HTTP→HTTPS redirect). App served on http://localhost:8100. SERVER_NAME: ":80" FRANKENPHP_WORKER_CONFIG: watch FRANKENPHP_SITE_CONFIG: hot_reload MERCURE_EXTRA_DIRECTIVES: demo # See https://xdebug.org/docs/all_settings#mode XDEBUG_MODE: "${XDEBUG_MODE:-develop}" APP_ENV: "${APP_ENV:-dev}" extra_hosts: # Ensure that host.docker.internal is correctly defined on Linux - host.docker.internal:host-gateway # Marks php healthy only once the backend can serve a request AND reach the # DB (GET /api/health → {status:ok,db:ok}). Overrides the image's baked-in # HEALTHCHECK (which only probes Caddy's :2019/metrics = "web server alive", # no DB). This is observability only — nothing in the dev path *blocks* on it # (the e2e race-prevention lives in the CI e2e job, which waits on # /api/health before Playwright; gating `frontend` on it here only risks # hanging `dev-up`). # Use 127.0.0.1, NOT localhost: the container resolves localhost to ::1 # (IPv6) first, but FrankenPHP binds :80 on IPv4 → an IPv6 probe is refused # and the container never goes healthy (same gotcha as #52). The probe is a # SINGLE `php -r` expression: podman feeds the test args straight to the # binary and does NOT preserve a multi-statement script (newlines collapse → # parse error), so keep it to one expression — `str_contains` the body for # the healthy marker, exit 0/1. healthcheck: test: - CMD - php - -r - exit(str_contains((string) @file_get_contents("http://127.0.0.1:80/api/health"), "\"db\":\"ok\"") ? 0 : 1); interval: 10s timeout: 5s retries: 5 start_period: 30s tty: true # Standalone React/Vite SPA (dev only — not in compose.prod.yaml; see #33/#35). frontend: # Custom dev image = node:22-alpine with npm pinned (see frontend/Dockerfile.dev). build: context: ./frontend dockerfile: Dockerfile.dev args: NPM_VERSION: ${NPM_VERSION} working_dir: /app volumes: - ./frontend:/app # Named volume shadows the bind-mounted node_modules so native deps # (esbuild/rollup) are built for the container's arch, not the host's. - frontend_node_modules:/app/node_modules ports: - "${VITE_PORT:-5173}:5173" command: sh -c "npm install && npm run dev -- --host 0.0.0.0 --port 5173" depends_on: # Start php first, but DON'T gate on its health — a not-yet-ready backend # only means the Vite proxy 502s a few early /api calls until php boots, # which self-heals. Gating on `service_healthy` here turned a flaky/slow # php healthcheck into a `dev-up` hang. e2e (CI) does the real readiness # wait on /api/health itself before driving the browser. - php environment: # Reliable file-watching under the bind-mount. CHOKIDAR_USEPOLLING: "true" # Marks the service healthy only once Vite is actually serving — the e2e # runner waits on this (condition: service_healthy) to avoid racing startup. # Use 127.0.0.1, NOT localhost: the container resolves localhost to ::1 # (IPv6) first, but Vite binds 0.0.0.0 (IPv4 only) → an IPv6 probe is # refused and the container never goes healthy. start_period covers the # first boot (npm install of the app deps). healthcheck: test: ["CMD", "wget", "-qO-", "http://127.0.0.1:5173/"] interval: 5s timeout: 3s retries: 5 start_period: 90s tty: true # Playwright e2e runner (browsers + libs preinstalled). On-demand only — opt # in with `--profile e2e`, so it never starts with a plain `dev-up`. Reaches # the SPA by service name on the compose network (frontend:5173), so no host # browsers are needed (NixOS host can't run Chromium natively). Image tag is # pinned to the @playwright/test version in e2e/package.json — keep in sync. playwright: # Custom image = the Playwright image with npm pinned (see e2e/Dockerfile). build: context: ./e2e dockerfile: Dockerfile args: NPM_VERSION: ${NPM_VERSION} profiles: [e2e] working_dir: /e2e volumes: - ./e2e:/e2e - e2e_node_modules:/e2e/node_modules environment: # Target the SPA by compose service name, not localhost. E2E_BASE_URL: "http://frontend:5173" depends_on: # Wait until Vite is actually serving, not just the container started. frontend: condition: service_healthy # npm is pinned in the image (e2e/Dockerfile); just install deps + run. command: sh -c "npm install && npm test" ###> symfony/mercure-bundle ### ###< symfony/mercure-bundle ### volumes: frontend_node_modules: e2e_node_modules: