Skip to main content

CI/CD Pipeline

CI/CD Pipeline

Project: Drop{{PROJECT_NAME}} Version: 0.1.0{{VERSION}} Date: 2026-02-23{{DATE}} Author: Platform Architect (AI){{AUTHOR}} Status: Draft | In Review | Approved Reviewers: Alem Bašić (CEO){{REVIEWERS}}

Document History

Version Date Author Changes
0.1 2026-02-23{{DATE}} Platform Architect (AI){{AUTHOR}} 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{{PLATFORM}} Actions (.github/workflows/ci.yml) Container Registry: AWS{{REGISTRY}} ECR (324480209768.dkr.ecr.eu-west-1.amazonaws.com/drop-web) Deployment Target (production):Target: AWS{{DEPLOY_TARGET}} App Runner (eu-west-1) Deployment Target (staging): Fly.io (Stockholm, region arn) Strategy: Rolling{{STRATEGY}} (App Runner managed)


2. Pipeline Overview

flowchart LR
    subgraph Source
        PR[Pull Request\nto main/master]Request]
        MERGE[PushMerge to\nmain/master]to main]
    end

    subgraph CI [CI["CI — runs on every push/PR"]
        LINT[1.Lint lint-and-typecheck\nnpm& runFormat]
        lint\ntscTEST_UNIT[Unit --noEmit]Tests]
        TEST[2.TEST_INT[Integration test\nvitestTests]
        run]SAST[SAST Scan]
        SCA[Dependency Scan]
        BUILD[3.Build build\nnext build]
        E2E[4. e2e\nPlaywright\nuser-flows + full-flows]
        DOCKER[5. docker-build\ndocker build -t drop-app:ci]Artifact]
    end

    subgraph CD_STG [CD_DEV["CD Staging manual"Dev Auto-Deploy"]
        STG[flyDEPLOY_DEV[Deploy deploy\nFly.ioto Stockholm]Dev]
        SMOKE_DEV[Smoke Tests]
    end

    subgraph CD_PROD [CD_STAGING["CD Production— Staging (auto on main)"]
        DEPLOY_STG[Deploy to Staging]
        TEST_E2E[E2E Tests]
        PERF[Performance Tests]
    end

    subgraph CD_PROD["CDpendingProduction implementation"(manual gate)"]
        APPROVAL[Manual Approval\nviaApproval]
        GitHubDEPLOY_PROD[Deploy Environments]to PROD[awsProduction]
        apprunner\nstart-deployment]SMOKE_PROD[Smoke Tests]
        MONITOR[Verify Monitoring]
    end

    PR --> LINT
    MERGE --> LINT
    LINT --> TESTTEST_UNIT
    LINTTEST_UNIT --> TEST_INT
    TEST_INT --> SAST
    SAST --> SCA
    SCA --> BUILD
    TESTMERGE --> E2ECD_DEV
    BUILD --> E2EDEPLOY_DEV
    E2EDEPLOY_DEV --> DOCKERSMOKE_DEV
    DOCKERSMOKE_DEV --> STGDEPLOY_STG
    DOCKERDEPLOY_STG --> TEST_E2E
    TEST_E2E --> PERF
    PERF --> APPROVAL
    APPROVAL --> PRODDEPLOY_PROD
    DEPLOY_PROD --> SMOKE_PROD
    SMOKE_PROD --> MONITOR

3. Source Control Configuration

3.1 Branching Strategy

Strategy: GitHub{{BRANCH_STRATEGY}} Flow (feature branches off main, PR into main)

Branch Purpose Naming Convention Lifetime
main Production-ready code fixed Permanent
developIntegration branchfixedPermanent
feature/* New features feature/{{TICKET}}-description Until merged
fix/* Bug fixes fix/{{TICKET}}-description Until merged
hotfix/* Production hotfixes hotfix/{{TICKET}}-descriptionUntil merged
release/*Release preparationrelease/v{{VERSION}} Until merged

3.2 Branch Protection Rules

Protected Branches: main, develop

docker-build
Rule maindevelop
Require PR Yes Yes
Required approvals {{APPROVALS}}1 (Alem Bašić)
Dismiss stale reviewsYes Yes
Require status checks Yes Yes
Required checks lint-and-typecheck,lint, test,unit-tests, build,integration-tests, e2e,sast lint, unit-tests
Require up-to-date Yes No
Allow force push No No
Allow deletionsNo No

3.3 Code Review Requirements

  • Minimum 1{{APPROVALS}} approvalapproval(s) required before merge
  • At least one approval from Alema Bašićcode owner (orsee delegated lead)CODEOWNERS)
  • All review comments must be resolved before merge
  • Review turnaround SLA: 24{{REVIEW_SLA}} business hours
  • Auto-assign reviewers via: {{ASSIGN_MECHANISM}}

4. Build Stage

4.1 Build Tool & Configuration

Parameter Value
Build Tool npm{{BUILD_TOOL}} (Node.js 22)
Install Commandnpm ci (requires python3, make, g++ for better-sqlite3 native deps)
Build Command npm run build{{BUILD_CMD}} (Next.js standalone output)
Artifact Type Docker{{ARTIFACT}} image (multi-stage: deps → builder → runner)
Artifact Naming 324480209768.dkr.ecr.eu-west-1.amazonaws.com/drop-web:<git-sha>{{REGISTRY}}/{{IMAGE_NAME}}:{{TAG_STRATEGY}}
Tag Strategy git-sha for CIPRs, builds, semantic versionsemver for releases
Base Imagenode:22-alpine
Production Image Usernextjs (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') OS}}-{{LOCKFILE_HASH}} node-modules-linux-{{OS}}-
Docker build layers BuildKit layer cache via cache-from: type=ghabuildx-{{DOCKERFILE_HASH}} buildx-
Test resultstest-results-{{COMMIT_SHA}}N/A

4.3 Artifact Generation

Artifact Storage Retention Signed
Docker image AWS ECR drop-web repository{{REGISTRY}} 90 days (non-prod tags)prod), Forever (releaseprod tags) No (planned){{SIGNING}}
PlaywrightTest HTML reportreports GitHub ActionsCI artifact playwright-report/storage 730 days No
QA report (qa-report.html)SBOM GitHub Actions artifact{{SBOM_STORAGE}} 71 yearYes
Coverage report{{COVERAGE_STORAGE}}30 days No

5. Test Stages

5.1 Unit Tests

Parameter Value
Framework Vitest 4.0.18{{UNIT_FRAMEWORK}}
Command npm test{{UNIT_CMD}} (runs vitest run)
Test filestests/**/*.test.ts
Setuptests/setup.ts — sets NODE_ENV=test, in-memory databases
Coverage Tool TBD — not yet configured{{COVERAGE_TOOL}}
Coverage Gate TBD {{COVERAGE_GATE}}% requireslines, coverage configuration{{BRANCH_GATE}}% branches
Failure Action Block PR merge (CI job 2 test must pass)

5.2 Integration Tests

Parameter Value
Framework Vitest (same runner as unit tests){{INT_FRAMEWORK}}
Command npm test{{INT_CMD}}
Dependencies SQLite{{INT_DEPS}} in-memory (no external services needed)
Failure Action Block PR merge

5.3 E2E Tests

Parameter Value
Framework Playwright{{E2E_FRAMEWORK}} 1.58.2
Command npx playwright test user-flows full-flows{{E2E_CMD}}
BrowserChromium only (installed in CI via npx playwright install chromium)
Environment Production build running locally (npm run build && npm run start)Staging
Parallelization 1{{E2E_SHARDS}} worker (serial — rate limiter is shared state)
Retries2 (CI)
Timeout30,000ms
Base URLhttp://localhost:3000
Test suitesuser-flows.spec.ts, full-flows.spec.ts, input-chaos.spec.ts (depends on user-flows)
ArtifactsPlaywright HTML report + qa-report.html uploaded to GitHub Actionsshards
Failure Action Block docker-buildstaging jobpromotion

5.4 Security Scanning

Scan Type Tool StatusCommand Gate
SAST TBD{{SAST_TOOL}} — not yet implemented Pending{{SAST_CMD}} Block on HIGH/CRITICAL
SCA (dependencies) npm{{SCA_TOOL}} audit — not yet in CI Pending{{SCA_CMD}} Block on CRITICAL
Container scan TBD{{CONTAINER_SCAN}} (Trivy planned) Pending{{CONTAINER_SCAN_CMD}} Block on CRITICAL
Secret scanning GitHub{{SECRET_SCAN}} native secret scanning Enabled via GitHub{{SECRET_SCAN_CMD}} Block on any finding

Note: Security scanning tracked in security/hardening-checklist.md. npm audit and Snyk integration are pending.

5.5 Linting & Formatting

5
Tool Purpose Command Auto-fix
ESLint 9{{LINTER}} Code linting npm run lint{{LINT_CMD}} PR comment
TypeScript{{FORMATTER}} Code formatting{{FMT_CMD}}Auto-commit or fail
{{TYPE_CHECK}} Type checking npx tsc --noEmit{{TYPE_CMD}} No

6. Deploy Stages

6.1 Deployment Strategy

Strategy: Rolling{{DEPLOY_STRATEGY}} (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 RunnerRolling Deployment:

  • Trigger:Batch awssize: apprunner{{BATCH_SIZE}}% start-deploymentof (currentlyinstances
  • manual)
  • Pause between batches: {{PAUSE}}min
  • Health check:check GETwait: /api/health every 30s{{HEALTH_WAIT}}s
  • Auto-rollback:Rollback App Runner reverts if new deployment failstrigger: health checks
  • check
  • Deployment time: ~3-5 minutesfailure

StagingCanary Deployment (Fly.io)if Deployment:used):

  • Command:Initial flycanary deployweight: (manual){{CANARY_INITIAL}}%
  • Region:Increment: arn{{CANARY_INCREMENT}}% (Stockholm)every {{CANARY_INTERVAL}}min
  • Auto-scale:Promotion Scalescriteria: toerror 0rate when< idle,{{ERROR_THRESHOLD}}%, auto-startsp99 < {{LATENCY_THRESHOLD}}ms
  • Rollback trigger: automatic on requestthreshold breach

6.2 Environment Promotion

PR Branch → CIDev (auto) → Staging (manualauto flyon deploy)main merge) → Production (manual apprunner start-deployment)approval)
Bašić
Promotion Trigger Gate Approver
StagingDev ManualMerge to develop / PR All CI checks pass AlemAutomatic
→ StagingMerge to mainAll CI + Dev smoke testsAutomatic
→ Production ManualTag v*.*.* All CItests + stagingmanual verificationapproval Alem Bašić{{PROD_APPROVER}}

6.3 Approval Gates

Production Approval Required: Yes (manual) Approvers: Alem Bašić{{PROD_APPROVERS}} (soleat approverleast during{{APPROVAL_COUNT}} MVP phase)required) Approval Window: No automated timeout{{APPROVAL_WINDOW}}h (manualpipeline process)cancels after timeout) Emergency Override: Direct{{EMERGENCY_OVERRIDE}} aws apprunner start-deployment via AWS Console (Alem only)

