Skip to Content
Living documentation — last reviewed 2026-05-28
RunbooksCI / CD

CI / CD

GitHub Actions workflows live in .github/workflows/. This document explains what each one does, when it runs, and what to do when it’s red.

Workflows

FileTriggerPurposeRequired to merge?
ci-smoke-tests.ymlPull request (paths: apps/**, libs/**, lockfiles, nx.json, Makefile, .env.test.example, workflows)DB migration + API unit/integration/e2e + Web unit/integrationYes
cd-full-test-gate.ymlPush to main, manual dispatchEverything in smoke + Playwright web E2EYes (blocks deploy)
deploy-pr-preview.ymlPull requestBuilds a preview environment for reviewNo, but useful
publish-shared.ymlWhen libs/shared/** changesPublishes the shared lib (if applicable)No
smegrep.ymlManualAdhoc grep-based checksNo

Smoke workflow (PRs)

ci-smoke-tests.yml — runs on every PR that touches code/lockfiles/Makefile/workflows. Goes:

  1. Stand up Postgres (pgvector/pg16) on port 55432 and Redis on 6379.
  2. Set test env (TEST_AUTH_BYPASS=true, TEST_HOOKS_ENABLED=true, CRONS_ENABLED=true, Clerk test creds from secrets).
  3. Migrate the DB.
  4. Run API: unit → integration → e2e.
  5. Run Web: unit → integration.
  6. Cancel duplicates of the same ref via concurrency.cancel-in-progress.

Total budget: 45 minutes (timeout-minutes).

When it’s red

  • API tests: rerun locally with the same env (make test-unit-api, make test-integration-api, make test-e2e-api). Most flakes are timing-related — see testing/strategy.md.
  • Web tests: make test-unit-web, make test-integration-web.
  • Migration step: the journal-monotonicity bug (migrations.md) sometimes appears as “migration applied successfully” but a follow-up assertion fails because the schema didn’t change. Check libs/db/drizzle/meta/_journal.json for any out-of-order when values.
  • Auth bypass surprises: TEST_AUTH_BYPASS=true lets x-test-user-id header replace Clerk. If a test fails because the user isn’t found, the seed step probably didn’t run — check the testApi.seedX() calls in the spec.

Full test gate (main / pre-deploy)

cd-full-test-gate.yml — runs on push to main and manual dispatch. Includes Playwright web E2E (which the smoke workflow skips).

Total budget: 75 minutes.

When it’s red

Same diagnostic steps as smoke, plus Playwright artifacts: traces, screenshots, video are uploaded as workflow artifacts. Download from the GitHub Actions UI. See testing/driver-pattern.md for the e2e architecture.

A red full-gate blocks deploy. Don’t bypass it — investigate first.

PR preview

deploy-pr-preview.yml — builds a preview env for visual review. Not enforced, but useful for stakeholder sign-off before merge.

Required GitHub secrets

For both ci-smoke-tests.yml and cd-full-test-gate.yml:

  • CI_TEST_DATABASE_URL — points at a self-hosted CI Postgres if you don’t use the workflow’s bundled services.postgres. Currently the workflows boot their own Postgres so this is unused unless you point them elsewhere.
  • CLERK_SECRET_KEY, CLERK_PUBLISHABLE_KEY — a Clerk test instance dedicated to CI.

For the full gate only:

  • E2E_CLERK_USER_EMAIL, E2E_CLERK_USER_PASSWORD, E2E_CLERK_USER_ID — credentials for the E2E sign-in flow.

Add new secrets via Repo Settings → Secrets and Variables → Actions.

Local “act-like-CI”

To reproduce the smoke env locally:

make test-db-up make test-db-migrate make test-local-smoke # unit + integration for API & web (no Playwright) make test-local-all # the above + Playwright web e2e

Adding a workflow

Pre-flight:

  • Use concurrency.cancel-in-progress: true for everything that doesn’t deploy.
  • Set timeout-minutes so a hung step doesn’t burn the runner quota.
  • Pin actions to a major version, not @main.
  • If the workflow needs secrets, document them in this file.