Adds an integration-e2e job to ci.yml alongside the fast hermetic check job. It brings up the Supabase CLI stack, applies the canonical scripts/schema.sql + seed.sql, then runs the integration suite (psql-driven shipped-SQL: RPCs / RLS / triggers / constraints) and the Playwright e2e suite (chromium) against the app wired to that stack (env sourced from `supabase status`). The integration suite self-skips when no DB is reachable, so a missing DB could pass silently. Two guards prevent that: an explicit `select 1` reachability check, and a post-run assertion that fails the job when the suite executed 0 tests (parsed from the vitest json reporter). Auto-seed is disabled for the ephemeral CI checkout (seed.sql needs tables that schema.sql creates outside the migration path); schema + seed are applied explicitly. Local dev is unaffected. Validated locally: integration suite (58 tests) and both e2e specs (53 tests, chromium) pass against the real local stack.
145 lines
4.8 KiB
YAML
145 lines
4.8 KiB
YAML
name: CI
|
|
|
|
# Checks only — completely separate from deploy.yml (which SSHes to the droplet
|
|
# on push to main). This never deploys; it runs typecheck, lint, unit tests, and
|
|
# the DB-backed integration + Playwright e2e suites on every PR and on main after
|
|
# merge, so a regression is caught before (and independently of) a deploy.
|
|
on:
|
|
pull_request:
|
|
push:
|
|
branches: [main]
|
|
|
|
# Don't pile up redundant runs on a busy branch.
|
|
concurrency:
|
|
group: ci-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
jobs:
|
|
# Fast, hermetic gate: no DB, no browser.
|
|
check:
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 15
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Install pnpm
|
|
uses: pnpm/action-setup@v4
|
|
with:
|
|
version: 10
|
|
|
|
- name: Install Node
|
|
uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 20
|
|
cache: pnpm
|
|
|
|
- name: Install dependencies
|
|
run: pnpm install --frozen-lockfile
|
|
|
|
- name: Typecheck
|
|
run: pnpm run typecheck
|
|
|
|
- name: Lint
|
|
run: pnpm run lint
|
|
|
|
- name: Unit tests
|
|
run: pnpm run test:run
|
|
|
|
# DB-backed gate: brings up the Supabase CLI stack, applies the canonical
|
|
# schema, then runs the integration suite (shipped SQL: RPCs / RLS / triggers /
|
|
# constraints, driven through psql) and the Playwright e2e suite against the
|
|
# app wired to that stack.
|
|
integration-e2e:
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 25
|
|
env:
|
|
DB_URL: postgresql://postgres:[email protected]:54322/postgres
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Install pnpm
|
|
uses: pnpm/action-setup@v4
|
|
with:
|
|
version: 10
|
|
|
|
- name: Install Node
|
|
uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 20
|
|
cache: pnpm
|
|
|
|
- name: Install dependencies
|
|
run: pnpm install --frozen-lockfile
|
|
|
|
# The integration harness (integration/db.ts) drives the DB through a
|
|
# host `psql`, so it must be on PATH.
|
|
- name: Install psql client
|
|
run: |
|
|
sudo apt-get update
|
|
sudo apt-get install -y postgresql-client
|
|
|
|
- name: Setup Supabase CLI
|
|
uses: supabase/setup-cli@v1
|
|
with:
|
|
version: 2.98.2
|
|
|
|
- name: Start Supabase stack
|
|
run: |
|
|
# Auto-seed on first start would fail: seed.sql needs tables that
|
|
# schema.sql (applied below, not via a migration) hasn't created yet.
|
|
# Disable it for this ephemeral CI checkout; schema + seed are applied
|
|
# explicitly in the next step. (Local dev is unaffected.)
|
|
sed -i '/^\[db\.seed\]/,/^\[/ s/^enabled = true/enabled = false/' supabase/config.toml
|
|
supabase start
|
|
|
|
- name: Apply canonical schema + seed
|
|
run: |
|
|
psql "$DB_URL" -v ON_ERROR_STOP=1 -f scripts/schema.sql
|
|
psql "$DB_URL" -v ON_ERROR_STOP=1 -f scripts/seed.sql
|
|
|
|
# Guard: if the DB is unreachable the integration suite self-skips and
|
|
# would pass with zero coverage. Fail loudly here first.
|
|
- name: Verify DB is reachable
|
|
run: psql "$DB_URL" -tAc 'select 1'
|
|
|
|
- name: Integration tests
|
|
run: >
|
|
pnpm exec vitest run --config vitest.integration.config.ts
|
|
--reporter=default --reporter=json --outputFile=integration-result.json
|
|
|
|
# Belt-and-suspenders: even with a reachable DB, refuse to pass if the
|
|
# suite executed 0 tests (a glob miss, an all-skipped run, etc.).
|
|
- name: Fail if the integration suite executed 0 tests
|
|
run: |
|
|
node -e '
|
|
const r = require("./integration-result.json");
|
|
const ran = (r.numPassedTests || 0) + (r.numFailedTests || 0);
|
|
console.log(`integration tests executed: ${ran} (passed ${r.numPassedTests}, failed ${r.numFailedTests}, skipped ${r.numPendingTests})`);
|
|
if (ran < 1) {
|
|
console.error("::error::integration suite executed 0 tests — the DB was unreachable and the suite self-skipped. Refusing to pass.");
|
|
process.exit(1);
|
|
}
|
|
'
|
|
|
|
- name: Install Playwright browser (chromium)
|
|
run: pnpm exec playwright install --with-deps chromium
|
|
|
|
- name: E2E (Playwright)
|
|
run: |
|
|
eval "$(supabase status -o env 2>/dev/null)"
|
|
export NEXT_PUBLIC_SUPABASE_URL="$API_URL"
|
|
export NEXT_PUBLIC_SUPABASE_ANON_KEY="$ANON_KEY"
|
|
export SUPABASE_SERVICE_ROLE_KEY="$SERVICE_ROLE_KEY"
|
|
export NEXT_PUBLIC_APP_URL="http://localhost:3000"
|
|
# Playwright's webServer boots `npm run dev` (see playwright.config.ts)
|
|
# and inherits these env vars.
|
|
pnpm exec playwright test --project=chromium
|
|
|
|
- name: Upload Playwright report
|
|
if: ${{ !cancelled() }}
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: playwright-report
|
|
path: playwright-report/
|
|
retention-days: 7
|