CI/CD¶
GitHub Actions workflows for automated testing and deployment.
Overview¶
flowchart LR
subgraph Trigger
A[Push/PR to main]
P[Schedule/Manual]
end
subgraph Backend
B[Path Filter] --> C[Restore]
C --> D[Build]
D --> E[Unit Tests]
E --> F[Integration Tests]
F --> G[Vulnerability Scan]
end
subgraph Frontend
H[Path Filter] --> I[Install]
I --> J[Audit]
J --> K[Lint]
K --> L[Type Check]
L --> M[Unit Tests]
M --> N[Build]
end
subgraph Security
O[Gitleaks Scan]
end
subgraph Database
Q[pg_dump] --> R[Upload to R2]
R --> S[Verify]
end
A --> B
A --> H
A --> O
P --> Q Workflows¶
Backend (backend.yml)¶
Trigger: Push or PR to main when UnicornTrails.API/** changes
Steps:
| Step | Command | Purpose |
|---|---|---|
| Restore | dotnet restore | Install dependencies |
| Build | dotnet build --warnaserror | Compile with strict warnings |
| Unit Tests | dotnet test | Run unit tests with coverage |
| Integration Tests | dotnet test | Run integration tests |
| Vulnerability Scan | dotnet list package --vulnerable | Check for vulnerable packages |
Configuration:
- uses: actions/setup-dotnet@v5
with:
dotnet-version: '10.0.x'
- uses: actions/cache@v6
with:
path: ~/.nuget/packages
key: nuget-${{ runner.os }}-${{ hashFiles('UnicornTrails.API/**/*.csproj') }}
Frontend (frontend.yml)¶
Trigger: Push or PR to main when UnicornTrails.Client/** changes
Steps:
| Step | Command | Purpose |
|---|---|---|
| Install | npm ci | Clean install |
| Audit | npm audit --audit-level=high | Security check |
| Lint | npm run lint | ESLint |
| Type Check | tsc -b --noEmit | TypeScript validation |
| Unit Tests | npm run test:unit | Vitest |
| Build | npm run build | Production build |
Configuration:
- uses: actions/setup-node@v6
with:
node-version: '22.x'
cache: 'npm'
cache-dependency-path: './UnicornTrails.Client/package-lock.json'
Security (security.yml)¶
Trigger: All pushes and PRs to main
Tool: Gitleaks
Purpose: Detect leaked secrets in code
Database Backup (db-backup.yml)¶
Trigger: Schedule (daily at 02:00 UTC) or manual dispatch
Steps:
| Step | Command | Purpose |
|---|---|---|
| Dump | pg_dump | Export database to SQL |
| Upload | aws s3 cp | Upload gzipped backup to R2 |
| Verify | aws s3api head-object | Confirm upload size matches |
Secrets required:
| Secret | Purpose |
|---|---|
BACKUP_DATABASE_URL | Read-only PostgreSQL connection string |
BACKUP_R2_ACCESS_KEY | R2 API access key |
BACKUP_R2_SECRET_KEY | R2 API secret key |
BACKUP_R2_BUCKET | Backup bucket name |
BACKUP_R2_ENDPOINT | R2 S3 endpoint URL |
Configuration:
- Backups stored in separate R2 bucket from user images
- 10-day lifecycle rule auto-deletes old backups
- Read-only database user via
pg_read_all_datarole
Restore:
# Download backup
aws s3 cp s3://unicorn-trails-db-backups/backup-YYYY-MM-DD-HH-MM.sql.gz ./backup.sql.gz \
--endpoint-url https://<account-id>.r2.cloudflarestorage.com
gunzip backup.sql.gz
# Restore (use admin connection string, not backup_reader)
psql "$DATABASE_URL" < backup.sql
Warning: Restore overwrites existing data. Pause the API service in Railway first.
Path Filtering¶
Workflows only run when relevant files change:
Concurrency Control¶
Cancel in-progress runs on new pushes:
Dependabot¶
Automated dependency updates:
| Ecosystem | Schedule | Groups |
|---|---|---|
| NuGet | Weekly (Monday) | ef-core, microsoft |
| npm | Weekly (Monday) | mui, tanstack |
| GitHub Actions | Weekly (Monday) | — |
Secret Scanning¶
.gitleaks.toml defines patterns to detect:
- Cloudflare R2 credentials
- Resend API keys
- Sentry auth tokens
- Turnstile secrets
- JWT signing keys
- PostgreSQL passwords
Running Locally¶
# Backend
cd UnicornTrails.API
dotnet restore
dotnet build
dotnet test
# Frontend
cd UnicornTrails.Client
npm ci
npm run lint
npm run test:unit
npm run build
Related¶
- Testing — Test strategies
- Deployment — Production deployment