Skip to content

Developer Workflow Guide

This document describes the branching strategy, CI/CD pipeline, local development setup, and staging/production promotion flow for Compath.


Environments

EnvironmentBackend URLFrontend URLBranchAuto-deploy
Localhttp://localhost:3000http://localhost:5173any
Staginghttps://brain-staging.compath.appCloudflare Pages branch previewstaging✅ on push
Productionhttps://brain.compath.apphttps://app.compath.appmain✅ on push

Branch Strategy

main ─────────────────────────────────────────────── production

  └── staging ──────────────────────────────────────── staging env

        ├── feat/calendar-sync
        ├── fix/booking-notification
        └── chore/update-deps

Branch naming

TypePatternExample
New featurefeat/<short-description>feat/reschedule-booking
Bug fixfix/<short-description>fix/pending-booking-calendar
Chore / maintenancechore/<short-description>chore/update-go-deps
Hotfix (prod only)hotfix/<short-description>hotfix/payment-500-error

Protected branches

BranchRules
mainRequires PR, CI must pass, 1 approval
stagingRequires PR, CI must pass

Standard Developer Flow

1. branch off staging
       git checkout staging && git pull
       git checkout -b feat/my-feature

2. develop locally
       make dev           # backend with hot reload
       bun dev            # frontend

3. push and open PR → staging
       git push -u origin feat/my-feature
       # Open PR targeting staging (not main)

4. CI runs automatically
       ✓ Go tests + race detector
       ✓ golangci-lint
       ✓ TypeScript type-check
       ✓ Swagger generation

5. PR reviewed and merged → staging
       → deploy-staging workflow fires
       → backend image tagged :staging pushed to GHCR
       → staging containers restarted on VPS
       → https://brain-staging.compath.app live within ~3 min

6. QA on staging
       → verify feature works end-to-end
       → run any manual checks

7. open PR: staging → main
       → requires 1 approval
       → CI reruns

8. merge → production
       → deploy-backend workflow fires
       → image tagged :latest pushed to GHCR
       → production containers restarted on VPS

Hotfix Flow (production issue, no time for staging)

1. branch off main
       git checkout main && git pull
       git checkout -b hotfix/critical-bug

2. fix, test locally

3. open PR → main
       → CI runs
       → requires 1 approval (expedited)

4. merge → production auto-deploys

5. backport to staging
       git checkout staging && git pull
       git cherry-pick <commit-sha>
       git push

Prototype UI Promotion Flow (prototype -> main)

Use this for validated UI in prototype routes/components when /proto must not ship to main.

bash
# 1) Create a clean promotion branch from main
git fetch origin
git switch main
git pull --ff-only origin main
git switch -c promote/<scope>-<yyyy-mm-dd>

# 2) Cherry-pick production-ready commits from prototype
git cherry-pick -x <sha1> <sha2> ...

# 3) For mixed commits (prod + proto), apply without commit and exclude proto files
git cherry-pick -n <sha>
git restore --source=HEAD --staged --worktree frontend/src/app/proto
git commit -m "promote(ui): <summary> (proto excluded)"

# 4) Verify no proto leakage
git diff --name-only origin/main...HEAD | rg "^frontend/src/app/proto/"
rg -n "@/app/proto" frontend/src --glob "!frontend/src/app/proto/**"

# 5) Validate and publish
cd frontend
bun run type-check
bun run build
git push -u origin promote/<scope>-<yyyy-mm-dd>