6.4 Feature Flags Integration

Feature Flag Tool: Environment{{FF_TOOL}} variables (build-timeFlag via Next.js NEXT_PUBLIC_FF_* pattern)

FlagDevStagingProduction
NEXT_PUBLIC_FF_VIRTUAL_CARDSfalsefalsefalse
NEXT_PUBLIC_FF_PHYSICAL_CARDSfalsefalsefalse
NEXT_PUBLIC_FF_CARD_DETAILSfalsefalsefalse
NEXT_PUBLIC_FF_CARD_FREEZEfalsefalsefalse
NEXT_PUBLIC_FF_CARD_PINfalsefalsefalse
NEXT_PUBLIC_FF_SPENDING_LIMITSfalsefalsefalse
NEXT_PUBLIC_FF_NOTIFICATIONStruetruetrue
NEXT_PUBLIC_FF_MERCHANT_DASHBOARDtruetruetrue

Note:Validation: FlagsFeature areflags bakedvalidated in atstaging buildbefore timeproduction bydeploy Next.js.Kill ChangingSwitch: a flag requires aAll new buildfeatures andbehind deployment.flags for first {{FF_PERIOD}} days


7. Post-Deploy

7.1 Smoke Tests

Check Expected Timeout
Health endpoint GET /api/health HTTP 200, "status":"ok"200 10s
DBAuth connectivityendpoint (via health endpoint)reachable "db":HTTP {"status":"pass"}40110s
Database connectionHealthy 15s
AuthCache endpoint GET /api/auth/meconnection HTTP 401 (no cookie)Healthy 10s
LandingCritical pageuser journey HTTP 200Success 10s60s

