Files
HealthLog/.github/workflows/e2e.yml
T
Marc-André Bombeck 95f8fbb1f2 ci: run e2e in three shards with a 30-minute cap
Two shards took 14 to 19 minutes against a 20-minute cap after the mobile
route sweep landed, and a slow runner cancelled one twice in a week.
2026-10-02 00:28:57 +02:00

158 lines
6.0 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
name: e2e
on:
pull_request:
branches: [main]
push:
branches: [main]
permissions:
contents: read
# Cancel in-progress runs when a new commit lands on the same PR.
concurrency:
group: e2e-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
# The suite outgrew the runner the same way the integration suite did: the
# single job passed at ~18 minutes against a 20-minute cap, and the settings
# geometry measurements pushed it over. Same remedy, split rather than raise:
# Playwright shards, each with its own database and production build, and
# a gate job below that carries the exact name branch protection requires.
# Two shards grew to 14–19 minutes against the 20-minute cap once the
# mobile route sweep landed, and a slow runner tipped them over; three
# shards with a 30-minute cap leave room for both.
shard:
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
shard: [1, 2, 3]
name: e2e shard ${{ matrix.shard }}/3
services:
postgres:
image: postgres:16-alpine
env:
POSTGRES_USER: healthlog
POSTGRES_PASSWORD: healthlog
POSTGRES_DB: healthlog
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -U healthlog"
--health-interval 5s
--health-timeout 5s
--health-retries 10
env:
DATABASE_URL: postgresql://healthlog:healthlog@localhost:5432/healthlog?schema=public
ENCRYPTION_KEYS: '{"v1":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}'
ENCRYPTION_ACTIVE_KEY_ID: v1
API_TOKEN_HMAC_KEY: bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
AUTH_RP_NAME: HealthLog
AUTH_RP_ID: localhost
AUTH_RP_ORIGIN: http://localhost:3000
SESSION_COOKIE_SECURE: "false"
NODE_ENV: production
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 22
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm db:generate
- run: pnpm db:migrate:deploy
- name: Cache Playwright browsers
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.cache/ms-playwright
key: playwright-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
restore-keys: |
playwright-${{ runner.os }}-
- run: pnpm exec playwright install --with-deps chromium
# `pnpm build` (not bare `next build`): the prebuild hook generates
# the versioned service-worker stamp AND the static i18n catalog
# assets (public/i18n/<locale>.js) the client boots from — without
# them every UI interaction waits on missing catalogs.
# The repository-wide Next type worker exceeds V8's ~4 GiB default
# old-space ceiling. Keep the larger heap scoped to this production
# build only; tests and the runtime retain their normal limits.
- run: NODE_OPTIONS=--max-old-space-size=8192 pnpm build
# Client-bundle budget gate — rides this job because it is the one
# place CI already has a production build on disk. Catches the
# regressions the dead webpack-era tooling (ANALYZE=1, the old
# bundle-report) let through: a statically imported message catalog
# (~100 KB gz on every route) or a chart import bypassing the shared
# chart-runtime boundary (a duplicated ~92 KB gz recharts chunk).
- run: pnpm bundle-check
- run: pnpm exec playwright test --shard=${{ matrix.shard }}/3
env:
E2E_BASE_URL: http://localhost:3000
- name: Upload Playwright HTML report
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: playwright-report-shard-${{ matrix.shard }}
path: playwright-report/
retention-days: 7
if-no-files-found: ignore
# The machine-readable report, for the skipped-spec check in the gate
# job below. Only uploaded when the shard ran to completion, so a missing
# artifact there means a shard that did not finish, not an empty suite.
- name: Upload Playwright JSON report
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: playwright-json-shard-${{ matrix.shard }}
path: playwright-json/results.json
retention-days: 7
if-no-files-found: error
# Branch protection requires a check named exactly "e2e". This job carries
# that name and answers for both shards: it fails when any shard failed or
# was cancelled, so a half-green matrix cannot satisfy the gate.
e2e:
runs-on: ubuntu-latest
needs: shard
if: always()
timeout-minutes: 5
steps:
- name: Every shard finished green
run: |
if [ "${{ needs.shard.result }}" != "success" ]; then
echo "shard result: ${{ needs.shard.result }}"
exit 1
fi
# A spec file that skipped every one of its tests reports green. The
# check aggregates both shards' reports, because Playwright splits a
# file's tests across shards; see the script's header for the allowlist.
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
sparse-checkout: scripts
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 22
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
pattern: playwright-json-shard-*
path: playwright-json
- name: No spec file skipped all of its tests
run: node scripts/check-e2e-skipped-specs.mjs playwright-json/*/results.json