CI/CD Pipelines
Patterns for continuous integration and deployment with GitHub Actions.
Overview#
CI/CD automates testing and deployment for reliable releases. This pattern covers:
- GitHub Actions workflows
- Testing and linting
- Preview deployments
- Production deployments
- Database migrations
Prerequisites#
# GitHub repository with Actions enabled
# Vercel, AWS, or other deployment targetCode Example#
Complete CI Workflow#
1# .github/workflows/ci.yml
2name: CI
3
4on:
5 push:
6 branches: [main]
7 pull_request:
8 branches: [main]
9
10env:
11 NODE_VERSION: '20'
12
13jobs:
14 lint:
15 runs-on: ubuntu-latest
16 steps:
17 - uses: actions/checkout@v4
18 - uses: actions/setup-node@v4
19 with:
20 node-version: ${{ env.NODE_VERSION }}
21 cache: 'npm'
22
23 - run: npm ci
24 - run: npm run lint
25
26 typecheck:
27 runs-on: ubuntu-latest
28 steps:
29 - uses: actions/checkout@v4
30 - uses: actions/setup-node@v4
31 with:
32 node-version: ${{ env.NODE_VERSION }}
33 cache: 'npm'
34
35 - run: npm ci
36 - run: npm run typecheck
37
38 test:
39 runs-on: ubuntu-latest
40 steps:
41 - uses: actions/checkout@v4
42 - uses: actions/setup-node@v4
43 with:
44 node-version: ${{ env.NODE_VERSION }}
45 cache: 'npm'
46
47 - run: npm ci
48 - run: npm test -- --coverage
49
50 - name: Upload coverage
51 uses: codecov/codecov-action@v3
52 with:
53 token: ${{ secrets.CODECOV_TOKEN }}
54
55 build:
56 runs-on: ubuntu-latest
57 needs: [lint, typecheck, test]
58 steps:
59 - uses: actions/checkout@v4
60 - uses: actions/setup-node@v4
61 with:
62 node-version: ${{ env.NODE_VERSION }}
63 cache: 'npm'
64
65 - run: npm ci
66 - run: npm run build
67
68 - name: Upload build
69 uses: actions/upload-artifact@v4
70 with:
71 name: build
72 path: .next
73 retention-days: 1
74
75 e2e:
76 runs-on: ubuntu-latest
77 needs: build
78 steps:
79 - uses: actions/checkout@v4
80 - uses: actions/setup-node@v4
81 with:
82 node-version: ${{ env.NODE_VERSION }}
83 cache: 'npm'
84
85 - name: Install Playwright
86 run: npx playwright install --with-deps
87
88 - name: Download build
89 uses: actions/download-artifact@v4
90 with:
91 name: build
92 path: .next
93
94 - run: npm ci
95 - run: npm run e2e
96
97 - name: Upload test results
98 if: failure()
99 uses: actions/upload-artifact@v4
100 with:
101 name: playwright-report
102 path: playwright-reportProduction Deployment#
1# .github/workflows/deploy.yml
2name: Deploy
3
4on:
5 push:
6 branches: [main]
7
8jobs:
9 deploy:
10 runs-on: ubuntu-latest
11 environment: production
12 steps:
13 - uses: actions/checkout@v4
14 - uses: actions/setup-node@v4
15 with:
16 node-version: '20'
17 cache: 'npm'
18
19 - run: npm ci
20
21 - name: Run migrations
22 run: npx prisma migrate deploy
23 env:
24 DATABASE_URL: ${{ secrets.DATABASE_URL }}
25
26 - name: Deploy to Vercel
27 run: vercel --prod --token=${{ secrets.VERCEL_TOKEN }}
28 env:
29 VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
30 VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}Preview Deployments#
1# .github/workflows/preview.yml
2name: Preview
3
4on:
5 pull_request:
6 types: [opened, synchronize]
7
8jobs:
9 deploy-preview:
10 runs-on: ubuntu-latest
11 steps:
12 - uses: actions/checkout@v4
13
14 - name: Deploy to Vercel
15 id: deploy
16 uses: amondnet/vercel-action@v25
17 with:
18 vercel-token: ${{ secrets.VERCEL_TOKEN }}
19 vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
20 vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
21
22 - name: Comment PR
23 uses: actions/github-script@v7
24 with:
25 script: |
26 github.rest.issues.createComment({
27 issue_number: context.issue.number,
28 owner: context.repo.owner,
29 repo: context.repo.repo,
30 body: `## Preview Deployment
31
32 Your changes have been deployed to:
33 ${{ steps.deploy.outputs.preview-url }}
34
35 Commit: \`${{ github.sha }}\``
36 })Matrix Testing#
1# Test across multiple Node versions and OS
2jobs:
3 test:
4 runs-on: ${{ matrix.os }}
5 strategy:
6 matrix:
7 os: [ubuntu-latest, macos-latest]
8 node-version: [18, 20, 22]
9 fail-fast: false
10
11 steps:
12 - uses: actions/checkout@v4
13 - uses: actions/setup-node@v4
14 with:
15 node-version: ${{ matrix.node-version }}
16
17 - run: npm ci
18 - run: npm testDatabase Migration Job#
1# .github/workflows/migrate.yml
2name: Database Migration
3
4on:
5 workflow_dispatch:
6 inputs:
7 environment:
8 description: 'Environment to migrate'
9 required: true
10 type: choice
11 options:
12 - staging
13 - production
14
15jobs:
16 migrate:
17 runs-on: ubuntu-latest
18 environment: ${{ inputs.environment }}
19 steps:
20 - uses: actions/checkout@v4
21 - uses: actions/setup-node@v4
22 with:
23 node-version: '20'
24
25 - run: npm ci
26
27 - name: Run migrations
28 run: npx prisma migrate deploy
29 env:
30 DATABASE_URL: ${{ secrets.DATABASE_URL }}
31
32 - name: Notify on failure
33 if: failure()
34 uses: actions/github-script@v7
35 with:
36 script: |
37 github.rest.issues.create({
38 owner: context.repo.owner,
39 repo: context.repo.repo,
40 title: 'Migration failed in ${{ inputs.environment }}',
41 body: 'Check workflow run: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}',
42 labels: ['bug', 'urgent']
43 })Release Workflow#
1# .github/workflows/release.yml
2name: Release
3
4on:
5 push:
6 tags:
7 - 'v*'
8
9jobs:
10 release:
11 runs-on: ubuntu-latest
12 steps:
13 - uses: actions/checkout@v4
14 with:
15 fetch-depth: 0
16
17 - uses: actions/setup-node@v4
18 with:
19 node-version: '20'
20
21 - run: npm ci
22 - run: npm run build
23
24 - name: Generate changelog
25 id: changelog
26 uses: requarks/changelog-action@v1
27 with:
28 token: ${{ secrets.GITHUB_TOKEN }}
29 tag: ${{ github.ref_name }}
30
31 - name: Create Release
32 uses: softprops/action-gh-release@v1
33 with:
34 body: ${{ steps.changelog.outputs.changelog }}
35 generate_release_notes: true
36 files: |
37 dist/*Caching Strategies#
1# Efficient caching
2- uses: actions/setup-node@v4
3 with:
4 node-version: '20'
5 cache: 'npm'
6
7# Custom caching for Next.js
8- uses: actions/cache@v4
9 with:
10 path: |
11 ~/.npm
12 .next/cache
13 key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('**/*.ts', '**/*.tsx') }}
14 restore-keys: |
15 ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-
16 ${{ runner.os }}-nextjs-
17
18# Playwright browser caching
19- uses: actions/cache@v4
20 with:
21 path: ~/.cache/ms-playwright
22 key: ${{ runner.os }}-playwright-${{ hashFiles('**/package-lock.json') }}Conditional Jobs#
1jobs:
2 changes:
3 runs-on: ubuntu-latest
4 outputs:
5 frontend: ${{ steps.filter.outputs.frontend }}
6 backend: ${{ steps.filter.outputs.backend }}
7 steps:
8 - uses: actions/checkout@v4
9 - uses: dorny/paths-filter@v2
10 id: filter
11 with:
12 filters: |
13 frontend:
14 - 'app/**'
15 - 'components/**'
16 backend:
17 - 'lib/**'
18 - 'prisma/**'
19
20 frontend-tests:
21 needs: changes
22 if: ${{ needs.changes.outputs.frontend == 'true' }}
23 runs-on: ubuntu-latest
24 steps:
25 - run: npm run test:frontend
26
27 backend-tests:
28 needs: changes
29 if: ${{ needs.changes.outputs.backend == 'true' }}
30 runs-on: ubuntu-latest
31 steps:
32 - run: npm run test:backendUsage Instructions#
- Create workflow files in
.github/workflows/ - Set up required secrets in repository settings
- Configure branch protection rules
- Enable required status checks
- Set up deployment environments
Best Practices#
- Run jobs in parallel - Speed up CI with parallel execution
- Use caching - Cache dependencies and build artifacts
- Fail fast - Stop on first failure in matrix builds
- Environment protection - Require approvals for production
- Status checks - Block merges on failing checks
- Artifact retention - Set appropriate retention periods
- Secrets management - Never log secrets, use GitHub Secrets
Related Patterns#
- Docker - Container configuration
- Environments - Environment management
- Monitoring - Post-deployment monitoring
- Testing - Test configuration