home / skills / eddiebe147 / claude-settings / ci-cd-pipeline-builder

ci-cd-pipeline-builder skill

/skills/ci-cd-pipeline-builder

This skill helps you design and automate robust CI/CD pipelines across GitHub Actions and Vercel for reliable builds, tests, and deployments.

npx playbooks add skill eddiebe147/claude-settings --skill ci-cd-pipeline-builder

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
17.0 KB
---
name: ci-cd-pipeline-builder
description: Expert guide for building CI/CD pipelines with GitHub Actions, Vercel, and other platforms. Use when automating builds, tests, deployments, or release workflows.
---

# CI/CD Pipeline Builder Skill

## Overview

This skill helps you build robust CI/CD pipelines for automated testing, building, and deployment. Covers GitHub Actions, Vercel integration, testing strategies, deployment patterns, and security best practices.

## CI/CD Philosophy

### Pipeline Principles
1. **Fast feedback**: Fail early, inform quickly
2. **Reproducible**: Same inputs = same outputs
3. **Secure**: Secrets protected, dependencies verified
4. **Observable**: Clear logs, status visibility

### Pipeline Stages
```
Trigger → Lint → Test → Build → Deploy → Verify
```

## GitHub Actions Fundamentals

### Basic Workflow Structure

```yaml
# .github/workflows/ci.yml
name: CI

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

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

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
      - run: npm ci
      - run: npm run lint

  test:
    runs-on: ubuntu-latest
    needs: lint
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
      - run: npm ci
      - run: npm test

  build:
    runs-on: ubuntu-latest
    needs: [lint, test]
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
      - run: npm ci
      - run: npm run build
```

### Complete Next.js CI/CD Pipeline

```yaml
# .github/workflows/ci-cd.yml
name: CI/CD Pipeline

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

env:
  NODE_VERSION: '20'
  VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
  VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}

jobs:
  # ===================
  # Quality Checks
  # ===================
  quality:
    name: Code Quality
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Type check
        run: npm run typecheck

      - name: Lint
        run: npm run lint

      - name: Format check
        run: npm run format:check

  # ===================
  # Unit & Integration Tests
  # ===================
  test:
    name: Tests
    runs-on: ubuntu-latest
    needs: quality
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Run tests
        run: npm run test:ci

      - name: Upload coverage
        uses: codecov/codecov-action@v3
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
          files: ./coverage/lcov.info
          fail_ci_if_error: false

  # ===================
  # Build
  # ===================
  build:
    name: Build
    runs-on: ubuntu-latest
    needs: [quality, test]
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Build
        run: npm run build
        env:
          NEXT_PUBLIC_SUPABASE_URL: ${{ secrets.NEXT_PUBLIC_SUPABASE_URL }}
          NEXT_PUBLIC_SUPABASE_ANON_KEY: ${{ secrets.NEXT_PUBLIC_SUPABASE_ANON_KEY }}

      - name: Upload build artifact
        uses: actions/upload-artifact@v4
        with:
          name: build
          path: .next
          retention-days: 1

  # ===================
  # E2E Tests
  # ===================
  e2e:
    name: E2E Tests
    runs-on: ubuntu-latest
    needs: build
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Install Playwright browsers
        run: npx playwright install --with-deps chromium

      - name: Download build
        uses: actions/download-artifact@v4
        with:
          name: build
          path: .next

      - name: Run E2E tests
        run: npm run test:e2e
        env:
          NEXT_PUBLIC_SUPABASE_URL: ${{ secrets.NEXT_PUBLIC_SUPABASE_URL }}
          NEXT_PUBLIC_SUPABASE_ANON_KEY: ${{ secrets.NEXT_PUBLIC_SUPABASE_ANON_KEY }}

      - name: Upload test results
        if: failure()
        uses: actions/upload-artifact@v4
        with:
          name: playwright-report
          path: playwright-report
          retention-days: 7

  # ===================
  # Deploy Preview (PRs)
  # ===================
  deploy-preview:
    name: Deploy Preview
    runs-on: ubuntu-latest
    needs: [build, e2e]
    if: github.event_name == 'pull_request'
    environment:
      name: preview
      url: ${{ steps.deploy.outputs.url }}
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Install Vercel CLI
        run: npm install -g vercel

      - name: Pull Vercel Environment
        run: vercel pull --yes --environment=preview --token=${{ secrets.VERCEL_TOKEN }}

      - name: Build Project
        run: vercel build --token=${{ secrets.VERCEL_TOKEN }}

      - name: Deploy to Vercel
        id: deploy
        run: |
          url=$(vercel deploy --prebuilt --token=${{ secrets.VERCEL_TOKEN }})
          echo "url=$url" >> $GITHUB_OUTPUT

      - name: Comment PR with URL
        uses: actions/github-script@v7
        with:
          script: |
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: `🚀 Preview deployed to: ${{ steps.deploy.outputs.url }}`
            })

  # ===================
  # Deploy Production
  # ===================
  deploy-production:
    name: Deploy Production
    runs-on: ubuntu-latest
    needs: [build, e2e]
    if: github.ref == 'refs/heads/main' && github.event_name == 'push'
    environment:
      name: production
      url: https://myapp.com
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Install Vercel CLI
        run: npm install -g vercel

      - name: Pull Vercel Environment
        run: vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }}

      - name: Build Project
        run: vercel build --prod --token=${{ secrets.VERCEL_TOKEN }}

      - name: Deploy to Vercel
        run: vercel deploy --prebuilt --prod --token=${{ secrets.VERCEL_TOKEN }}
```

