# 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
