From e18cd086fde2acb05c8ce04d8e39d48a3d9ad9fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Thu, 22 Oct 2026 13:44:10 +0200 Subject: [PATCH] ci(e2e): run the WebKit spec in the Playwright image, keep the shards on Chromium Installing WebKit's system packages in every shard stalled one shard for 29 minutes on apt and left another without time for its tests. The bottom-sheet spec now runs in its own job inside Microsoft's Playwright image, whose tag follows the playwright-core version the lockfile resolves; the shards install Chromium only and skip the WebKit project. The e2e gate answers for the shards and the WebKit job. --- .github/workflows/e2e.yml | 112 +++++++++++++++++++++++++++++++++++--- playwright.config.ts | 30 ++++++---- 2 files changed, 122 insertions(+), 20 deletions(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 5a21adfa6..cc008d5ce 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -56,6 +56,9 @@ jobs: AUTH_RP_ORIGIN: http://localhost:3000 SESSION_COOKIE_SECURE: "false" NODE_ENV: production + # WebKit runs in the `webkit` job below; installing its ~180 system + # packages here once stalled a shard past its 30-minute cap. + E2E_SKIP_WEBKIT: "1" steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 @@ -81,9 +84,7 @@ jobs: restore-keys: | playwright-${{ runner.os }}- - # WebKit for the one phone spec that needs Safari's engine - # (`bottom-sheet.spec.ts`, the `webkit-iphone` project). - - run: pnpm exec playwright install --with-deps chromium webkit + - 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 @@ -126,19 +127,114 @@ jobs: 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 spec (`bottom-sheet.spec.ts`, project `webkit-iphone`) in + # Microsoft's Playwright image, which ships WebKit with its system + # libraries, so nothing is installed from apt at run time. + 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:16-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 + HOME: /root + + 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 + + - run: NODE_OPTIONS=--max-old-space-size=8192 pnpm build + + - run: pnpm exec playwright test --project=webkit-iphone + 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 both shards: it fails when any shard failed or - # was cancelled, so a half-green matrix cannot satisfy the gate. + # 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 + needs: [shard, webkit] if: always() timeout-minutes: 5 steps: - name: Every shard finished green run: | - if [ "${{ needs.shard.result }}" != "success" ]; then - echo "shard result: ${{ needs.shard.result }}" + if [ "${{ needs.shard.result }}" != "success" ] || [ "${{ needs.webkit.result }}" != "success" ]; then + echo "shard result: ${{ needs.shard.result }}, webkit result: ${{ needs.webkit.result }}" exit 1 fi diff --git a/playwright.config.ts b/playwright.config.ts index 969480f97..9a53b27c1 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -308,18 +308,24 @@ export default defineConfig({ colorScheme: "dark", }, }, - { - // The bottom sheet's drag, on the engine it is for: iOS Safari is - // WebKit, and Chromium's touch scrolling and pointer capture behave - // differently enough that a pass there proves little about an iPhone. - // One spec only, so the extra browser costs the shards seconds. - name: "webkit-iphone", - testMatch: ["bottom-sheet.spec.ts"], - use: { - ...devices["iPhone 13"], - colorScheme: "dark", - }, - }, + // The bottom sheet's drag, on the engine it is for: iOS Safari is + // WebKit, and Chromium's touch scrolling and pointer capture behave + // differently enough that a pass there proves little about an iPhone. + // CI runs it in its own job inside the Playwright image, where WebKit's + // system libraries are already present; the Chromium shards set + // E2E_SKIP_WEBKIT so they neither install WebKit nor pick the spec up. + ...(process.env.E2E_SKIP_WEBKIT + ? [] + : [ + { + name: "webkit-iphone", + testMatch: ["bottom-sheet.spec.ts"], + use: { + ...devices["iPhone 13"], + colorScheme: "dark" as const, + }, + }, + ]), ], // Spin up the production build for E2E. `pnpm build` is run