Files
familysync/.planning/phases/08-gitea-ci/08-CONTEXT.md
T

9.4 KiB

Phase 8: Gitea CI - Context

Gathered: 2026-06-11 Status: Ready for planning

## Phase Boundary

Phase 8 adds CI on the existing self-hosted Gitea Actions runner. Two outcomes:

  1. PR regression gate — every PR targeting main runs lint, typecheck (both apps), unit tests, API-integration tests against a MariaDB service container, and the Phase 7 mobile Playwright harness (against a CI-brought-up dev stack with DEV_AUTH_BYPASS=true). Any failure blocks the merge.
  2. Publish on merge — a push to main builds and publishes the API Docker image to the Gitea container registry.

This phase owns only the CI plumbing: workflow files, dev-stack bring-up + readiness waits, image build/push. It does not modify the Phase 7 harness specs (CI reuses them unchanged), the Dockerfile (already multi-stage, builds API + PWA), or application code. Requirements: CI-01, CI-02.

## Implementation Decisions

Dev-stack bring-up in CI (for the harness step)

  • D-01: Bring up the stack with bare background processes + a MariaDB service container — NOT docker compose, NOT a production image.
    • MariaDB runs as a Gitea service container (the same one the API-integration job needs; DB_HOST=127.0.0.1, service creds).
    • The API runs as a background process via pnpm dev:api (or equivalent) with DEV_AUTH_BYPASS=true and DB_HOST=127.0.0.1, listening on :3000.
    • The PWA Vite dev server is started by Playwright's own webServer config (already present; reuseExistingServer: !process.env.CI), on :5173. Vite proxies /api, /health, /callback:3000.
    • Rationale: no docker-in-docker on the self-hosted runner; matches the Phase 7 dev-server harness contract exactly; reuses the MariaDB service container already required for integration tests.
  • D-02: The harness step MUST wait for both the API (:3000) and the PWA Vite server (:5173) to accept connections before Playwright launches. The harness already polls baseURL/health (proxied to the API) in global-setup.ts; CI must additionally ensure the API process is up first. This is on top of the MariaDB-11 readiness loop (Pitfall 11 — healthcheck.sh --connect --innodb_initialized, never mysqladmin ping).

Workflow topology & jobs

  • D-03: One workflow file with parallel, event-gated jobs.
    • pull_requestmain: fast-checks job (lint + typecheck both apps + unit tests) runs in parallel with the heavier API-integration job and the harness job. Fast feedback — a lint failure does not wait behind the harness.
    • pushmain (merge): build-and-publish job runs.
    • Single file so the whole regression + publish story lives in one place; accept the minor setup duplication (checkout, pnpm cache, Node-22 pin) across jobs.

Docker image tag strategy (CI-02)

  • D-04: On merge to main, publish the API image with two tags: :latest (moving pointer) and :<milestone>-<shortsha> (immutable, e.g. v1.1-4303a1b).
    • The milestone string (e.g. v1.1) is read from PROJECT.md / ROADMAP.md, not hardcoded inline if avoidable.
    • <shortsha> is the short commit SHA of the merge commit.
    • Rationale: :latest for easy pulls; the milestone-prefixed SHA tag groups builds by release line and stays immutable for rollback/traceability.

Failure artifacts & browser matrix

  • D-05: Run both device profiles in CI — iPhone 14/WebKit and Pixel 7/Chromium (the full Phase 7 matrix). Install whatever system deps WebKit needs on the runner (probe in the runner-probe step).
  • D-06: On harness failure, upload Playwright traces / screenshots / videos as CI artifacts for debugging. The config already emits trace/video on-first-retry and screenshot: only-on-failure; CI must upload the test-results/ output. Note the config's reporter: 'github' may not render natively in Gitea Actions — verify during the runner probe and fall back to list/html if annotations don't surface.

Claude's Discretion

  • Exact job names, step ordering within a job, pnpm store cache key strategy, and whether fast-checks is one job or split — planner/executor decide.
  • Whether the API background process is launched with pnpm dev:api vs a built node dist — pick whatever gives reliable :3000 readiness under DEV_AUTH_BYPASS; the harness only needs the authed PWA reachable (Dev User 1 has no CalDAV creds, so verify layout/flows, not live event-create).
  • Registry hostname / image repository path under the Gitea registry.

<canonical_refs>

Canonical References

Downstream agents MUST read these before planning or implementing.

