mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 20:42:43 +00:00
The gate summed every emitted chunk once and watched five pages. The sum counts a module once per chunk group it lands in, so it rose whenever a page got lighter by moving code into on-demand chunks, and it needed a raise in most releases while no reader downloaded more; and a heavy import into a page outside the five went unseen. scripts/check-bundle-budget.mjs now measures: - the first load of every page: rootMainFiles plus the page's client-reference chunks, each file counted once by content hash; - p50, p95 and max of those first loads across pages; - the distinct client code: every chunk evaluated in a node:vm sandbox with a stub TURBOPACK array, each module factory counted once by its module id (not its text: each chunk is minified on its own, so copies of one module differ in mangled names), catalogs excluded, gzipped once. The summed aggregate is still printed but no longer gated. The catalog, per-catalog and one-Recharts checks are unchanged. `--write-baselines` rewrites the per-page baselines, `--dir` measures another build directory, `--json` writes the report. bundle-budget.json is re-based from the v1.44.0 build (local arm64): 110 pages, first load p50 336, p95 437, max 451 KB gz; distinct client code 1904 KB gz in 1224 modules; summed aggregate 3875 KB for comparison. Every page has a baseline with the same 20 KB allowance; caps are p95 447, max 461 and distinct code 1930 KB. A page without a baseline fails until one is written in the same change. Proof: importing zod into the weight insights page measured /insights/weight at 427.8 KB gz, 88.8 KB over its 339 KB baseline, and the gate failed; the old gate passed the same build, because the page was not watched and the summed aggregate did not move (3875 KB). The import was removed again.
159 lines
6.0 KiB
YAML
159 lines
6.0 KiB
YAML
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-24.04
|
||
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@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.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. Measures the first
|
||
# load of every page (files deduplicated by content hash) against its
|
||
# baseline, the p95 and max across pages, and the distinct client code
|
||
# (modules deduplicated by id); see the script header. Catches a
|
||
# statically imported message catalog, a duplicated recharts chunk, or
|
||
# a heavy module (Zod) pulled into a page that had none.
|
||
- 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@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||
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@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||
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-24.04
|
||
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@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
|
||
with:
|
||
node-version: 22
|
||
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
|
||
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
|