CI/CD Pipelines with GitHub Actions

June 18, 2024 (1y ago)

CI/CD Pipelines with GitHub Actions

Automate your development workflow with CI/CD pipelines. Here's how to set up GitHub Actions for your projects.

Basic CI Pipeline

.github/workflows/ci.yml

name: CI
 
on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]
 
jobs:
  test:
    runs-on: ubuntu-latest
 
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
 
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
 
      - name: Install dependencies
        run: npm ci
 
      - name: Run linter
        run: npm run lint
 
      - name: Run tests
        run: npm run test
 
      - name: Build
        run: npm run build

Matrix Testing

Test Multiple Node Versions

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [18, 20, 22]
        os: [ubuntu-latest, windows-latest, macos-latest]
 
    steps:
      - uses: actions/checkout@v4
      
      - name: Use Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
 
      - run: npm ci
      - run: npm test

Code Coverage

jobs:
  test:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
      
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
 
      - run: npm ci
      - run: npm run test:coverage
 
      - name: Upload coverage to Codecov
        uses: codecov/codecov-action@v3
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
          files: ./coverage/lcov.info

Deploy to Vercel

name: Deploy
 
on:
  push:
    branches: [main]
 
jobs:
  deploy:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
 
      - 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'

Docker Build & Push

name: Docker Build
 
on:
  push:
    branches: [main]
    tags: ['v*']
 
jobs:
  build:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
 
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
 
      - name: Login to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_TOKEN }}
 
      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: |
            username/app:latest
            username/app:${{ github.sha }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

Database Migrations

jobs:
  migrate:
    runs-on: ubuntu-latest
    
    services:
      postgres:
        image: postgres:15
        env:
          POSTGRES_USER: test
          POSTGRES_PASSWORD: test
          POSTGRES_DB: testdb
        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'
 
      - run: npm ci
      
      - name: Run migrations
        run: npm run db:migrate
        env:
          DATABASE_URL: postgres://test:test@localhost:5432/testdb

Release Workflow

name: Release
 
on:
  push:
    tags:
      - 'v*'
 
jobs:
  release:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
 
      - name: Generate changelog
        id: changelog
        uses: TriPSs/conventional-changelog-action@v4
        with:
          skip-commit: true
          skip-tag: true
 
      - name: Create Release
        uses: actions/create-release@v1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          tag_name: ${{ github.ref }}
          release_name: Release ${{ github.ref }}
          body: ${{ steps.changelog.outputs.clean_changelog }}

Caching

npm Cache

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

Custom Cache

- name: Cache dependencies
  uses: actions/cache@v3
  with:
    path: |
      ~/.npm
      node_modules
    key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
    restore-keys: |
      ${{ runner.os }}-node-

Secrets Management

env:
  API_KEY: ${{ secrets.API_KEY }}
  DATABASE_URL: ${{ secrets.DATABASE_URL }}

Add secrets in: Repository Settings → Secrets and variables → Actions

Branch Protection

Configure in GitHub:

  1. Require pull request reviews
  2. Require status checks to pass
  3. Require branches to be up to date
  4. Include administrators

Best Practices

  1. Keep workflows fast - Parallel jobs, caching
  2. Fail early - Lint before tests
  3. Use specific versions - Pin action versions
  4. Secure secrets - Never hardcode credentials
  5. Test PRs - Run CI on pull requests
  6. Cache dependencies - Speed up builds
  7. Use environments - staging, production
  8. Monitor workflows - Track failures

CI/CD automation gives you confidence in every deployment!