Skip to content

Release: observability, error handling, and admin fixes #173

Release: observability, error handling, and admin fixes

Release: observability, error handling, and admin fixes #173

Workflow file for this run

# CI pipeline — runs on every PR to main and dev
# Validates code quality, types, build, tests, and database integrity
#
# Minute-saving strategy:
# - Path-based filtering skips expensive jobs when irrelevant files change
# - E2E only runs on PRs to main (most expensive job at ~6 min)
# - DB jobs only run when migration files change
# - Schema drift detection moved to deploy-dev (informational only)
# - Skipped jobs satisfy required status checks in GitHub rulesets
# - npm ci with cache — reproducible installs, no lockfile deletion
name: CI
on:
pull_request:
branches: [main, dev]
concurrency:
group: ci-${{ github.head_ref || github.ref_name }}
cancel-in-progress: true
env:
# Turborepo remote cache (optional — works without these)
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
# Filter to only packages affected since the PR base branch
TURBO_FILTER: --filter=...[origin/${{ github.base_ref }}]
jobs:
# ─── Detect what changed ─────────────────────────────────────
changes:
name: Detect Changes
runs-on: ubuntu-latest
outputs:
src: ${{ steps.filter.outputs.src }}
migrations: ${{ steps.filter.outputs.migrations }}
web: ${{ steps.filter.outputs.web }}
steps:
- uses: actions/checkout@v4
- uses: dorny/paths-filter@v3
id: filter
with:
filters: |
src:
- 'apps/**/*.{ts,tsx,js,jsx,css}'
- 'packages/**/*.{ts,tsx,js,jsx}'
- '!packages/db/types/database.ts'
- '**/package.json'
- '**/tsconfig*.json'
- 'turbo.json'
migrations:
- 'packages/db/supabase/migrations/**'
- 'packages/db/supabase/seed.sql'
web:
- 'apps/web/**'
- 'packages/**'
# ─── Lint & Type-check (parallel) ─────────────────────────────
lint:
name: Lint
needs: [changes]
if: needs.changes.outputs.src == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: 24
cache: npm
- name: Install dependencies
run: npm ci
- name: Turbo lint
run: npx turbo run lint ${{ env.TURBO_FILTER }}
- name: Summary
if: always()
run: echo "### Lint ${{ job.status == 'success' && '✅' || '❌' }}" >> "$GITHUB_STEP_SUMMARY"
typecheck:
name: Type-check
needs: [changes]
if: needs.changes.outputs.src == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: 24
cache: npm
- name: Install dependencies
run: npm ci
- name: Turbo check-types
run: npx turbo run check-types ${{ env.TURBO_FILTER }}
- name: Summary
if: always()
run: echo "### Type-check ${{ job.status == 'success' && '✅' || '❌' }}" >> "$GITHUB_STEP_SUMMARY"
# ─── Build & Test (parallel, after lint+typecheck) ────────────
build:
name: Build
needs: [changes, lint, typecheck]
if: needs.changes.outputs.src == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: 24
cache: npm
- name: Install dependencies
run: npm ci
- name: Turbo build
run: npx turbo run build ${{ env.TURBO_FILTER }}
- name: Summary
if: always()
run: echo "### Build ${{ job.status == 'success' && '✅' || '❌' }}" >> "$GITHUB_STEP_SUMMARY"
test:
name: Test
needs: [changes, lint, typecheck]
if: needs.changes.outputs.src == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: 24
cache: npm
- name: Install dependencies
run: npm ci
- uses: supabase/setup-cli@v1
with:
version: latest
- name: Start local Supabase (db + API only)
run: supabase start -x realtime,storage,imgproxy,inbucket,studio,edge-runtime,logflare,vector,supavisor
working-directory: packages/db
- name: Reset DB (migrations + seed)
run: supabase db reset
working-directory: packages/db
- name: Turbo test
run: npx turbo run test ${{ env.TURBO_FILTER }}
- name: Stop local Supabase
if: always()
run: supabase stop
working-directory: packages/db
- name: Summary
if: always()
run: echo "### Test ${{ job.status == 'success' && '✅' || '❌' }}" >> "$GITHUB_STEP_SUMMARY"
# ─── E2E (main PRs only — most expensive job) ────────────────
e2e:
name: E2E
needs: [changes, lint, typecheck]
if: github.base_ref == 'main' && needs.changes.outputs.web == 'true'
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: 24
cache: npm
- name: Install dependencies
run: npm ci
- uses: supabase/setup-cli@v1
with:
version: latest
- name: Start local Supabase
run: supabase start -x imgproxy,inbucket,studio,edge-runtime,logflare,vector,supavisor
working-directory: packages/db
- name: Export Supabase keys
id: supabase-keys
run: |
eval "$(supabase status -o env)"
echo "anon_key=${ANON_KEY}" >> "$GITHUB_OUTPUT"
echo "service_role_key=${SERVICE_ROLE_KEY}" >> "$GITHUB_OUTPUT"
working-directory: packages/db
- name: Reset DB (migrations + seed)
run: supabase db reset
working-directory: packages/db
- name: Wait for Supabase services to be ready
run: |
for i in $(seq 1 30); do
if curl -sf http://127.0.0.1:54321/rest/v1/ -H "apikey: $SUPABASE_ANON_KEY" > /dev/null 2>&1; then
echo "Supabase is ready"
exit 0
fi
echo "Waiting for Supabase... ($i/30)"
sleep 2
done
echo "Supabase failed to start"
exit 1
env:
SUPABASE_ANON_KEY: ${{ steps.supabase-keys.outputs.anon_key }}
- name: Install Playwright browsers (chromium)
run: npx playwright install --with-deps chromium
working-directory: apps/web
- name: Build web app for E2E
run: npx turbo run build --filter=web
env:
NEXT_PUBLIC_SUPABASE_URL: http://127.0.0.1:54321
NEXT_PUBLIC_SUPABASE_PUB_KEY: ${{ steps.supabase-keys.outputs.anon_key }}
SUPABASE_SECRET_KEY: ${{ steps.supabase-keys.outputs.service_role_key }}
- name: Run Playwright tests
run: npx playwright test
working-directory: apps/web
env:
NEXT_PUBLIC_SUPABASE_URL: http://127.0.0.1:54321
NEXT_PUBLIC_SUPABASE_PUB_KEY: ${{ steps.supabase-keys.outputs.anon_key }}
SUPABASE_SECRET_KEY: ${{ steps.supabase-keys.outputs.service_role_key }}
- name: Upload Playwright report
if: failure()
uses: actions/upload-artifact@v4
with:
name: playwright-report
path: apps/web/playwright-report/
retention-days: 7
- name: Stop local Supabase
if: always()
run: supabase stop
working-directory: packages/db
- name: Summary
if: always()
run: echo "### E2E ${{ job.status == 'success' && '✅' || '❌' }}" >> "$GITHUB_STEP_SUMMARY"
# ─── Supabase database checks (only when migrations change) ──
db-lint:
name: DB Lint
needs: [changes]
if: needs.changes.outputs.migrations == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: supabase/setup-cli@v1
with:
version: latest
- name: Link to dev project
run: supabase link --project-ref "${{ secrets.SUPABASE_DEV_PROJECT_REF }}"
working-directory: packages/db
env:
SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }}
- name: Lint migrations
run: supabase db lint --linked
working-directory: packages/db
env:
SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }}
- name: Summary
if: always()
run: echo "### DB Lint ${{ job.status == 'success' && '✅' || '❌' }}" >> "$GITHUB_STEP_SUMMARY"
migration-dry-run:
name: Migration Dry Run
needs: [changes]
if: needs.changes.outputs.migrations == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: supabase/setup-cli@v1
with:
version: latest
- name: Start local Supabase (db only)
run: supabase start -x realtime,storage,imgproxy,inbucket,postgrest,gotrue,studio,edge-runtime,logflare,vector,supavisor
working-directory: packages/db
- name: Reset DB (replay all migrations + seed)
run: supabase db reset
working-directory: packages/db
- name: Stop local Supabase
if: always()
run: supabase stop
working-directory: packages/db
- name: Summary
if: always()
run: echo "### Migration Dry Run ${{ job.status == 'success' && '✅' || '❌' }}" >> "$GITHUB_STEP_SUMMARY"