fix(08-03): keep API alive during harness — start API + run e2e in one step
CI / fast-checks (pull_request) Successful in 49s
CI / api (pull_request) Successful in 56s
CI / harness (pull_request) Failing after 2m50s

Run #7 harness failed: global-setup polled :5173/health (Vite proxy → :3000)
and never got 200. The API connected to the DB and :3000/health was green
during the separate 'Wait for API' step, but the bare-backgrounded node
process was reaped at the step boundary and was dead by the time the e2e step
ran (after the multi-minute browser install). Confirmed locally the API does
not self-crash (alive + healthy for 75s in-shell).

Install browsers first, then start the API and run Playwright in a SINGLE step
so the API stays a child of the test shell for the whole run; capture the test
exit code and kill the API after. No harness files touched.
This commit is contained in:
Lucas Berger
2026-06-11 15:05:04 -04:00
parent 71c89093b1
commit 53a989c3fb
+33 -40
View File
@@ -212,47 +212,25 @@ jobs:
- name: Build API - name: Build API
run: pnpm --filter @familysync/api build run: pnpm --filter @familysync/api build
# Start the API as a background process. # Install Playwright browsers with system deps BEFORE starting the API, so the long
# DEV_AUTH_BYPASS=true is passed INLINE on the node line (Pitfall 8 — env inheritance # browser download does not run during the API's lifetime.
# across & steps is not guaranteed in all runner modes). NODE_ENV=development is required:
# global-setup.ts refuses NODE_ENV=production, and the API devBypass.ts checks development.
- name: Start API background process
run: |
NODE_ENV=development DEV_AUTH_BYPASS=true DB_HOST=$DB_HOST DB_PORT=3306 DB_USER=familysync DB_PASSWORD=testpass DB_NAME=familysync node apps/api/dist/index.js &
echo $! > /tmp/api.pid
echo "API PID: $(cat /tmp/api.pid)"
# Wait for the API :3000/health before Playwright starts (D-02 / T-08-08).
# This step-level wait is SEPARATE from global-setup.ts's poll — global-setup runs AFTER
# Playwright starts and polls through the Vite proxy. The step-level wait ensures the API
# is up before Playwright even attempts to start Vite. ~60s deadline.
- name: Wait for API (:3000/health)
run: |
deadline=$((SECONDS + 60))
until curl -sf http://localhost:3000/health > /dev/null 2>&1; do
if [ $SECONDS -ge $deadline ]; then
echo "API did not start within 60s"
kill $(cat /tmp/api.pid) 2>/dev/null || true
exit 1
fi
sleep 2
done
echo "API ready at :3000"
# Install Playwright browsers with system deps.
# Must run from apps/pwa/ where @playwright/test is installed (D-PROBE-05 confirmed exit 0). # Must run from apps/pwa/ where @playwright/test is installed (D-PROBE-05 confirmed exit 0).
# Do NOT cache browser binaries — Playwright explicitly recommends against it in CI. # Do NOT cache browser binaries — Playwright explicitly recommends against it in CI.
- name: Install Playwright browsers - name: Install Playwright browsers
run: npx playwright install --with-deps webkit chromium run: npx playwright install --with-deps webkit chromium
working-directory: apps/pwa working-directory: apps/pwa
# Run the Phase 7 Playwright harness across both device profiles (iphone + pixel). # Start the API AND run the harness in ONE step. A bare `node &` started in an EARLIER
# CI=true makes Playwright: start Vite :5173 itself (reuseExistingServer=false), # step is reaped at the step boundary: CI run #7 proved :3000 was healthy during a
# use retries:2/workers:1, and apply reporter:'github' from config — which we # separate "wait" step but dead by the time global-setup polled :5173/health → :3000
# override with --reporter=list,html because Gitea does not render github annotations # (after the multi-minute browser install). Keeping the API a child of THIS step's shell
# (Pitfall 5 / D-06). Both projects run by default (no --project filter). # guarantees it stays alive for the entire Playwright run.
- name: Run Playwright harness (iphone + pixel) # DEV_AUTH_BYPASS=true + NODE_ENV=development are set both inline and in env: — global-setup.ts
run: pnpm test:e2e -- --reporter=list,html # refuses NODE_ENV=production and the API devBypass.ts checks development. DB_* come from env:.
# CI=true makes Playwright start Vite :5173 itself (reuseExistingServer=false), use
# retries:2/workers:1, and apply reporter:'github' — which --reporter=list,html overrides
# because Gitea does not render github annotations (Pitfall 5 / D-06). Both projects run.
- name: Run harness (start API + Playwright iphone + pixel)
env: env:
CI: 'true' CI: 'true'
PLAYWRIGHT_BASE_URL: http://localhost:5173 PLAYWRIGHT_BASE_URL: http://localhost:5173
@@ -263,6 +241,26 @@ jobs:
DB_USER: familysync DB_USER: familysync
DB_PASSWORD: testpass DB_PASSWORD: testpass
DB_NAME: familysync DB_NAME: familysync
run: |
NODE_ENV=development DEV_AUTH_BYPASS=true node apps/api/dist/index.js &
API_PID=$!
echo "API PID: $API_PID"
# Wait for the API :3000/health before launching Playwright (D-02 / T-08-08).
deadline=$((SECONDS + 60))
until curl -sf http://localhost:3000/health > /dev/null 2>&1; do
if ! kill -0 "$API_PID" 2>/dev/null; then echo "API process exited before becoming ready"; exit 1; fi
if [ $SECONDS -ge $deadline ]; then echo "API did not become ready within 60s"; kill "$API_PID" 2>/dev/null || true; exit 1; fi
sleep 2
done
echo "API ready at :3000"
# Run the Phase 7 harness across both profiles; preserve its exit code, always kill the API.
set +e
pnpm test:e2e -- --reporter=list,html
rc=$?
kill "$API_PID" 2>/dev/null || true
exit $rc
# Upload traces/screenshots/videos on failure for debugging (D-06). # Upload traces/screenshots/videos on failure for debugging (D-06).
# MUST use ChristopherHX/gitea-upload-artifact@v4 — the standard upload-artifact action # MUST use ChristopherHX/gitea-upload-artifact@v4 — the standard upload-artifact action
@@ -274,8 +272,3 @@ jobs:
name: playwright-traces-${{ github.run_id }} name: playwright-traces-${{ github.run_id }}
path: apps/pwa/test-results/ path: apps/pwa/test-results/
retention-days: 14 retention-days: 14
# Always kill the API background process to clean up, even on success.
- name: Kill API background process
if: always()
run: kill $(cat /tmp/api.pid) 2>/dev/null || true