--- phase: 08-gitea-ci plan: 03 subsystem: testing tags: [playwright, ci, gitea, mariadb, webkit, chromium, dev-auth-bypass] # Dependency graph requires: - phase: 07-mobile-test-harness provides: Phase 7 Playwright specs (both device profiles) run unchanged in CI - phase: 08-02 provides: ci.yml with fast-checks + api jobs; runner-mode (ubuntu-latest, Docker-executor, services:) provides: - harness job in .gitea/workflows/ci.yml bringing up the full dev stack in CI and running the Phase 7 mobile specs across both device profiles - four infrastructure fixes resolving API-reap, IPv6/IPv4 mismatch, missing dev-user seed, and double-pnpm reporter forwarding affects: [08-04, phase-09, phase-10, phase-11, phase-12] # Tech tracking tech-stack: added: [] patterns: - 'Combine API-start + readiness + test run in a single CI step so the API is not reaped at a step boundary' - 'NODE_OPTIONS=--dns-result-order=ipv4first when Vite is IPv4-only and the runner resolves localhost to ::1 first' - 'Idempotent seed step (INSERT IGNORE) for the DEV_AUTH_BYPASS user before global-setup runs — FK chain requires it' - 'Call the PWA test:e2e script directly with --filter instead of root test:e2e -- -- to avoid double-pnpm arg forwarding' key-files: created: [] modified: - .gitea/workflows/ci.yml key-decisions: - "FIX-1 (53a989c): Start API + run e2e in a single step — bare 'node &' in an early step is reaped when that step exits; the API must remain a child of the test shell through the entire Playwright run" - 'FIX-2 (7389740): Use PLAYWRIGHT_BASE_URL=http://127.0.0.1:5173 and NODE_OPTIONS=--dns-result-order=ipv4first — Vite binds IPv4-only; Node fetch does not fall back from ::1 to 127.0.0.1 unlike curl' - 'FIX-3 (e486c6b): Seed dev user id=1 (INSERT IGNORE) after migrate, before API start — DEV_AUTH_BYPASS injects the user in-memory only; on a fresh CI DB the FK constraint silently aborted the calendars seed' - "FIX-4 (03e8088): Call 'pnpm --filter @familysync/pwa test:e2e --reporter=list,html' directly — 'pnpm test:e2e -- --reporter=list,html' double-forwards '--' through two pnpm layers; Playwright treats --reporter as a test-file filter and finds no tests" - 'Phase 7 harness files (playwright.config.ts, global-setup.ts, vite.config.ts, all specs) were NOT modified — CI owns stack bring-up only (D-01/D-02 phase boundary held)' - "Reporter override --reporter=list,html kept: Gitea does not render 'github' annotations; list output is legible in the log" patterns-established: - 'Harness step pattern: install browsers, then start API + wait for :3000/health, then run Playwright — all in one step' - "Idempotent user seed step: INSERT IGNORE + upsert pattern for DEV_AUTH_BYPASS user before global-setup's FK-dependent seeds" requirements-completed: [CI-01] # Metrics duration: ~2h (including CI iteration across 4 infrastructure fixes) completed: 2026-06-11 --- # Phase 08 Plan 03: Harness CI Job Summary **Gitea Actions harness job brings up MariaDB + API (DEV_AUTH_BYPASS) + Playwright Vite on every PR and runs 58 Phase 7 specs across iPhone/WebKit + Pixel/Chromium in 1.6 min — four infrastructure fixes required, no harness file modified** ## Performance - **Duration:** ~2h (task authoring + 4 CI fix iterations) - **Started:** 2026-06-11 - **Completed:** 2026-06-11 - **Tasks:** 2 auto + 1 checkpoint (human-verified) - **Files modified:** 1 (.gitea/workflows/ci.yml) ## Accomplishments - Harness job added to ci.yml: MariaDB 11 service → migrate → seed dev user → API background (DEV_AUTH_BYPASS=true, :3000) → Playwright starts Vite (:5173) → both iphone (WebKit) + pixel (Chromium) profiles → traces upload on failure - 58 Phase 7 specs passed green on Gitea Actions run #11 (PR #3, pull_request) — cold CI stack, 1.6 min - All four CI-side infrastructure fixes resolved without touching any Phase 7 harness file (phase boundary D-01/D-02 held) - Artifact upload confirmed working: playwright-traces-10 downloaded from Gitea UI on run #10 ## Task Commits 1. **Task 1: Add harness job — DB + migrate + API background + :3000 readiness** - `d55e347` (feat) 2. **Task 2: Add Playwright install + run (both profiles) + artifact upload** - `71c8909` (feat) 3. **Fix 1: Keep API alive during harness — start API + run e2e in one step** - `53a989c` (fix) 4. **Fix 2: Harness uses 127.0.0.1 + ipv4first — Vite is IPv4-only** - `7389740` (fix) 5. **Fix 3: Seed dev user id=1 — global-setup assumes it exists** - `e486c6b` (fix) 6. **Fix 4: Call pwa test:e2e directly so --reporter forwards cleanly** - `03e8088` (fix) ## Files Created/Modified - `.gitea/workflows/ci.yml` — harness job added; fast-checks + api jobs unchanged ## Decisions Made **D-08-03-COMBINE-STEP:** API start + readiness wait + `pnpm test:e2e` run combined into a single CI step. When the API was started with `node &` in a standalone step, the backgrounded process was reaped when that step exited — the multi-minute browser install that followed caused the API to die before the test step. Confirmed the API does not self-crash when left as a background child of the test shell. **D-08-03-IPV4FIRST:** `PLAYWRIGHT_BASE_URL=http://127.0.0.1:5173` and `NODE_OPTIONS=--dns-result-order=ipv4first` set on the harness step. The Gitea runner resolves `localhost` to `::1` (IPv6) first; Vite binds IPv4-only (`127.0.0.1:5173`); Node `fetch` does not fall back to IPv4 unlike `curl`. Proven: `[::1]:5173 ECONNREFUSED` vs `127.0.0.1:5173 200`. The API is dual-stack so its `localhost:3000` references were unaffected. **D-08-03-SEED-USER:** An idempotent "Seed dev user (id=1)" step runs after `db:migrate` and before the API starts. `DEV_AUTH_BYPASS` in `devBypass.ts` injects the user entirely in-memory — on a fresh CI database there is no `users` row, so the `global-setup.ts` `INSERT IGNORE INTO calendars` silently fails on the FK constraint and calendar id=10 is absent, causing a cascade FK error on `calendar_events`. The seed is `INSERT IGNORE INTO users (id, oidc_iss, oidc_sub, display_name, color) VALUES (1, 'dev', 'dev-user', 'Dev User', '#4A90D9')`. **D-08-03-REPORTER-FORWARD:** The root `test:e2e` script is `pnpm --filter @familysync/pwa test:e2e`. Calling `pnpm test:e2e -- --reporter=list,html` from the root passes `--` through two pnpm layers, resulting in `playwright test -- --reporter=list,html` where `--reporter=list,html` is treated as a test-file path filter — Playwright finds no tests. Fix: call `pnpm --filter @familysync/pwa test:e2e --reporter=list,html` directly. Validated: 58 specs listed vs 0 with the broken invocation. ## Deviations from Plan ### Auto-fixed Issues (all Rule 3 — blocking) **1. [Rule 3 - Blocking] API reaped at step boundary** - **Found during:** CI run after Task 1+2 commits - **Issue:** Bare `node apps/api/dist/index.js &` in an early step was reaped when that step exited. The browser install (multi-minute) ran next, then the test step found no API. - **Fix:** Merged API start + curl :3000/health readiness loop + `pnpm test:e2e` into a single step; moved browser install to the step immediately before it. - **Files modified:** .gitea/workflows/ci.yml - **Committed in:** 53a989c **2. [Rule 3 - Blocking] global-setup ECONNREFUSED on Vite :5173** - **Found during:** CI run post fix 1 - **Issue:** `global-setup.ts` fetched `${PLAYWRIGHT_BASE_URL}/health`; `PLAYWRIGHT_BASE_URL` defaulted to `http://localhost:5173`; runner resolved `localhost` → `::1`; Vite bound only `127.0.0.1:5173` → `ECONNREFUSED`. - **Fix:** Added `PLAYWRIGHT_BASE_URL: http://127.0.0.1:5173` and `NODE_OPTIONS: --dns-result-order=ipv4first` to the harness step env. - **Files modified:** .gitea/workflows/ci.yml - **Committed in:** 7389740 **3. [Rule 3 - Blocking] Missing dev user id=1 causes FK error in global-setup seed** - **Found during:** CI run post fix 2 - **Issue:** `global-setup.ts` seeds `calendars` + `calendar_events` for `user_id=1`. `DEV_AUTH_BYPASS` injects that user in-memory only (no DB row). On a fresh CI DB, the `INSERT IGNORE INTO calendars` silently aborted on the `users` FK; calendar id=10 was absent; the `calendar_events` insert then failed on the calendars FK. - **Fix:** Added a "Seed dev user" step after `db:migrate`: `INSERT IGNORE INTO users` with `id=1, oidc_iss='dev', oidc_sub='dev-user', display_name='Dev User', color='#4A90D9'`. - **Files modified:** .gitea/workflows/ci.yml - **Committed in:** e486c6b **4. [Rule 3 - Blocking] --reporter flag treated as test-file filter** - **Found during:** CI run post fix 3 - **Issue:** `pnpm test:e2e -- --reporter=list,html` from the workspace root double-forwarded `--` through two pnpm invocations, delivering `playwright test -- --reporter=list,html`; Playwright interpreted `--reporter=list,html` as a test-file path and found no tests. - **Fix:** Changed invocation to `pnpm --filter @familysync/pwa test:e2e --reporter=list,html` — bypasses the root script delegation entirely. - **Files modified:** .gitea/workflows/ci.yml - **Committed in:** 03e8088 --- **Total deviations:** 4 auto-fixed (all Rule 3 — blocking CI failures). All were infrastructure/orchestration issues. No Phase 7 harness files (playwright.config.ts, global-setup.ts, vite.config.ts, or any spec) were modified. ## Verified CI Result **Gitea Actions run #11** (PR #3, `pull_request` event) — conclusion **SUCCESS** - **Harness result:** 58 passed in 1.6 min - **Profiles:** iphone (WebKit) + pixel (Chromium), both passing - **Co-running jobs:** fast-checks (191 PWA tests) + api (238 API tests) — all green in the same run - **Artifact upload:** Confirmed working on run #10 — `playwright-traces-10` uploaded with a download URL via `ChristopherHX/gitea-upload-artifact@v4` - **Phase boundary:** Zero Phase 7 files modified — confirmed via `git diff --quiet -- apps/pwa/playwright.config.ts apps/pwa/e2e/global-setup.ts apps/pwa/vite.config.ts` ## CI Stack Bring-Up Order (confirmed working) 1. `services: mariadb:11` container (DB_HOST=mariadb, Docker-executor mode) 2. `actions/checkout@v4` + `setup-node@v4` (Node 22) + `corepack enable pnpm` 3. `pnpm install --frozen-lockfile` 4. mysql2 readiness loop until mariadb port 3306 accepts connections 5. `pnpm --filter @familysync/api db:migrate` (never push — verified no `db:push` in ci.yml) 6. Seed dev user id=1 (INSERT IGNORE — idempotent) 7. `pnpm --filter @familysync/api build` → dist/index.js 8. `npx playwright install --with-deps webkit chromium` (from apps/pwa working-directory) 9. Combined step: `NODE_ENV=development DEV_AUTH_BYPASS=true ... node apps/api/dist/index.js &` → curl :3000/health readiness loop → `pnpm --filter @familysync/pwa test:e2e --reporter=list,html` 10. `if: failure()` — artifact upload via `ChristopherHX/gitea-upload-artifact@v4` 11. `if: always()` — kill API background process ## Issues Encountered WebKit deps install: clean exit 0 — confirmed on this runner (D-PROBE-05 from plan 01, re-verified here). No issues encountered. Reporter legibility: `list` reporter produced readable per-test output in the Gitea log; `html` report built but is only accessible via artifact download. ## Known Stubs None. ## Threat Flags None — no new network endpoints or auth paths introduced. The `DEV_AUTH_BYPASS=true` flag is scoped to the harness job only; it does not appear in the publish job (Plan 04). Threat mitigations T-08-06, T-08-07, T-08-08 confirmed implemented. ## Next Phase Readiness - Plan 04 (publish job) is unblocked: harness green, CI-01 harness half complete - ROADMAP CI-01 criteria 3 (failure gates merge) and 4 (readiness waits) satisfied - Plan 04 needs `GITEA_REGISTRY_PAT` (deferred D-PROBE-08) — operator must create the PAT before the publish step can push to the Gitea container registry --- _Phase: 08-gitea-ci_ _Completed: 2026-06-11_