Smoke test timeout: 2 {{SMOKE_TIMEOUT}}min total On failure: Manual Auto-rollback via App Runner console (revert to previous deployment)triggered

7.2 Monitoring Verification

After each deployment, verify via BetterStack and Slack:

Metric Threshold Check Duration
BetterStackError health monitorrate UP< {{ERROR_RATE}}% 5 min post-deploy
ErrorP99 spike detectionlatency No< alerts in Slack #drop-ops{{P99}}ms 105 min post-deploy
AppCPU Runner service statusutilization RUNNING< {{CPU}}% Immediate5 min
RDSMemory connectionutilization Healthy< (via /api/health){{MEM}}% Immediate5 min

7.3 Rollback Triggers

Automatic rollback triggers:

  • AppSmoke Runnertest healthfailure
  • Error rate > {{AUTO_ROLLBACK_ERROR}}% for {{AUTO_ROLLBACK_DURATION}}min post-deploy
  • Health check failure duringon deployment{{HEALTH_FAIL_THRESHOLD}}% (Appof Runner native)
  • BetterStack detects downtime → Slack alert → manual investigationinstances

Manual rollback: See rollback-plan.md

# 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{{CONFIG_PATH}}

Key environment variables injected by CI:

forECRpushandApp
Variable Source Purpose
JWT_SECRETREGISTRY_TOKEN GitHub Actions Secret{{SECRET_STORE}} Build-timeContainer placeholderregistry (required by Next.js build)auth
AWS_ROLE_ARNDEPLOY_KEY GitHub Actions Secret{{SECRET_STORE}} OIDCDeployment rolecredentials
SENTRY_DSN {{SECRET_STORE}} Error Runner deploytracking
SLACK_WEBHOOK GitHub Actions Secret{{SECRET_STORE}} Build notificationsNotifications

