2026-06-15 00:14:42 +02:00
|
|
|
---
|
|
|
|
|
# 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
|
2026-06-15 00:57:22 +02:00
|
|
|
ports:
|
|
|
|
|
# HTTP-only in dev — no 443 mapping (SERVER_NAME ":80" means nothing listens there).
|
|
|
|
|
- "${HTTP_PORT:-8100}:80"
|
2026-06-15 00:14:42 +02:00
|
|
|
environment:
|
2026-06-15 00:57:22 +02:00
|
|
|
# HTTP-only in dev (no TLS, no HTTP→HTTPS redirect). App served on http://localhost:8100.
|
|
|
|
|
SERVER_NAME: ":80"
|
2026-06-15 00:14:42 +02:00
|
|
|
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
|
2026-06-30 21:07:45 +02:00
|
|
|
# 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
|
2026-06-15 00:14:42 +02:00
|
|
|
tty: true
|
2026-06-28 02:06:56 +02:00
|
|
|
|
|
|
|
|
# Standalone React/Vite SPA (dev only — not in compose.prod.yaml; see #33/#35).
|
|
|
|
|
frontend:
|
2026-06-28 18:29:11 +02:00
|
|
|
# Custom dev image = node:22-alpine with npm pinned (see frontend/Dockerfile.dev).
|
|
|
|
|
build:
|
|
|
|
|
context: ./frontend
|
|
|
|
|
dockerfile: Dockerfile.dev
|
|
|
|
|
args:
|
|
|
|
|
NPM_VERSION: ${NPM_VERSION}
|
2026-06-28 02:06:56 +02:00
|
|
|
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"
|
2026-06-28 18:29:11 +02:00
|
|
|
command: sh -c "npm install && npm run dev -- --host 0.0.0.0 --port 5173"
|
2026-06-28 02:06:56 +02:00
|
|
|
depends_on:
|
2026-06-30 21:07:45 +02:00
|
|
|
# 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.
|
2026-06-28 02:06:56 +02:00
|
|
|
- php
|
|
|
|
|
environment:
|
|
|
|
|
# Reliable file-watching under the bind-mount.
|
|
|
|
|
CHOKIDAR_USEPOLLING: "true"
|
2026-06-28 18:18:01 +02:00
|
|
|
# Marks the service healthy only once Vite is actually serving — the e2e
|
|
|
|
|
# runner waits on this (condition: service_healthy) to avoid racing startup.
|
2026-06-28 18:44:59 +02:00
|
|
|
# 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).
|
2026-06-28 18:18:01 +02:00
|
|
|
healthcheck:
|
2026-06-28 18:44:59 +02:00
|
|
|
test: ["CMD", "wget", "-qO-", "http://127.0.0.1:5173/"]
|
2026-06-28 18:18:01 +02:00
|
|
|
interval: 5s
|
|
|
|
|
timeout: 3s
|
|
|
|
|
retries: 5
|
|
|
|
|
start_period: 90s
|
2026-06-28 02:06:56 +02:00
|
|
|
tty: true
|
2026-06-28 18:04:02 +02:00
|
|
|
|
|
|
|
|
# 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:
|
2026-06-28 18:29:11 +02:00
|
|
|
# Custom image = the Playwright image with npm pinned (see e2e/Dockerfile).
|
|
|
|
|
build:
|
|
|
|
|
context: ./e2e
|
|
|
|
|
dockerfile: Dockerfile
|
|
|
|
|
args:
|
|
|
|
|
NPM_VERSION: ${NPM_VERSION}
|
2026-06-28 18:04:02 +02:00
|
|
|
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:
|
2026-06-28 18:18:01 +02:00
|
|
|
# Wait until Vite is actually serving, not just the container started.
|
|
|
|
|
frontend:
|
|
|
|
|
condition: service_healthy
|
2026-06-28 18:29:11 +02:00
|
|
|
# npm is pinned in the image (e2e/Dockerfile); just install deps + run.
|
|
|
|
|
command: sh -c "npm install && npm test"
|
2026-06-28 18:04:02 +02:00
|
|
|
|
2026-06-15 00:14:42 +02:00
|
|
|
###> symfony/mercure-bundle ###
|
|
|
|
|
###< symfony/mercure-bundle ###
|
2026-06-28 02:06:56 +02:00
|
|
|
|
|
|
|
|
volumes:
|
|
|
|
|
frontend_node_modules:
|
2026-06-28 18:04:02 +02:00
|
|
|
e2e_node_modules:
|