mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-07 04:12:04 +00:00
The compose file runs Postgres 18. An install from v1.45 or earlier keeps its Postgres 16 data in `pgdata`, which 18 cannot open, so a one-shot `db-upgrade` service moves it on the first start: it starts Postgres 16 on the old volume inside its own container, streams a dump into 18 in one transaction, compares every table's row count, gathers planner statistics and marks the old volume as moved. The app waits for it to succeed. The old volume is never changed apart from the marker, so the way back is the previous image and volume lines. A move cut short leaves a "moving" marker and runs again from scratch; an emptied new volume after a finished move is refused rather than refilled with the old state. scripts/compose-db-upgrade-check.sh plays all of it against real containers from the v1.45.0 compose file, and runs in CI on every change to the compose file. Pull requests run the integration suite and e2e on Postgres 18; the nightly integration run keeps 16 covered for installs that bring their own database.
259 lines
10 KiB
YAML
259 lines
10 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.
|
||
# Every Playwright job runs inside Microsoft's Playwright image, which ships
|
||
# the browsers with their system libraries: installing them from apt at run
|
||
# time stalled shards for 29 minutes twice, once for WebKit and once for
|
||
# Chromium alone.
|
||
shard:
|
||
runs-on: ubuntu-24.04
|
||
needs: playwright-version
|
||
timeout-minutes: 30
|
||
container:
|
||
image: mcr.microsoft.com/playwright:v${{ needs.playwright-version.outputs.version }}-noble
|
||
strategy:
|
||
fail-fast: false
|
||
matrix:
|
||
shard: [1, 2, 3]
|
||
name: e2e shard ${{ matrix.shard }}/3
|
||
services:
|
||
postgres:
|
||
image: postgres:18-alpine
|
||
env:
|
||
POSTGRES_USER: healthlog
|
||
POSTGRES_PASSWORD: healthlog
|
||
POSTGRES_DB: healthlog
|
||
options: >-
|
||
--health-cmd "pg_isready -U healthlog"
|
||
--health-interval 5s
|
||
--health-timeout 5s
|
||
--health-retries 10
|
||
|
||
env:
|
||
# Inside a job container the service is reached by its name.
|
||
DATABASE_URL: postgresql://healthlog:healthlog@postgres: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
|
||
# The WebKit specs run in the `webkit` job below.
|
||
E2E_SKIP_WEBKIT: "1"
|
||
|
||
steps:
|
||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||
|
||
# The image runs as root while the checkout belongs to the runner user;
|
||
# git refuses such a tree, and the prepare hook calls git.
|
||
- run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
|
||
|
||
- 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
|
||
|
||
# `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
|
||
|
||
# The Playwright image must carry the browsers of the exact Playwright the
|
||
# lockfile resolves, or the run refuses them. Read the version from the
|
||
# lockfile so a dependency bump moves the image with it.
|
||
playwright-version:
|
||
runs-on: ubuntu-24.04
|
||
timeout-minutes: 5
|
||
outputs:
|
||
version: ${{ steps.read.outputs.version }}
|
||
steps:
|
||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||
with:
|
||
sparse-checkout: pnpm-lock.yaml
|
||
sparse-checkout-cone-mode: false
|
||
- id: read
|
||
run: |
|
||
version=$(grep -m1 -E "^ playwright-core@[0-9]" pnpm-lock.yaml | sed -E "s/^ playwright-core@([0-9.]+):.*/\1/")
|
||
if [ -z "$version" ]; then echo "playwright-core not found in pnpm-lock.yaml"; exit 1; fi
|
||
echo "version=$version" >> "$GITHUB_OUTPUT"
|
||
|
||
# The WebKit specs, in the same image as the shards: the bottom sheet on an
|
||
# iPhone (`bottom-sheet.spec.ts`, project `webkit-iphone`) and the app shell
|
||
# on an iPad (`tablet-shell.spec.ts`, project `webkit-tablet`).
|
||
webkit:
|
||
runs-on: ubuntu-24.04
|
||
needs: playwright-version
|
||
timeout-minutes: 30
|
||
name: e2e webkit
|
||
container:
|
||
image: mcr.microsoft.com/playwright:v${{ needs.playwright-version.outputs.version }}-noble
|
||
services:
|
||
postgres:
|
||
image: postgres:18-alpine
|
||
env:
|
||
POSTGRES_USER: healthlog
|
||
POSTGRES_PASSWORD: healthlog
|
||
POSTGRES_DB: healthlog
|
||
options: >-
|
||
--health-cmd "pg_isready -U healthlog"
|
||
--health-interval 5s
|
||
--health-timeout 5s
|
||
--health-retries 10
|
||
|
||
env:
|
||
# Inside a job container the service is reached by its name.
|
||
DATABASE_URL: postgresql://healthlog:healthlog@postgres: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
|
||
|
||
# The image runs as root while the checkout belongs to the runner user;
|
||
# git refuses such a tree, and the prepare hook calls git.
|
||
- run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
|
||
|
||
- 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
|
||
|
||
- run: NODE_OPTIONS=--max-old-space-size=8192 pnpm build
|
||
|
||
- run: pnpm exec playwright test --project=webkit-iphone --project=webkit-tablet
|
||
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-webkit
|
||
path: playwright-report/
|
||
retention-days: 7
|
||
if-no-files-found: ignore
|
||
|
||
- name: Upload Playwright JSON report
|
||
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||
with:
|
||
name: playwright-json-shard-webkit
|
||
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 every shard and the WebKit job: it fails when
|
||
# any of them failed or was cancelled, so a half-green run cannot satisfy
|
||
# the gate.
|
||
e2e:
|
||
runs-on: ubuntu-24.04
|
||
needs: [shard, webkit]
|
||
if: always()
|
||
timeout-minutes: 5
|
||
steps:
|
||
- name: Every shard finished green
|
||
run: |
|
||
if [ "${{ needs.shard.result }}" != "success" ] || [ "${{ needs.webkit.result }}" != "success" ]; then
|
||
echo "shard result: ${{ needs.shard.result }}, webkit result: ${{ needs.webkit.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
|