Phase scope & requirements

  • .planning/ROADMAP.md §"Phase 8: Gitea CI" — goal, 6 success criteria, pitfalls this phase owns.
  • .planning/REQUIREMENTS.md — CI-01 (PR regression incl. harness), CI-02 (publish image on merge).
  • .planning/PITFALLS.md — Pitfalls 11 (MariaDB-11 readiness), 12 (runner-probe first), 13 (--password-stdin), 15 (SW block, harness side).

Harness the CI step runs (reused unchanged from Phase 7)

  • apps/pwa/playwright.config.ts — device matrix, serviceWorkers: 'block', webServer (Vite-only, reuseExistingServer: !CI), retries/workers/reporter under CI, env-driven PLAYWRIGHT_BASE_URL.
  • apps/pwa/e2e/global-setup.ts/health readiness poll, /api/me DEV_AUTH_BYPASS reachability gate, fail-closed env guard (refuses NODE_ENV=production or missing DEV_AUTH_BYPASS), mysql2 truncate-and-seed (calendar id 10, lists/items for user 1).
  • .planning/phases/07-mobile-test-harness/07-CONTEXT.md — Phase 7 decisions D-01..D-10 (auth strategy, SW block, env baseURL, compose-managed backend).

Infra the CI builds/runs against

  • apps/api/Dockerfile — multi-stage: builder (API), pwa-builder (PWA dist → ./public), production target. CI publishes the production target.
  • docker-compose.yml / docker-compose.dev.yml — service shape, MariaDB 11 healthcheck (healthcheck.sh --connect --innodb_initialized), dev override exposing 3306, API dev build target.
  • apps/pwa/vite.config.ts — dev proxy (/api, /health, /callback:3000) the harness depends on.
  • package.json (root) — scripts: dev:api, dev:pwa, test, test:e2e, lint, typecheck.

</canonical_refs>

<code_context>

Existing Code Insights

Reusable Assets

  • Playwright config + global-setup (Phase 7): ready to run headlessly in CI. retries: 2, workers: 1, reporter: 'github' already gated on process.env.CI. CI sets CI=true and PLAYWRIGHT_BASE_URL and the harness behaves correctly. No spec changes.
  • MariaDB service-container pattern: API-integration tests already require a real MariaDB with DB_HOST=127.0.0.1 + service creds + Drizzle generate+migrate for schema. The harness's global-setup seeds the same DB directly via mysql2. One MariaDB service container can back both the integration job and the harness job.
  • Multi-stage Dockerfile: production target already builds API + PWA and serves both on :3000. CI build/push is a thin wrapper (docker build --target production + docker login --password-stdin + docker push).

Established Patterns

  • Gitea, not GitHub: origin is self-hosted Gitea; main is protected (PRs only). Gitea Actions is GitHub-Actions-syntax-compatible but do not assume actions/setup-node behaves identically — runner-probe first (Pitfall 12), pin Node 22 explicitly.
  • node-cron lesson (long-running process): not directly relevant to CI, but the API in CI is short-lived/background — no scheduler concerns.

Integration Points

  • CI orchestrates, in order, for the harness job: MariaDB service container (readiness loop) → Drizzle generate+migrate → API background process (DEV_AUTH_BYPASS=true, :3000, readiness wait) → Playwright (webServer starts Vite :5173, global-setup polls /health + /api/me) → specs → upload artifacts on failure.
  • Publish job depends on apps/api/Dockerfile production target + Gitea registry credentials (PAT with write:package, piped via --password-stdin).

</code_context>

## Specific Ideas
  • Image tag format locked to :latest + :v1.1-<shortsha> (milestone prefix + short SHA). Example: v1.1-4303a1b.
  • Start the very first CI iteration as a runner-probe only: node --version / pnpm --version / Docker access / WebKit dep availability on the self-hosted runner — before any real test/build steps are designed.
## Deferred Ideas
  • ROADMAP status fix: ROADMAP.md line 29 marks Phase 8 "completed 2026-06-11" while line 204 says "Not started" and no Phase 8 artifacts exist. This is a bookkeeping error to correct (Phase 8 is being started now) — a docs/roadmap cleanup, not Phase 8 scope.
  • Desktop e2e coverage (decided 2026-06-11): The Phase 7 harness defines only mobile profiles (iphone/WebKit + pixel/Chromium), so Phase 8 CI gates mobile only. Desktop validation is wanted but deferred to a dedicated harness follow-up phase — it requires editing apps/pwa/playwright.config.ts to add a Desktop profile and a spec-compat pass (the existing specs assume touch + mobile viewport/layout). Phase 8 deliberately reuses the Phase 7 harness unchanged; CI will pick up the desktop profile automatically via pnpm test:e2e once the follow-up adds it. Track as a backlog phase.

Phase: 8-Gitea CI Context gathered: 2026-06-11