## Testing Strategies

### Parallel Test Execution

```yaml
# .github/workflows/test-parallel.yml
name: Parallel Tests

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        shard: [1, 2, 3, 4]
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'

      - run: npm ci

      - name: Run tests (shard ${{ matrix.shard }}/4)
        run: npm run test -- --shard=${{ matrix.shard }}/4

  merge-results:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - name: Report results
        run: echo "All test shards passed!"
```

### Matrix Testing

```yaml
# .github/workflows/matrix-test.yml
name: Matrix Tests

on: [push, pull_request]

jobs:
  test:
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-latest, macos-latest, windows-latest]
        node: [18, 20, 22]
        exclude:
          - os: windows-latest
            node: 18
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node }}
          cache: 'npm'
      - run: npm ci
      - run: npm test
```

## Database Integration

### Supabase in CI

```yaml
# .github/workflows/test-with-db.yml
name: Tests with Database

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    services:
      postgres:
        image: postgres:15
        env:
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: test_db
        ports:
          - 5432:5432
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Setup Supabase CLI
        uses: supabase/setup-cli@v1

      - name: Start Supabase
        run: supabase start

      - name: Run migrations
        run: supabase db reset
        env:
          SUPABASE_DB_URL: postgresql://postgres:postgres@localhost:54322/postgres

      - name: Run tests
        run: npm run test:integration
        env:
          DATABASE_URL: postgresql://postgres:postgres@localhost:54322/postgres
          SUPABASE_URL: http://localhost:54321
          SUPABASE_ANON_KEY: ${{ secrets.SUPABASE_ANON_KEY }}
```

## Release Automation

### Automated Releases

```yaml
# .github/workflows/release.yml
name: Release

on:
  push:
    branches: [main]

jobs:
  release:
    runs-on: ubuntu-latest
    permissions:
      contents: write
      pull-requests: write
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Create Release
        uses: google-github-actions/release-please-action@v4
        with:
          release-type: node
          package-name: my-app

      - name: Publish to npm
        if: ${{ steps.release.outputs.release_created }}
        run: npm publish
        env:
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
```

### Changelog Generation

```yaml
# .github/workflows/changelog.yml
name: Changelog

on:
  release:
    types: [published]

jobs:
  update-changelog:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          ref: main

      - name: Update CHANGELOG
        uses: stefanzweifel/changelog-updater-action@v1
        with:
          latest-version: ${{ github.event.release.tag_name }}
          release-notes: ${{ github.event.release.body }}

      - name: Commit changes
        uses: stefanzweifel/git-auto-commit-action@v5
        with:
          commit_message: "docs: update CHANGELOG for ${{ github.event.release.tag_name }}"
          file_pattern: CHANGELOG.md
```

## Security Scanning

### Dependency & Code Scanning

```yaml
# .github/workflows/security.yml
name: Security

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
  schedule:
    - cron: '0 0 * * 0'  # Weekly

jobs:
  dependency-audit:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
      - run: npm audit --audit-level=high

  codeql:
    runs-on: ubuntu-latest
    permissions:
      security-events: write
    steps:
      - uses: actions/checkout@v4
      - uses: github/codeql-action/init@v3
        with:
          languages: javascript
      - uses: github/codeql-action/analyze@v3

  secrets-scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: gitleaks/gitleaks-action@v2
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
```

## Caching Strategies

### Effective Caching

```yaml
# .github/workflows/cached-build.yml
name: Cached Build

on: [push, pull_request]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'

      # Cache Next.js build
      - name: Cache Next.js
        uses: actions/cache@v4
        with:
          path: |
            .next/cache
          key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx') }}
          restore-keys: |
            ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-

      # Cache Playwright browsers
      - name: Cache Playwright
        uses: actions/cache@v4
        with:
          path: ~/.cache/ms-playwright
          key: ${{ runner.os }}-playwright-${{ hashFiles('**/package-lock.json') }}

      - run: npm ci
      - run: npm run build
```

## Environment Management

### Environment-Based Deployments

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

on:
  push:
    branches:
      - main
      - develop

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment:
      name: ${{ github.ref == 'refs/heads/main' && 'production' || 'staging' }}
      url: ${{ github.ref == 'refs/heads/main' && 'https://myapp.com' || 'https://staging.myapp.com' }}
    steps:
      - uses: actions/checkout@v4

      - name: Deploy to ${{ github.ref == 'refs/heads/main' && 'production' || 'staging' }}
        run: |
          echo "Deploying to ${{ github.ref == 'refs/heads/main' && 'production' || 'staging' }}"
        env:
          API_URL: ${{ vars.API_URL }}
          API_KEY: ${{ secrets.API_KEY }}
