Trovella Wiki

Job Definitions

Complete reference for every CI job -- steps, environment variables, permissions, and conditional execution.

Complete reference for all five jobs in .github/workflows/ci.yml. Each section lists the job's steps, environment variables, required permissions, and conditional logic.

Global Configuration

These settings apply to the entire workflow:

concurrency:
  group: ci-${{ github.ref }}
  cancel-in-progress: true

env:
  TURBO_TELEMETRY_DISABLED: 1

The concurrency group cancels in-progress runs when a new push arrives for the same branch. See Concurrency & Caching for details.

quality

Name: Lint, Typecheck, Test, Build Trigger: All pushes and PRs to main Timeout: 15 minutes Permissions: Default (read)

Service Containers

ServiceImagePortCredentials
postgrespgvector/pgvector:pg185433:5432trovella / trovella_dev
redisredis:8-alpine6379:6379None
typesensetypesense/typesense:27.18108:8108API key: ci-test-key

Environment Variables

VariableValuePurpose
DATABASE_URLpostgresql://trovella:trovella_dev@localhost:5433/trovellaCI Postgres (not production)
REDIS_URLredis://localhost:6379CI Redis
BETTER_AUTH_SECRETci-test-secret-at-least-32-characters-longAuth library requirement
BETTER_AUTH_URLhttp://localhost:3000Auth callback base
GOOGLE_CLIENT_IDci-placeholderOAuth (not exercised in tests)
GOOGLE_CLIENT_SECRETci-placeholderOAuth (not exercised in tests)
ANTHROPIC_API_KEYci-placeholderAI SDK (not exercised in tests)
GOOGLE_AI_API_KEYci-placeholderGoogle AI (not exercised in tests)
TYPESENSE_API_KEYci-test-keySearch integration tests
TYPESENSE_URLhttp://localhost:8108Search integration tests

Placeholder values prevent the application from failing at startup due to missing env vars. Tests that would call external APIs are mocked or skipped.

Steps (in order)

#StepCommand
1Checkoutactions/checkout@v4 (fetch-depth: 2)
2Install pnpmpnpm/action-setup@v4
3Setup Node.jsactions/setup-node@v4 (reads .node-version, caches pnpm)
4Install dependenciespnpm install --frozen-lockfile
5Check formattingpnpm format:check
6Lintpnpm turbo lint --affected
7Dependency graphpnpm dep-cruise
8Dead code detectionpnpm lint:dead-code
9Duplication detectionpnpm lint:duplication
10Upload duplication reportactions/upload-artifact@v4 (always, 14-day retention)
11Typecheckpnpm turbo typecheck --affected
12Database migrationspnpm db:migrate
13Testpnpm turbo test --affected
14Buildpnpm turbo build --affected (with SENTRY_AUTH_TOKEN secret)

The step order is deliberate. See Quality Checks for why lint must precede typecheck and why migrations must precede tests.

docs

Name: Documentation Quality Trigger: All pushes and PRs to main Timeout: 5 minutes Permissions: Default (read) Does not gate deployment.

Steps (in order)

#StepCommand
1Checkoutactions/checkout@v4 (fetch-depth: 0, full history for freshness)
2Install pnpmpnpm/action-setup@v4
3Setup Node.jsactions/setup-node@v4 (reads .node-version, caches pnpm)
4Install dependenciespnpm install --frozen-lockfile
5Sync Vale stylespnpm vale sync
6Vale prose lintpnpm docs:lint
7Link checkpnpm docs:links
8Freshness checkpnpm docs:freshness (with CI=true)
9Upload freshness reportactions/upload-artifact@v4 (always, 14-day retention)

The checkout uses fetch-depth: 0 (full Git history) because the freshness check compares file modification dates against Git log timestamps.

The docs job was separated from quality so prose lint failures do not block production deploys. A typo in a guide is not worth holding back a security fix.

build-push

Name: Build & Push Docker Image Trigger: Main branch only (push + refs/heads/main) Timeout: 15 minutes Depends on: None (runs in parallel with quality) Permissions: contents: read, id-token: write (for WIF)

Environment Variables