Rules:

  • Never open PRs from prototype directly to main.
  • Open PRs from promote/* to main only.
  • If a commit introduces dependencies not yet in main, promote the minimal required file(s) as a separate commit.

Local Development Setup

Prerequisites

  • Go 1.25+
  • Bun 1.x
  • Docker + Docker Compose

1. Start local services

bash
# From repo root — starts PostgreSQL + Adminer
npm run docker:up

2. Backend

bash
cd backend
cp .env.example .env      # fill in required values
make migrate-up           # run migrations
make dev                  # hot reload at http://localhost:3000

Key env vars for local development:

DATABASE_URL=postgresql://postgres:postgres@localhost:5432/compath?sslmode=disable
SUPABASE_URL=https://<your-project>.supabase.co
SERVER_PORT=3000
ENVIRONMENT=development

3. Frontend

bash
cd frontend
cp .env.example .env.local    # fill in required values
bun install
bun dev                        # dev server at http://localhost:5173

Key env vars:

VITE_API_URL=http://localhost:3000
VITE_SUPABASE_URL=https://<your-project>.supabase.co
VITE_SUPABASE_ANON_KEY=<your-anon-key>

4. Verify setup

bash
curl http://localhost:3000/api/v1/health   # → {"status":"ok"}

Adminer (DB browser): http://localhost:8080

  • Server: postgres, User: postgres, Password: postgres, DB: compath

CI Pipeline

CI runs automatically on every PR. All jobs must pass before merge is allowed.

On PRs targeting staging or main

JobWhat it checksBlocks merge
testgo test -race ./... against a real Postgres✅ Yes
lintgolangci-lint⚠️ Warning only (continue-on-error)
swaggerswag init succeeds✅ Yes
type-checktsc --noEmit✅ Yes

Workflow files

FileTriggerPurpose
ci-backend.ymlPR → staging or mainTest, lint, swagger
ci-frontend.ymlPR → staging or mainType-check
deploy-staging.ymlPush to stagingBuild :staging image, deploy
deploy-backend.ymlPush to mainBuild :latest image, deploy production

Staging Environment

Architecture

VPS (same server as production)
├── compath-backend         :3000  ← production
├── compath-backend-staging :3001  ← staging (joins production nginx network)
├── compath-postgres        :5432  ← production DB
├── compath-postgres-staging:5433  ← staging DB (separate data)
├── compath-redis                  ← production cache
├── compath-redis-staging          ← staging cache (separate)
└── compath-nginx           :80/:443 ← routes both domains
    ├── brain.compath.app     → compath-backend
    └── brain-staging.compath.app → compath-backend-staging

Cloudflare SSL Setup (one-time, covers all subdomains)

SSL is handled by Cloudflare — no certbot required. The nginx config uses a Cloudflare Origin Certificate (wildcard *.compath.app) which is already installed for production. If the Origin Certificate doesn't exist yet, follow these steps:

1. Generate the Cloudflare Origin Certificate

  1. Go to Cloudflare Dashboardcompath.appSSL/TLSOrigin Server
  2. Click Create Certificate
  3. Choose:
    • Key type: RSA (2048)
    • Hostnames: compath.app, *.compath.app
    • Validity: 15 years
  4. Click Create
  5. Copy the Origin Certificate (PEM) and Private Key

2. Install on the VPS

bash
ssh compath-vps
sudo mkdir -p /etc/ssl/cloudflare

# Paste the Origin Certificate
sudo nano /etc/ssl/cloudflare/compath.app.pem

# Paste the Private Key
sudo nano /etc/ssl/cloudflare/compath.app.key

sudo chmod 644 /etc/ssl/cloudflare/compath.app.pem
sudo chmod 600 /etc/ssl/cloudflare/compath.app.key

If production is already running, the cert files are already on the VPS — staging reuses them.

3. Set Cloudflare SSL mode

In Cloudflare Dashboardcompath.appSSL/TLSOverview:

  • Set encryption mode to Full (strict)

This ensures Cloudflare validates the Origin Certificate when connecting to your VPS.


First-time staging setup on the VPS

SSH into the VPS and run these steps once:

bash
ssh compath-vps
cd /home/compath

# 1. Create the staging env file
cp infrastructure/deployment/.env infrastructure/deployment/.env.staging
nano infrastructure/deployment/.env.staging
# Edit: STAGING_POSTGRES_*, STAGING_REDIS_PASSWORD, SUPABASE_URL (staging project), etc.

# 2. Add DNS record in Cloudflare
#    Type: A  Name: brain-staging  Content: <VPS IP>  Proxy: ON (orange cloud)
#    (The wildcard Origin Certificate already covers *.compath.app — no cert work needed)

# 3. Reload nginx to pick up staging.conf
docker exec compath-nginx nginx -s reload

# Verify nginx config is valid before reloading
docker exec compath-nginx nginx -t

# 4. Start staging stack for the first time
docker compose -f infrastructure/deployment/docker-compose.staging.yml up -d

# 5. Run staging migrations
docker exec compath-backend-staging ./migrate -command up

# 6. Health check
curl -sf https://brain-staging.compath.app/api/v1/health

Required GitHub Secrets

The staging deploy reuses the existing production VPS secrets. No new secrets needed unless you want to separate them.

SecretUsed byValue
VPS_HOSTdeploy-staging, deploy-backendVPS IP address
VPS_USERNAMEdeploy-staging, deploy-backendSSH user
VPS_SSH_KEYdeploy-staging, deploy-backendPrivate key
VPS_PORTdeploy-staging, deploy-backendSSH port (usually 22)
GHCR_TOKENdeploy-staging, deploy-backendGitHub PAT with read:packages
GHCR_USERNAMEdeploy-staging, deploy-backendGitHub username

Staging .env.staging reference

env
# Server
SERVER_PORT=3000
SERVER_HOST=0.0.0.0
ENVIRONMENT=staging

# Database (staging-specific)
STAGING_POSTGRES_USER=postgres
STAGING_POSTGRES_PASSWORD=<strong-password>
STAGING_POSTGRES_DB=compath_staging
DATABASE_URL=postgresql://postgres:<password>@compath-postgres-staging:5432/compath_staging?sslmode=disable

# Redis (staging-specific)
STAGING_REDIS_PASSWORD=<strong-password>
REDIS_URL=redis://:${STAGING_REDIS_PASSWORD}@compath-redis-staging:6379

# Supabase — use your STAGING Supabase project, not production
SUPABASE_URL=https://<staging-project>.supabase.co
SUPABASE_ANON_KEY=<staging-anon-key>

# Email — use test mode or a staging Resend domain
RESEND_API_KEY=<resend-key>
RESEND_FROM_EMAIL=no-reply@staging.compath.app

# Google Calendar — use a separate GCP project or the same with staging redirect URI
GOOGLE_CLIENT_ID=<google-client-id>
GOOGLE_CLIENT_SECRET=<google-client-secret>
GOOGLE_REDIRECT_URI=https://brain-staging.compath.app/api/v1/calendar/callback/google
GOOGLE_WEBHOOK_URL=https://brain-staging.compath.app/api/v1/webhooks/google/calendar

# Axiom — same API token as production, separate dataset
AXIOM_TOKEN=<axiom-api-token>
VECTOR_AXIOM_DATASET=compath-staging

Running migrations on staging

bash
ssh compath-vps
docker exec compath-backend-staging ./migrate -command version
docker exec compath-backend-staging ./migrate -command up

Frontend Staging (Cloudflare Pages)

Cloudflare Pages automatically creates a branch preview for every branch. When the staging branch is pushed:

  • Branch preview URL: https://staging.compath.pages.dev
  • To use a custom domain (app-staging.compath.app):
    1. Go to Cloudflare Pages → your project → Settings → Environment Variables
    2. Add a staging environment with VITE_API_URL=https://brain-staging.compath.app
    3. Go to Custom Domains → add app-staging.compath.app mapped to the staging branch

Environment variables per branch (Cloudflare Pages dashboard)

VariableProduction (main)Staging (staging)
VITE_API_URLhttps://brain.compath.apphttps://brain-staging.compath.app
VITE_SUPABASE_URLprod Supabase URLstaging Supabase URL
VITE_SUPABASE_ANON_KEYprod anon keystaging anon key

Deployment Checklist

Deploying to staging

Staging deploys automatically on every merge to staging. No manual steps needed unless running migrations with a schema change.

bash
# If the PR includes a new migration:
ssh compath-vps
docker exec compath-backend-staging ./migrate -command up

Promoting staging → production

  1. Open PR from stagingmain on GitHub
  2. CI reruns — wait for green
  3. Get approval
  4. Merge
  5. deploy-backend.yml fires automatically (~3 min to deploy)
  6. SSH in and run migrations if needed:
    bash
    ssh compath-vps
    docker exec compath-backend ./migrate -command up
    curl -sf https://brain.compath.app/api/v1/health

Common Commands Reference

bash
# Check staging is healthy
curl -sf https://brain-staging.compath.app/api/v1/health

# View staging logs
ssh compath-vps "docker logs compath-backend-staging --tail 100 -f"

# View staging compose status
ssh compath-vps "docker compose -f /home/compath/infrastructure/deployment/docker-compose.staging.yml ps"

# Restart staging backend manually
ssh compath-vps "docker compose -f /home/compath/infrastructure/deployment/docker-compose.staging.yml restart backend"

# Connect to staging database
ssh compath-vps -t "docker exec -it compath-postgres-staging psql -U postgres -d compath_staging"

# Roll back last staging migration
ssh compath-vps "docker exec compath-backend-staging ./migrate -command down"

Last updated: