diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index b67fb0f6b..f12d14d7a 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -21,11 +21,8 @@ env: # Forces playwright.config.ts:141,155 to spawn fresh backend + Vite # instead of reusing whatever is on the runner. CI: "true" - # docker-compose.yml has `${JWT_SECRET:?required}` interpolation on - # the backend-api service. Compose validates the WHOLE file even when - # we `up -d` only Postgres / Redis / RabbitMQ — so JWT_SECRET must be - # in env at every step that runs `docker compose ...`. # Falls back to a CI-only dev key if the Forgejo secret is unset. + # Used at the "Build + start backend API" step. JWT_SECRET: ${{ secrets.E2E_JWT_SECRET || 'ci-dev-jwt-secret-32-chars-min-padding!!' }} jobs: @@ -38,6 +35,56 @@ jobs: name: e2e (${{ github.event_name == 'pull_request' && '@critical' || 'full' }}) runs-on: ubuntu-latest timeout-minutes: ${{ github.event_name == 'pull_request' && 20 || 45 }} + + # Service containers are managed by act_runner: spawned on the job + # network with healthchecks, torn down at the end. This replaces + # the previous `docker compose up -d` pattern which relied on + # docker socket sharing + host port mappings — fragile (port + # collisions across concurrent jobs, manual cleanup, double-DinD, + # whole compose file validated even when only 3 services are + # needed). Service hostnames (`postgres`, `redis`, `rabbitmq`) + # resolve from the job container on standard ports. + services: + postgres: + image: postgres:16-alpine + env: + POSTGRES_USER: veza + POSTGRES_PASSWORD: devpassword + POSTGRES_DB: veza + options: >- + --health-cmd "pg_isready -U veza" + --health-interval 5s + --health-timeout 3s + --health-retries 10 + redis: + # Match docker-compose.yml (REM-023: password required even + # in dev). Default redis:7-alpine entrypoint reads + # REDIS_ARGS, so requirepass works without a `command:`. + image: redis:7-alpine + env: + REDIS_ARGS: "--requirepass devpassword" + options: >- + --health-cmd "redis-cli -a devpassword ping" + --health-interval 5s + --health-timeout 3s + --health-retries 10 + rabbitmq: + image: rabbitmq:3-management-alpine + env: + RABBITMQ_DEFAULT_USER: veza + RABBITMQ_DEFAULT_PASS: devpassword + options: >- + --health-cmd "rabbitmq-diagnostics -q check_port_connectivity" + --health-interval 10s + --health-timeout 5s + --health-retries 10 + + # Service hostnames + standard ports — no host-port mapping needed. + env: + DATABASE_URL: postgresql://veza:${{ secrets.E2E_DB_PASSWORD || 'devpassword' }}@postgres:5432/veza?sslmode=disable + REDIS_URL: redis://:devpassword@redis:6379 + RABBITMQ_URL: ${{ secrets.E2E_RABBITMQ_URL || 'amqp://veza:devpassword@rabbitmq:5672/' }} + steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 @@ -69,29 +116,12 @@ jobs: ./scripts/generate-jwt-keys.sh ./scripts/generate-ssl-cert.sh - - name: Start backend services (Postgres, Redis, RabbitMQ) - run: | - docker compose up -d postgres redis rabbitmq - echo "Waiting for Postgres..." - for i in $(seq 1 30); do - if docker exec veza_postgres pg_isready -U veza 2>/dev/null; then - echo "Postgres ready" - break - fi - sleep 2 - done - docker compose ps - - name: Run database migrations - env: - DATABASE_URL: postgresql://veza:devpassword@localhost:15432/veza?sslmode=disable run: | cd veza-backend-api go run cmd/migrate_tool/main.go - name: Seed database (CI mode — 5 test accounts + minimal fixtures) - env: - DATABASE_URL: postgresql://veza:devpassword@localhost:15432/veza?sslmode=disable run: | cd veza-backend-api go run ./cmd/tools/seed --ci @@ -100,12 +130,8 @@ jobs: env: APP_ENV: test APP_PORT: "18080" - DATABASE_URL: postgresql://veza:${{ secrets.E2E_DB_PASSWORD || 'devpassword' }}@localhost:15432/veza?sslmode=disable - REDIS_URL: redis://localhost:16379 - # JWT_SECRET inherits from workflow-level env (above). COOKIE_SECURE: "false" CORS_ALLOWED_ORIGINS: http://veza.fr:5174,http://localhost:5174 - RABBITMQ_URL: ${{ secrets.E2E_RABBITMQ_URL || 'amqp://guest:guest@localhost:5672/' }} DISABLE_RATE_LIMIT_FOR_TESTS: "true" RATE_LIMIT_LIMIT: "10000" RATE_LIMIT_WINDOW: "60"