Back to catalog

ci-cd

Skill from vincentbollaert/turborepo-monorepo

  • Category: discovered
  • Tags: github, community
  • Version: 1.0.0
  • Author: walid
  • Downloads: 0
  • GitHub stars: 2
  • Updated: 2025-11-21

Download the latest version (v1.0.0) from this page.

SKILL.md

# CI/CD Pipelines

**Auto-detection:** GitHub Actions, CI/CD pipelines, Turborepo affected detection, Vercel remote cache, deployment automation, quality gates

**When to use:**

- Setting up GitHub Actions workflows with Bun and Turborepo
- Implementing Turborepo affected detection for faster PR builds
- Configuring Vercel remote cache for team sharing
- Setting up quality gates and branch protection rules

**Key patterns covered:**

- GitHub Actions with Bun 1.2.2 and Turborepo caching
- Affected detection (turbo run test --filter=...[origin/main] for PRs)
- Vercel remote cache (free, zero-config for Turborepo)
- Quality gates (lint, type-check, test, build - parallel jobs with dependencies)

---

# CI/CD Pipelines

> **Quick Guide:** GitHub Actions with Bun 1.2.2 for CI. Turborepo affected detection for monorepo optimization. Vercel remote cache (free). Quality gates: lint + type-check + test + build + coverage. Multi-environment deployments (preview/staging/prod). Secret scanning and dependency audits.

**CURRENT STATE: NO CI/CD IMPLEMENTED**

This document outlines **recommended best practices** for CI/CD pipelines in a Turborepo monorepo using Bun.

---

## Pipeline Configuration

**RECOMMENDED: GitHub Actions with Bun 1.2.2**

**Location:** `.github/workflows/` directory

### CI Platform Choice

**Recommended: GitHub Actions** (native integration, free tier, marketplace, secrets management)

**Alternatives:** GitLab CI, CircleCI, Buildkite, Azure Pipelines

### Workflow Structure

**Pattern: Separate workflows for different concerns**

**Recommended workflows:**

- `ci.yml` - Continuous integration (lint, test, type-check, build)
- `preview.yml` - Preview deployments for pull requests
- `deploy.yml` - Production deployment from main branch
- `release.yml` - Semantic versioning and changelog generation
- `dependabot.yml` - Automated dependency updates (optional)

### Job Dependencies

**Pattern: Parallel jobs with dependencies**

```yaml
jobs:
  install:
    # Install dependencies once, cache for other jobs

  lint:
    needs: install
    # Run in parallel with test and type-check

  test:
    needs: install
    # Run in parallel with lint and type-check

  type-check:
    needs: install
    # Run in parallel with lint and test

  build:
    needs: [lint, test, type-check]
    # Only run after all checks pass
```

### Caching Strategies

**CRITICAL: Proper caching dramatically speeds up CI**

**What to cache:**

1. **Bun dependencies** - `~/.bun/install/cache/`
2. **Turborepo cache** - `.turbo/` directory
3. **Next.js cache** - `.next/cache/` (per app)
4. **TypeScript incremental builds** - `tsconfig.tsbuildinfo`

**Cache invalidation:**

- Dependencies: Hash of `bun.lockb`
- Source code: Turborepo handles automatically
- Build cache: Based on input files

**Cache strategy:**

```yaml
- uses: actions/cache@v4
  with:
    path: ~/.bun/install/cache
    key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lockb') }}
    restore-keys: |
      ${{ runner.os }}-bun-
```

### Environment Variables

**Pattern: Use GitHub secrets for sensitive data**

**Types of variables:**

- **Secrets** - API keys, tokens (encrypted, not visible in logs)
- **Variables** - Non-sensitive config (visible in logs)
- **Environment-specific** - Different values per environment

**Example:**

```yaml
env:
  DATABASE_URL: ${{ secrets.DATABASE_URL }}
  NODE_ENV: production
  NEXT_PUBLIC_API_URL: https://api.example.com
```

**Best practices:**