VariableValuePurpose
REGISTRYus-central1-docker.pkg.dev/trovella-shared/trovellaArtifact Registry path
IMAGEus-central1-docker.pkg.dev/trovella-shared/trovella/webFull image path

Steps (in order)

#StepDetails
1Checkoutactions/checkout@v4
2Authenticate to GCPWIF via google-github-actions/auth@v2
3Configure Dockergcloud auth configure-docker us-central1-docker.pkg.dev
4Read SENTRY_DSNFrom GCP Secret Manager (trovella-sentry-dsn)
5Setup Buildxdocker/setup-buildx-action@v3
6Build and pushdocker/build-push-action@v6

Build Configuration

context: .
file: apps/web/Dockerfile
push: true
tags:
  - $IMAGE:$SHA # immutable rollback target
  - $IMAGE:latest # mutable, pulled by docker compose
cache-from: type=gha
cache-to: type=gha,mode=max
build-args:
  - NEXT_PUBLIC_BETTER_AUTH_URL=https://trovella.ai
  - NEXT_PUBLIC_SENTRY_DSN=$SENTRY_DSN
  - SENTRY_AUTH_TOKEN=$SENTRY_AUTH_TOKEN

NEXT_PUBLIC_* variables are inlined by Next.js at build time and cannot be overridden at runtime. The SENTRY_AUTH_TOKEN GitHub secret enables source map uploads during the build.

For the Docker multi-stage build details, see Infrastructure -- Deploy Pipeline.

migrate-prod

Name: Migrate Production Database Trigger: Main branch only Timeout: 5 minutes Depends on: quality Permissions: contents: read, id-token: write (for WIF)

Change Detection

Uses dorny/paths-filter@v3 to check whether the push includes changes in:

  • packages/db/src/migrations/**
  • packages/db/src/schema/**
  • packages/db/src/seed/**

If none of these paths changed, every subsequent step is skipped. This avoids the ~15 seconds of Cloud SQL Auth Proxy setup overhead on code-only deploys.

Steps (when migrations detected)

#StepDetails
1Checkoutactions/checkout@v4
2Check for schema changesdorny/paths-filter@v3
3Authenticate to GCPWIF via google-github-actions/auth@v2
4Read DATABASE_URLFrom Secret Manager, rewritten for local proxy
5Install Cloud SQL Auth ProxyDownload v2.21.2 binary
6Start proxyBackground process with health check (30s timeout)
7Install pnpmpnpm/action-setup@v4
8Setup Node.jsactions/setup-node@v4
9Install dependenciespnpm install --frozen-lockfile
10Run migrationspnpm db:migrate
11Verify migration countCompare _journal.json entries to drizzle.__drizzle_migrations rows
12Seed reference datapnpm db:seed with NODE_ENV=production
13Dump proxy logsAlways runs (even on success)

For the full migration mechanics (proxy configuration, readiness checks, URL rewriting, verification), see Data & Storage -- CI Deployment.

deploy-prod

Name: Deploy to Production VM Trigger: Main branch only Timeout: 10 minutes Depends on: quality, build-push, migrate-prod Permissions: contents: read, id-token: write (for WIF)

Environment Variables

VariableValuePurpose
VM_NAMEtrovella-prod-vmTarget VM
VM_ZONEus-central1-aGCP zone
VM_PROJECTtrovella-prodGCP project

Steps (in order)

#StepDetails
1Checkoutactions/checkout@v4
2Authenticate to GCPWIF via google-github-actions/auth@v2
3Copy compose filesSCP via IAP: docker-compose.prod.yml, Caddyfile, sync-secrets-vm.sh
4Sync secrets and deploySSH via IAP: move files, sync secrets, pull image, restart containers, prune

The deploy is a single SSH command that runs five operations sequentially on the VM:

  1. Move uploaded files to /opt/trovella/
  2. Run sync-secrets-vm.sh (Secret Manager to .env)
  3. gcloud auth configure-docker (for Artifact Registry pull)
  4. docker compose pull (fetches changed images only)
  5. docker compose up -d --remove-orphans + docker image prune -f

For the full deploy mechanics (container dependencies, health checks, secret sync), see Infrastructure -- Deploy Pipeline.

On this page