```

## Workflow Optimization

### Conditional Jobs

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

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  changes:
    runs-on: ubuntu-latest
    outputs:
      frontend: ${{ steps.filter.outputs.frontend }}
      backend: ${{ steps.filter.outputs.backend }}
      docs: ${{ steps.filter.outputs.docs }}
    steps:
      - uses: actions/checkout@v4
      - uses: dorny/paths-filter@v3
        id: filter
        with:
          filters: |
            frontend:
              - 'src/app/**'
              - 'src/components/**'
            backend:
              - 'src/api/**'
              - 'src/lib/**'
            docs:
              - 'docs/**'
              - '*.md'

  test-frontend:
    needs: changes
    if: ${{ needs.changes.outputs.frontend == 'true' }}
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npm run test:frontend

  test-backend:
    needs: changes
    if: ${{ needs.changes.outputs.backend == 'true' }}
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npm run test:backend

  deploy-docs:
    needs: changes
    if: ${{ needs.changes.outputs.docs == 'true' }}
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm run build:docs
```

## Notification & Reporting

### Slack Notifications

```yaml
# .github/workflows/notify.yml
name: CI with Notifications

on: [push]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npm run build

  notify:
    needs: build
    runs-on: ubuntu-latest
    if: always()
    steps:
      - name: Notify Slack
        uses: slackapi/slack-github-action@v1
        with:
          payload: |
            {
              "text": "${{ needs.build.result == 'success' && '✅' || '❌' }} Build ${{ needs.build.result }}",
              "blocks": [
                {
                  "type": "section",
                  "text": {
                    "type": "mrkdwn",
                    "text": "*${{ github.repository }}* - ${{ github.ref_name }}\nCommit: ${{ github.sha }}\nBy: ${{ github.actor }}"
                  }
                }
              ]
            }
        env:
          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
          SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK
```

## Pipeline Checklist

### Essential Components
- [ ] Lint and format checks
- [ ] Type checking (TypeScript)
- [ ] Unit and integration tests
- [ ] Build verification
- [ ] Security scanning

### Deployment
- [ ] Preview deployments for PRs
- [ ] Production deployment on main
- [ ] Environment-specific configs
- [ ] Rollback capability

### Performance
- [ ] Effective caching strategy
- [ ] Parallel job execution
- [ ] Conditional job execution
- [ ] Build artifact reuse

### Security
- [ ] Secrets properly managed
- [ ] Dependency auditing
- [ ] Code scanning enabled
- [ ] Protected environments

## When to Use This Skill

Invoke this skill when:
- Setting up CI/CD for a new project
- Optimizing slow pipelines
- Adding deployment automation
- Implementing release management
- Setting up security scanning
- Configuring environment-based deployments
- Creating preview environments

Overview

This skill is an expert guide for building reliable CI/CD pipelines using GitHub Actions, Vercel, and related tools. It focuses on automating linting, testing, building, deployments, and release workflows while emphasizing speed, reproducibility, security, and observability. Use it to design pipelines that scale from single-repo apps to multi-environment deployments.

How this skill works

I provide ready-to-adopt workflow patterns and practical examples: lint/test/build job graphs, deploy previews for PRs, production deploys, parallel and matrix test strategies, database-backed integration testing, caching, release automation, and security scanning. Each pattern includes concrete YAML snippets, environment and secrets handling, artifact management, and failure/notification hooks so you can copy, customize, and run quickly.

When to use it

  • When you need a complete CI/CD pipeline for Next.js or Node apps with GitHub Actions and Vercel.
  • When you want fast, parallel, or matrix-based test execution to reduce CI time.
  • When you need preview deployments for pull requests and automated production deploys.
  • When integrating a test database or Supabase into CI for integration tests.
  • When automating releases, changelog updates, and npm publishing from CI.

Best practices

  • Fail fast and provide clear feedback: separate lint, unit, E2E, and build stages with explicit needs.
  • Keep pipelines reproducible: pin runtimes, cache dependencies and build folders, and upload build artifacts.
  • Protect secrets: store tokens and keys in encrypted secrets and avoid leaking in logs.
  • Make pipelines observable: upload artifacts on failure, comment PRs with preview URLs, and emit status URLs.
  • Optimize cost and latency: shard tests, use matrix strategies, and cache Playwright/browser assets.

Example use cases

  • A Next.js app that runs typecheck, lint, tests, builds, runs E2E, deploys PR previews to Vercel, and deploys main to production.
  • A library that runs matrix tests across OSes and Node versions, publishes releases with automated changelog generation and npm publish.
  • An app with integration tests against a local Supabase/Postgres service spun up inside CI.
  • A project using parallel test shards to cut CI runtime and then merging results in a follow-up job.
  • A security-focused pipeline that schedules dependency audits, runs CodeQL analysis, and scans the repo for secrets.

FAQ

How do I keep secrets safe when deploying from CI?

Store tokens and keys in repository or organization secrets, reference them via secrets.* in workflows, and avoid printing them. Use environment-level protection and limited-permission tokens where possible.

When should I use prebuilt deployments to Vercel?

Use prebuilt deployments when you want to separate build and deploy steps, speed up deploys, or run E2E tests against the exact build artifact before pushing to production.