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
-
Fast feedback: Fail early, inform quickly
-
Reproducible: Same inputs = same outputs
-
Secure: Secrets protected, dependencies verified
-
Observable: Clear logs, status visibility
Pipeline Stages
Trigger → Lint → Test → Build → Deploy → Verify
GitHub Actions Fundamentals
Basic Workflow Structure
.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
.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
.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
.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
.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
.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
.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
.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
.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
.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
.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
.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