9. Secret Injection Strategy

Strategy: GitHub{{SECRET_STRATEGY}} Actions Secrets for CI variables; AWS Secrets Manager for runtime application secrets.

OIDC+AWSIAM
Secret Type Storage Injection Method Rotation
Registry credentials (ECR) GitHub{{STORAGE}} {{METHOD}} {{ROTATION}}
Cloud credentials{{STORAGE}} OIDC token/ Workload assumed roleIdentity Per-job (ephemeral)
App secrets (JWT_SECRET, DATABASE_URL) AWS Secrets Manager{{STORAGE}} App Runner environment from Secrets Manager{{METHOD}} 90 days
Slack webhookGitHub Actions SecretEnv var injectionOn compromise{{ROTATION}}

OIDC Preferred: GitHubCloud Actionscredentials usesinjected via OIDC to assume AWS IAM role — no long-lived AWS keys stored in GitHub.CI


10. Pipeline Metrics

Metric Target Current
Build duration (P50) < 5 {{BUILD_TARGET}}min TBD
E2E testTest duration (P50) < 10 {{TEST_TARGET}}min TBD
Total pipeline duration < 20 {{TOTAL_TARGET}}min TBD
Deploy frequency On-demand{{DEPLOY_FREQ}} TBD
Lead time for changes < 1 hour{{LEAD_TIME}} TBD
Change failure rate < 10%{{FAILURE_RATE}}% TBD
MTTR < 30 min{{MTTR}} TBD


Approval

Role Name Date Signature
Author Platform Architect (AI) 2026-02-23
Reviewer
Approver Alem Bašić