- Never commit secrets to repository
- Use different secrets per environment (staging vs production)
- Rotate secrets regularly
- Use least-privilege access (don't share prod secrets with preview deploys)

---

## Affected Detection

**CRITICAL: Only test/build changed packages in monorepo**

**Pattern: Turborepo filters with git comparison**

### Turborepo Filter Syntax

**Filter options:**

```bash
# All packages affected since main branch
turbo run test --filter=...[origin/main]

# Specific package and its dependencies
turbo run build --filter=@repo/ui...

# Specific package and its dependents
turbo run test --filter=...@repo/api

# Multiple filters
turbo run lint --filter=@repo/ui... --filter=...@repo/api
```

**Syntax breakdown:**

- `...` - Include dependents
- `[origin/main]` - Compare against main branch
- `^` - Include dependencies

### PR vs Main Branch Strategy

**Pull Requests:**

- Run affected tests: `--filter=...[origin/main]`
- Only test what changed + dependents
- Fast feedback loop

**Main branch:**

- Run full test suite: `turbo run test`
- Ensure everything still works together
- Catch integration issues

**Why:**

- PRs need fast feedback (< 5 minutes ideal)
- Main branch needs comprehensive validation
- Balance speed vs thoroughness

### Handling New Packages

**Problem:** New packages have no git history, may be skipped by affected detection

**Solution:**

```yaml
# Check if this is a new package
- name: Detect new packages
  run: |
    if git diff --name-only origin/main | grep -q "packages/.*/package.json"; then
      echo "NEW_PACKAGE=true" >> $GITHUB_ENV
    fi

# Run full tests if new package detected
- name: Test
  run: |
    if [ "$NEW_PACKAGE" = "true" ]; then
      turbo run test
    else
      turbo run test --filter=...[origin/main]
    fi
```

### Affected Build Optimization

**Pattern: Only build what needs deploying**

```bash
# Build only affected apps (not packages)
turbo run build --filter=./apps/*...[origin/main]

# Build specific app and its dependencies
turbo run build --filter=@repo/client-next...
```

**Benefits:**

- Faster CI (don't build unchanged apps)
- Faster deployments (only deploy what changed)
- Reduced resource usage

---

## Remote Caching

**RECOMMENDED: Vercel Remote Cache (free for Turborepo)**

**Pattern: Share build cache across team and CI**

### Cache Providers

**Vercel (recommended):**

- ✅ Free for Turborepo users
- ✅ Zero configuration
- ✅ Global CDN
- ✅ Team sharing built-in

**Self-hosted alternatives:**

- **AWS S3** - Pay per storage/transfer
- **Google Cloud Storage** - Similar to S3
- **Custom server** - Full control, more work

### Setup Remote Caching

**Step 1: Sign up for Vercel**

```bash
bun add -g vercel
vercel login
vercel link
```

**Step 2: Get team/token**

```bash
# Get your team ID and token
vercel team ls
```

**Step 3: Configure CI**

```yaml
env:
  TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
  TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
```

**Step 4: Enable in turbo.json**

```json
{
  "remoteCache": {
    "signature": true
  }
}
```

### Cache Hit Optimization

**What affects cache hits:**

- Input files (source code, configs)
- Environment variables (declared in `turbo.json`)
- Dependencies (package.json, lockfile)
- Task outputs

**Best practices:**

- Declare all env vars in `turbo.json` (cache invalidation)
- Use deterministic builds (no timestamps in output)
- Don't cache tasks with side effects (`cache: false`)
- Use granular tasks (separate lint/test/build)

**Cache hit rate:**

- 80%+ is excellent
- 50-80% is good
- <50% needs optimization

### Cache Invalidation

**Automatic invalidation:**

- Source file changes
- Dependency updates
- Environment variable changes (if declared)
- Turborepo version changes

**Manual invalidation:**

```bash
# Clear local cache
turbo run build --force

# Bypass remote cache
TURBO_FORCE=true turbo run build
```

---

## Quality Gates

**MANDATORY: Automated checks before merge**

### Required Checks

**Minimum required checks:**

1. **Linting** - Code style, no errors

   - `turbo run lint`
   - Must pass, no warnings allowed in CI

2. **Type checking** - TypeScript compilation

   - `turbo run type-check` (or `tsc --noEmit`)
   - Zero TypeScript errors

3. **Tests** - Unit and integration tests

   - `turbo run test`
   - All tests must pass

4. **Build** - Production build succeeds
   - `turbo run build`
   - Build must complete without errors

**Optional but recommended:** 5. **Coverage thresholds** - Minimum test coverage 6. **Bundle size check** - Prevent bundle bloat 7. **Security audit** - Check for vulnerabilities 8. **Accessibility audit** - Check for a11y issues

### Coverage Thresholds

**Pattern: Enforce minimum test coverage**

**Recommended thresholds:**

- Statements: 80%
- Branches: 75%
- Functions: 80%
- Lines: 80%

**Configuration:**

```json
{
  "coverageThreshold": {
    "global": {
      "statements": 80,
      "branches": 75,
      "functions": 80,
      "lines": 80
    }
  }
}
```

**Why:**

- Prevents untested code from merging
- Encourages comprehensive testing
- Catches regression in coverage

### Branch Protection Rules

**GitHub branch protection (recommended):**

**For `main` branch:**

- ✅ Require pull request before merging
- ✅ Require status checks to pass
  - Lint
  - Type check
  - Test
  - Build
- ✅ Require branches to be up to date
- ✅ Require linear history (no merge commits)
- ❌ Allow force pushes (dangerous!)
- ✅ Require code review (1+ approvers)

**For `develop` branch:**

- ✅ Require pull request
- ✅ Require status checks
- ❌ Require code review (optional, for faster iteration)

### Automated vs Manual Gates

**Automated gates:**

- Lint, test, type-check, build (always automated)
- Coverage thresholds
- Bundle size checks
- Security scans

**Manual gates:**

- Code review (human judgment)
- QA testing (exploratory testing)
- Product approval (feature validation)
- Security review (sensitive changes)

**Best practice:**

- Automate everything possible
- Use manual gates for human judgment only
- Don't block on manual gates for hotfixes

---

## Deployment Workflows

**RECOMMENDED: Automatic deployment from main, manual approval for production**

### Branch Deployment Strategy

**Recommended strategy:**

```
main branch → Production (automatic)
└── Requires: All checks pass + approval

develop branch → Staging (automatic)
└── Requires: All checks pass

feature/* → Preview (automatic on PR)
└── Requires: None (for testing)
```

**Why:**

- Fast feedback with preview deploys
- Staging mirrors production config
- Production requires approval (safety)

**Alternative strategies:**

**Trunk-based development:**

- All commits to `main`
- Deploy to production automatically
- Feature flags for incomplete features

**GitFlow:**

- `develop` → staging
- `release/*` → UAT
- `main` → production (tag-based)

### Environment Promotion

**Pattern: Promote builds through environments**

```
Code → Build → Test → Deploy Staging → Test Staging → Deploy Prod
```

**Why:**

- Same build artifact across environments
- Reduces "works on my machine" issues
- Faster deployments (no rebuild)

**Implementation:**

- Build once in CI
- Upload artifact
- Download and deploy to each environment
- Use environment-specific config (env vars)

### Rollback Procedures

**CRITICAL: Always have rollback plan**

**Strategies:**

1. **Revert commit** - Git revert, trigger new deploy

   - Simple, clean git history
   - Slow (requires new build)

2. **Redeploy previous version** - Vercel/Netlify built-in

   - Fast (instant rollback)
   - Easy with platforms like Vercel

3. **Blue-green deployment** - Two identical environments
   - Zero downtime
   - Instant switchback
   - More complex setup

**Best practice:**

- Use platform rollback for speed (Vercel, Netlify)
- Test rollback procedure regularly
- Monitor post-deployment (catch issues early)

### Preview Deployments

**MANDATORY for PR-based workflow**

**Pattern: Deploy every PR to unique URL**

**Benefits:**

- Visual review before merge
- Test on production-like environment
- Share with stakeholders
- Catch issues early

**Providers:**

- **Vercel** - Automatic preview deploys for Next.js
- **Netlify** - Automatic deploy previews
- **Cloudflare Pages** - Preview deployments
- **Custom** - Deploy to subdomain per PR

**URL patterns:**

- `pr-123.example.com`
- `feature-auth.staging.example.com`
- `pr-123-client-next.vercel.app`

**Cleanup:**

- Delete preview deploys after PR merge/close
- Saves costs
- Reduces clutter

---

## Security and Secrets

**CRITICAL: Never commit secrets**

### Secret Management

**GitHub Secrets:**

- Encrypted at rest
- Masked in logs
- Per-environment secrets
- Audit log

**Best practices:**

- Use least-privilege access (separate secrets per environment)
- Rotate secrets regularly (quarterly minimum)
- Don't share secrets across repos
- Use environment protection rules (require approval for production secrets)

### Dependency Security

**Automated scanning:**

**Dependabot:**

- Automatic security updates
- Creates PRs for vulnerable dependencies
- Free for GitHub

**Alternative tools:**

- **Snyk** - Comprehensive security scanning
- **npm audit** - Built-in npm vulnerability check
- **bun audit** - Bun vulnerability scanning

**Best practice:**

- Enable Dependabot
- Review security PRs within 24 hours
- Auto-merge low-risk patches

### Code Scanning

**GitHub Advanced Security:**

- CodeQL scanning (free for public repos)
- Secret scanning
- Dependency review

**Configuration:**

```yaml
# .github/workflows/codeql.yml
- uses: github/codeql-action/analyze@v3
```

---

## Performance Optimization

**Goal: CI runtime < 5 minutes**

### Parallelization

**Strategies:**

- Run independent jobs in parallel (lint || test || type-check)
- Use matrix builds for multiple Node/Bun versions
- Split test suites (unit || integration || e2e)

### Resource Management

**Runners:**

- Use GitHub-hosted runners (free, no maintenance)
- Use self-hosted runners for speed (private repos, large teams)
- Use larger runners for build-heavy tasks

**Concurrency:**

```yaml
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true
```

**Why:**

- Cancels outdated runs (saves resources)
- Only run latest commit

### Monitoring and Metrics

**Track:**

- CI runtime per workflow
- Cache hit rate
- Failure rate per check
- Time to deploy

**Tools:**

- GitHub Insights (built-in)
- Datadog CI Visibility
- CircleCI Insights

**Targets:**

- CI runtime: < 5 minutes
- Cache hit rate: > 80%
- Failure rate: < 5%
- Time to deploy: < 10 minutes

---

## Common Anti-Patterns

**❌ NEVER do these:**

- Running full test suite on every PR (use affected detection)
- Not using caching (wastes time and money)
- Building dependencies separately (use Turborepo pipeline)
- Rebuilding for each environment (build once, deploy many)
- Committing secrets to repository
- Not testing rollback procedures
- Running CI on draft PRs (waste of resources)
- Not parallelizing independent jobs
- Using `latest` Docker tags in CI (non-deterministic)
- Not setting concurrency limits (multiple runs on same PR)

---

## Resources

**Official documentation:**

- GitHub Actions: https://docs.github.com/en/actions
- Turborepo CI/CD: https://turbo.build/repo/docs/ci
- Vercel Deployment: https://vercel.com/docs/deployments

**Tools:**

- Bun CI setup: https://bun.sh/docs/install/ci
- Dependabot: https://docs.github.com/en/code-security/dependabot

**Best practices:**

- GitHub Actions Best Practices: https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions


---

# CI/CD Pipelines - Examples

---

## Pipeline Configuration

### Example: Deployment to Production

```yaml
# .github/workflows/deploy.yml
name: Deploy to Production

on:
  push:
    branches: [main]

concurrency:
  group: production-deployment
  cancel-in-progress: false  # Don't cancel production deploys

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production  # Requires approval

    steps:
      - uses: actions/checkout@v4

      - uses: oven-sh/setup-bun@v1
        with:
          bun-version: 1.2.2

      - name: Install dependencies
        run: bun install --frozen-lockfile

      - name: Run tests
        run: bun run test

      - name: Build for production
        run: bun run build
        env:
          NODE_ENV: production
          NEXT_PUBLIC_API_URL: ${{ secrets.PROD_API_URL }}
          DATABASE_URL: ${{ secrets.DATABASE_URL }}

      - name: Deploy to Vercel
        uses: amondnet/vercel-action@v25
        with:
          vercel-token: ${{ secrets.VERCEL_TOKEN }}
          vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
          vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
          vercel-args: "--prod"

      - name: Notify Slack on Success
        if: success()
        uses: 8398a7/action-slack@v3
        with:
          status: success
          text: "🚀 Production deployment successful!"
          webhook_url: ${{ secrets.SLACK_WEBHOOK }}

      - name: Notify Slack on Failure
        if: failure()
        uses: 8398a7/action-slack@v3
        with:
          status: failure
          text: "❌ Production deployment failed!"
          webhook_url: ${{ secrets.SLACK_WEBHOOK }}
```

**Why:**
- Automatic deployments to production from main
- Uses `environment: production` for approval gates
- Notifies team on success/failure
- Uses concurrency to prevent overlapping deploys

**Edge Cases:**
- Use `cancel-in-progress: false` for production (don't cancel ongoing deploys)
- Require environment approval for safety
- Tag releases with semantic versioning

---

### Example: Matrix Build (Multiple Environments)

```yaml
# .github/workflows/matrix.yml
name: Matrix Build

on:
  pull_request:
    branches: [main]

jobs:
  test-matrix:
    runs-on: ${{ matrix.os }}

    strategy:
      matrix:
        os: [ubuntu-latest, macos-latest, windows-latest]
        bun-version: [1.2.2, latest]

    steps:
      - uses: actions/checkout@v4

      - uses: oven-sh/setup-bun@v1
        with:
          bun-version: ${{ matrix.bun-version }}

      - name: Install dependencies
        run: bun install --frozen-lockfile

      - name: Run tests
        run: bun run test

      - name: Build
        run: bun run build
```

**Why:** Tests across multiple OS and Bun versions. Catches platform-specific issues.

**Edge Cases:**
- Matrix builds can be expensive (use sparingly)
- Only run on main branch or schedule (not every PR)
- Consider using `fail-fast: false` to see all failures

---

## Affected Detection

### Example: RECOMMENDED Turborepo Affected Detection

**RECOMMENDED for Turborepo monorepos:**

```yaml
# .github/workflows/affected.yml
name: CI - Affected Packages

on:
  pull_request:
    branches: [main]

jobs:
  affected:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0  # CRITICAL: Fetch all history for git diff

      - uses: oven-sh/setup-bun@v1
        with:
          bun-version: 1.2.2

      - name: Cache Bun dependencies
        uses: actions/cache@v4
        with:
          path: ~/.bun/install/cache
          key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lockb') }}

      - name: Install dependencies
        run: bun install --frozen-lockfile

      # Run affected tests (changed packages + dependents)
      - name: Test affected packages
        run: bunx turbo run test --filter=...[origin/main]

      # Run affected lint
      - name: Lint affected packages
        run: bunx turbo run lint --filter=...[origin/main]

      # Run affected type-check
      - name: Type-check affected packages
        run: bunx turbo run type-check --filter=...[origin/main]

      # Build affected apps (not packages)
      - name: Build affected apps
        run: bunx turbo run build --filter=./apps/*...[origin/main]
```

**Why:**
- Only tests changed code (fast feedback)
- `--filter=...[origin/main]` includes changed packages + dependents
- Separate build for apps only (don't build library packages)
- Uses `bunx turbo` instead of global installation

**Edge Cases:**
- Must use `fetch-depth: 0` for git history
- On main branch, run full test suite (not affected)
- Handle new packages correctly (no git history)

---

### Example: Handling New Packages

```yaml
# .github/workflows/ci-smart.yml
name: CI - Smart Detection

on:
  pull_request:
    branches: [main]