πŸ“‹ Cheat Sheets
Β· 2 min read

The Only GitHub Actions CI Pipeline You Need β€” Copy & Paste


Stop writing your CI pipeline from scratch every time. Here’s one that works.

The Workflow File

Save this as .github/workflows/ci.yml:

name: CI

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

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

jobs:
  ci:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

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

      - run: npm ci

      - name: Lint
        run: npm run lint

      - name: Type check
        run: npx tsc --noEmit

      - name: Test
        run: npm test

      - name: Build
        run: npm run build

What this does

  1. Triggers on push to main and on PRs targeting main
  2. Concurrency β€” if you push again while CI is running, it cancels the old run (saves minutes)
  3. Caches npm β€” actions/setup-node with cache: "npm" caches your node_modules automatically
  4. Runs in order: install β†’ lint β†’ type check β†’ test β†’ build
  5. If any step fails, the whole pipeline fails and you see it on the PR

Add deployment

Append this job to deploy after CI passes:

  deploy:
    needs: ci
    if: github.ref == 'refs/heads/main' && github.event_name == 'push'
    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 build
      - name: Deploy to Vercel
        run: npx vercel --prod --token=${{ secrets.VERCEL_TOKEN }}

Variations

Using pnpm instead of npm:

      - uses: pnpm/action-setup@v2
        with:
          version: 9
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: "pnpm"
      - run: pnpm install --frozen-lockfile

Using Bun:

      - uses: oven-sh/setup-bun@v1
      - run: bun install
      - run: bun test
      - run: bun run build

Add a test coverage comment on PRs:

      - name: Test with coverage
        run: npx vitest --coverage --reporter=json --outputFile=coverage.json

Pro tips

  • Don’t run on every branch β€” only main and PRs. Feature branch pushes waste CI minutes.
  • npm ci not npm install β€” ci is faster and uses the lockfile exactly.
  • cancel-in-progress saves you from queued runs eating your free minutes.

Copy the file, adjust the scripts to match your package.json, push. Done.

Related: Git Cheat Sheet Β· What is CI/CD