# ADR-017: CI/CD and Branching Strategy **Date**: 2025-12-12 **Status**: Accepted **Implemented**: 2026-01-09 ## Context The project has Gitea workflows but lacks a documented standard for how code moves from a developer's machine to production. This can lead to inconsistent deployment processes and uncertainty about code quality. ## Decision We will formalize the end-to-end CI/CD process using: 1. **Trunk-Based Development**: All work is merged to `main` branch. 2. **Automated Test Deployment**: Every push to `main` triggers deployment to test environment. 3. **Manual Production Deployment**: Production deployments require explicit confirmation. 4. **Semantic Versioning**: Automated version bumping on deployments. ## Consequences - **Positive**: Automates quality control and creates a safe, repeatable path to production. Increases development velocity and reduces deployment-related errors. - **Negative**: Initial setup effort for the CI/CD pipeline. May slightly increase the time to merge code due to mandatory checks. ## Implementation Details ### Branching Strategy **Trunk-Based Development**: ```text main ─────●─────●─────●─────●─────●─────▶ │ │ │ │ │ │ │ │ │ └── Deploy to Prod (manual) │ │ │ └── v0.9.70 (patch bump) │ │ └── Deploy to Test (auto) │ └── v0.9.69 (patch bump) └── Feature complete ``` - All development happens on `main` branch - Feature branches are short-lived (< 1 day) - Every merge to `main` triggers test deployment - Production deploys are manual with confirmation ### Pipeline Stages **Deploy to Test** (Automatic on push to `main`): ```yaml jobs: deploy-to-test: steps: - Checkout code - Setup Node.js 20 - Install dependencies (npm ci) - Bump patch version (npm version patch) - TypeScript type-check - Prettier check - ESLint check - Run unit tests with coverage - Run integration tests with coverage - Run E2E tests with coverage - Merge coverage reports - Check database schema hash - Build React application - Deploy to test server (rsync) - Install production dependencies - Reload PM2 processes - Update schema hash in database ``` **Deploy to Production** (Manual trigger): ```yaml on: workflow_dispatch: inputs: confirmation: description: 'Type "deploy-to-prod" to confirm' required: true jobs: deploy-production: steps: - Verify confirmation phrase - Checkout main branch - Install dependencies - Bump minor version (npm version minor) - Check production schema hash - Build React application - Deploy to production server - Reload PM2 processes - Update schema hash ``` ### Version Bumping Strategy | Trigger | Version Change | Example | | -------------------------- | -------------- | --------------- | | Push to main (test deploy) | Patch bump | 0.9.69 → 0.9.70 | | Production deploy | Minor bump | 0.9.70 → 0.10.0 | | Major release | Manual | 0.10.0 → 1.0.0 | **Commit Message Format**: ```text ci: Bump version to 0.9.70 [skip ci] ``` The `[skip ci]` tag prevents version bump commits from triggering another workflow. ### Database Schema Management Schema changes are tracked via SHA-256 hash: ```sql CREATE TABLE public.schema_info ( environment VARCHAR(50) PRIMARY KEY, schema_hash VARCHAR(64) NOT NULL, deployed_at TIMESTAMP DEFAULT NOW() ); ``` **Deployment Checks**: 1. Calculate hash of `sql/master_schema_rollup.sql` 2. Compare with hash in target database 3. If mismatch: **FAIL** deployment (manual migration required) 4. If match: Continue deployment 5. After deploy: Update hash in database ### Quality Gates | Check | Required | Blocking | | --------------------- | -------- | ---------------------- | | TypeScript type-check | ✅ | No (continue-on-error) | | Prettier formatting | ✅ | No | | ESLint | ✅ | No | | Unit tests | ✅ | No | | Integration tests | ✅ | No | | E2E tests | ✅ | No | | Schema hash check | ✅ | **Yes** | | Build | ✅ | **Yes** | ### Environment Variables Secrets are injected from Gitea repository settings: | Secret | Test | Production | | -------------------------------------------------------------- | ------------------ | ------------- | | `DB_DATABASE_TEST` / `DB_DATABASE_PROD` | flyer-crawler-test | flyer-crawler | | `REDIS_PASSWORD_TEST` / `REDIS_PASSWORD_PROD` | \*\*\* | \*\*\* | | `VITE_GOOGLE_GENAI_API_KEY_TEST` / `VITE_GOOGLE_GENAI_API_KEY` | \*\*\* | \*\*\* | ### Coverage Reporting Coverage reports are generated and published: ```text https://flyer-crawler-test.projectium.com/coverage/ ``` Coverage merging combines: - Unit test coverage (Vitest) - Integration test coverage (Vitest) - E2E test coverage (Vitest) - Server V8 coverage (c8) ### Gitea Workflows | Workflow | Trigger | Purpose | | ----------------------------- | ------------ | ------------------------- | | `deploy-to-test.yml` | Push to main | Automated test deployment | | `deploy-to-prod.yml` | Manual | Production deployment | | `manual-db-backup.yml` | Manual | Create database backup | | `manual-db-restore.yml` | Manual | Restore from backup | | `manual-db-reset-test.yml` | Manual | Reset test database | | `manual-db-reset-prod.yml` | Manual | Reset production database | | `manual-deploy-major.yml` | Manual | Major version release | | `manual-redis-flush-prod.yml` | Manual | Flush Redis cache | ## Key Files - `.gitea/workflows/deploy-to-test.yml` - Test deployment pipeline - `.gitea/workflows/deploy-to-prod.yml` - Production deployment pipeline - `.gitea/workflows/manual-db-backup.yml` - Database backup workflow - `ecosystem.config.cjs` - PM2 configuration ## Related ADRs - [ADR-014](./0014-containerization-and-deployment-strategy.md) - Containerization Strategy - [ADR-010](./0010-testing-strategy-and-standards.md) - Testing Strategy - [ADR-019](./0019-data-backup-and-recovery-strategy.md) - Backup Strategy