CI/CD Pipeline
CI/CD Pipeline
Project: Drop Version: 0.1.0 Date: 2026-02-23 Author: Platform Architect (AI) Status: In Review Reviewers: Alem Bašić (CEO)
Document History
| Version | Date | Author | Changes |
|---|---|---|---|
| 0.1 | 2026-02-23 | Platform Architect (AI) | Initial draft from source code analysis |
1. Overview
Drop uses GitHub Actions for CI with a 5-job pipeline covering lint, typecheck, unit tests, E2E tests, and Docker build. The CD (automated deployment) pipeline to AWS App Runner is tracked as a pending item — currently CI builds but does not deploy automatically. Staging deployment is manual via fly deploy to Fly.io (Stockholm).
CI/CD Platform: GitHub Actions (.github/workflows/ci.yml)
Container Registry: AWS ECR (324480209768.dkr.ecr.eu-west-1.amazonaws.com/drop-web)
Deployment Target (production): AWS App Runner (eu-west-1)
Deployment Target (staging): Fly.io (Stockholm, region arn)
Strategy: Rolling (App Runner managed)
2. Pipeline Overview
flowchart LR
subgraph Source
PR[Pull Request\nto main/master]
MERGE[Push to\nmain/master]
end
subgraph CI ["CI — runs on every push/PR"]
LINT[1. lint-and-typecheck\nnpm run lint\ntsc --noEmit]
TEST[2. test\nvitest run]
BUILD[3. build\nnext build]
E2E[4. e2e\nPlaywright\nuser-flows + full-flows]
DOCKER[5. docker-build\ndocker build -t drop-app:ci]
end
subgraph CD_STG ["CD Staging — manual"]
STG[fly deploy\nFly.io Stockholm]
end
subgraph CD_PROD ["CD Production — pending implementation"]
APPROVAL[Manual Approval\nvia GitHub Environments]
PROD[aws apprunner\nstart-deployment]
end
PR --> LINT
MERGE --> LINT
LINT --> TEST
LINT --> BUILD
TEST --> E2E
BUILD --> E2E
E2E --> DOCKER
DOCKER --> STG
DOCKER --> APPROVAL
APPROVAL --> PROD
3. Source Control Configuration
3.1 Branching Strategy
Strategy: GitHub Flow (feature branches off main, PR into main)
| Branch | Purpose | Naming Convention | Lifetime |
|---|---|---|---|
main |
Production-ready code | fixed | Permanent |
feature/* |
New features | feature/description |
Until merged |
fix/* |
Bug fixes | fix/description |
Until merged |
hotfix/* |
Production hotfixes | hotfix/description |
Until merged |
3.2 Branch Protection Rules
Protected Branches: main
| Rule | main |
|---|---|
| Require PR | Yes |
| Required approvals | 1 (Alem Bašić) |
| Dismiss stale reviews | Yes |
| Require status checks | Yes |
| Required checks | lint-and-typecheck, test, build, e2e, docker-build |
| Require up-to-date | Yes |
| Allow force push | No |
| Allow deletions | No |
3.3 Code Review Requirements
- Minimum 1 approval required before merge
- At least one approval from Alem Bašić (or delegated lead)
- All review comments must be resolved before merge
- Review turnaround SLA: 24 business hours
4. Build Stage
4.1 Build Tool & Configuration
| Parameter | Value |
|---|---|
| Build Tool | npm (Node.js 22) |
| Install Command | npm ci (requires python3, make, g++ for better-sqlite3 native deps) |
| Build Command | npm run build (Next.js standalone output) |
| Artifact Type | Docker image (multi-stage: deps → builder → runner) |
| Artifact Naming | 324480209768.dkr.ecr.eu-west-1.amazonaws.com/drop-web:<git-sha> |
| Tag Strategy | git-sha for CI builds, semantic version for releases |
| Base Image | node:22-alpine |
| Production Image User | nextjs (UID 1001, non-root) |
Multi-stage build:
Stage 1: deps — npm ci + native dependencies
Stage 2: builder — npm run build (Next.js standalone)
Stage 3: runner — minimal image, copies only public/ + .next/standalone/ + .next/static/
4.2 Dependency Caching
| Cache | Key | Restore Keys |
|---|---|---|
| Node modules | node-modules-linux-${{ hashFiles('package-lock.json') }} |
node-modules-linux- |
| Docker build layers | BuildKit layer cache via cache-from: type=gha |
— |
4.3 Artifact Generation
| Artifact | Storage | Retention | Signed |
|---|---|---|---|
| Docker image | AWS ECR drop-web repository |
90 days (non-prod tags), Forever (release tags) | No (planned) |
| Playwright HTML report | GitHub Actions artifact playwright-report/ |
7 days | No |
QA report (qa-report.html) |
GitHub Actions artifact | 7 days | No |
5. Test Stages
5.1 Unit Tests
| Parameter | Value |
|---|---|
| Framework | Vitest 4.0.18 |
| Command | npm test (runs vitest run) |
| Test files | tests/**/*.test.ts |
| Setup | tests/setup.ts — sets NODE_ENV=test, in-memory databases |
| Coverage Tool | TBD — not yet configured |
| Coverage Gate | TBD — requires coverage configuration |
| Failure Action | Block PR merge (CI job 2 test must pass) |
5.2 Integration Tests
| Parameter | Value |
|---|---|
| Framework | Vitest (same runner as unit tests) |
| Command | npm test |
| Dependencies | SQLite in-memory (no external services needed) |
| Failure Action | Block PR merge |
5.3 E2E Tests
| Parameter | Value |
|---|---|
| Framework | Playwright 1.58.2 |
| Command | npx playwright test user-flows full-flows |
| Browser | Chromium only (installed in CI via npx playwright install chromium) |
| Environment | Production build running locally (npm run build && npm run start) |
| Parallelization | 1 worker (serial — rate limiter is shared state) |
| Retries | 2 (CI) |
| Timeout | 30,000ms |
| Base URL | http://localhost:3000 |
| Test suites | user-flows.spec.ts, full-flows.spec.ts, input-chaos.spec.ts (depends on user-flows) |
| Artifacts | Playwright HTML report + qa-report.html uploaded to GitHub Actions |
| Failure Action | Block docker-build job |
5.4 Security Scanning
| Scan Type | Tool | Status | Gate |
|---|---|---|---|
| SAST | TBD — not yet implemented | Pending | Block on HIGH/CRITICAL |
| SCA (dependencies) | npm audit — not yet in CI |
Pending | Block on CRITICAL |
| Container scan | TBD (Trivy planned) | Pending | Block on CRITICAL |
| Secret scanning | GitHub native secret scanning | Enabled via GitHub | Block on any finding |
Note: Security scanning tracked in security/hardening-checklist.md. npm audit and Snyk integration are pending.
5.5 Linting & Formatting
| Tool | Purpose | Command | Auto-fix |
|---|---|---|---|
| ESLint 9 | Code linting | npm run lint |
PR comment |
| TypeScript 5 | Type checking | npx tsc --noEmit |
No |
6. Deploy Stages
6.1 Deployment Strategy
Strategy: Rolling (App Runner managed)
App Runner handles deployment atomically — new container version deployed, health checks verified, traffic shifted. No blue-green or canary at this stage.
App Runner Deployment:
- Trigger:
aws apprunner start-deployment(currently manual) - Health check:
GET /api/healthevery 30s - Auto-rollback: App Runner reverts if new deployment fails health checks
- Deployment time: ~3-5 minutes
Staging (Fly.io) Deployment:
- Command:
fly deploy(manual) - Region:
arn(Stockholm) - Auto-scale: Scales to 0 when idle, auto-starts on request
6.2 Environment Promotion
PR Branch → CI (auto) → Staging (manual fly deploy) → Production (manual apprunner start-deployment)
| Promotion | Trigger | Gate | Approver |
|---|---|---|---|
| → Staging | Manual | All CI checks pass | Alem Bašić |
| → Production | Manual | All CI + staging verification | Alem Bašić |
6.3 Approval Gates
Production Approval Required: Yes (manual)
Approvers: Alem Bašić (sole approver during MVP phase)
Approval Window: No automated timeout (manual process)
Emergency Override: Direct aws apprunner start-deployment via AWS Console (Alem only)
6.4 Feature Flags Integration
Feature Flag Tool: Environment variables (build-time via Next.js NEXT_PUBLIC_FF_* pattern)
| Flag | Dev | Staging | Production |
|---|---|---|---|
NEXT_PUBLIC_FF_VIRTUAL_CARDS |
false | false | false |
NEXT_PUBLIC_FF_PHYSICAL_CARDS |
false | false | false |
NEXT_PUBLIC_FF_CARD_DETAILS |
false | false | false |
NEXT_PUBLIC_FF_CARD_FREEZE |
false | false | false |
NEXT_PUBLIC_FF_CARD_PIN |
false | false | false |
NEXT_PUBLIC_FF_SPENDING_LIMITS |
false | false | false |
NEXT_PUBLIC_FF_NOTIFICATIONS |
true | true | true |
NEXT_PUBLIC_FF_MERCHANT_DASHBOARD |
true | true | true |
Note: Flags are baked in at build time by Next.js. Changing a flag requires a new build and deployment.
7. Post-Deploy
7.1 Smoke Tests
| Check | Expected | Timeout |
|---|---|---|
Health endpoint GET /api/health |
HTTP 200, "status":"ok" |
10s |
| DB connectivity (via health endpoint) | "db": {"status":"pass"} |
15s |
Auth endpoint GET /api/auth/me |
HTTP 401 (no cookie) | 10s |
| Landing page | HTTP 200 | 10s |
Smoke test timeout: 2 min total On failure: Manual rollback via App Runner console (revert to previous deployment)
7.2 Monitoring Verification
After each deployment, verify via BetterStack and Slack:
| Metric | Threshold | Check Duration |
|---|---|---|
| BetterStack health monitor | UP | 5 min post-deploy |
| Error spike detection | No alerts in Slack #drop-ops |
10 min post-deploy |
| App Runner service status | RUNNING |
Immediate |
| RDS connection | Healthy (via /api/health) |
Immediate |
7.3 Rollback Triggers
Automatic rollback triggers:
- App Runner health check failure during deployment (App Runner native)
- BetterStack detects downtime → Slack alert → manual investigation
Manual rollback:
# Rollback via AWS CLI (revert to previous ECR image)
aws apprunner start-deployment \
--service-arn arn:aws:apprunner:eu-west-1:324480209768:service/drop-web/8e45b0d335304487a1880f4e32d6aeec \
--region eu-west-1
8. Pipeline Configuration Reference
Config File Location: .github/workflows/ci.yml
Key environment variables injected by CI:
| Variable | Source | Purpose |
|---|---|---|
JWT_SECRET |
GitHub Actions Secret | Build-time placeholder (required by Next.js build) |
AWS_ROLE_ARN |
GitHub Actions Secret | OIDC role for ECR push and App Runner deploy |
SLACK_WEBHOOK |
GitHub Actions Secret | Build notifications |
9. Secret Injection Strategy
Strategy: GitHub Actions Secrets for CI variables; AWS Secrets Manager for runtime application secrets.
| Secret Type | Storage | Injection Method | Rotation |
|---|---|---|---|
| Registry credentials (ECR) | GitHub OIDC + AWS IAM | OIDC token → assumed role | Per-job (ephemeral) |
| App secrets (JWT_SECRET, DATABASE_URL) | AWS Secrets Manager | App Runner environment from Secrets Manager | 90 days |
| Slack webhook | GitHub Actions Secret | Env var injection | On compromise |
OIDC Preferred: GitHub Actions uses OIDC to assume AWS IAM role — no long-lived AWS keys stored in GitHub.
10. Pipeline Metrics
| Metric | Target | Current |
|---|---|---|
| Build duration (P50) | < 5 min | TBD |
| E2E test duration (P50) | < 10 min | TBD |
| Total pipeline duration | < 20 min | TBD |
| Deploy frequency | On-demand | TBD |
| Lead time for changes | < 1 hour | TBD |
| Change failure rate | < 10% | TBD |
| MTTR | < 30 min | TBD |
Related Documents
Approval
| Role | Name | Date | Signature |
|---|---|---|---|
| Author | Platform Architect (AI) | 2026-02-23 | |
| Reviewer | |||
| Approver | Alem Bašić |