CI/CD & DevOps

GitHub Actions from Scratch

Build a complete CI/CD pipeline with GitHub Actions — from first workflow to production deployment.

What GitHub Actions Is

GitHub Actions is CI/CD built directly into GitHub. When you push code, open a pull request, or create a release, GitHub can automatically run any workflow you define.

Workflows are YAML files stored in .github/workflows/ in your repository.

Core Concepts

Workflow — A YAML file that defines an automated process. You can have multiple workflows (CI tests, deployment, dependency updates, etc.).

Event — What triggers the workflow. Common events: push, pull_request, schedule (cron), workflow_dispatch (manual trigger).

Job — A set of steps that runs on a virtual machine (runner). Multiple jobs can run in parallel.

Step — An individual task within a job. Can be a shell command or a reusable action from the Marketplace.

Runner — The virtual machine that executes the job. GitHub provides Ubuntu, Windows, and macOS runners.

Your First Workflow

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

# Trigger on push to main and all pull requests
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest

    steps:
      # Step 1: Check out the repository code
      - uses: actions/checkout@v4

      # Step 2: Set up Node.js
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'  # Cache node_modules for faster subsequent runs

      # Step 3: Install dependencies
      - run: npm ci  # ci is faster and more reliable than npm install

      # Step 4: Run quality checks
      - run: npm run lint
      - run: npm run typecheck

      # Step 5: Run tests
      - run: npm test

      # Step 6: Build
      - run: npm run build

Using Secrets

Store sensitive values in GitHub repository settings (Settings → Secrets → Actions) and reference them in workflows:

yaml
steps:
  - name: Deploy to Vercel
    env:
      VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
      VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
      VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
    run: vercel --prod --token $VERCEL_TOKEN

Secrets are never visible in logs or to forks.

Matrix Builds

Test against multiple versions in parallel:

yaml
jobs:
  test:
    strategy:
      matrix:
        node-version: [18, 20, 22]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
      - run: npm ci && npm test

Branch Protection Rules

Require CI to pass before merging. In your repository: Settings → Branches → Add branch protection rule. Enable "Require status checks to pass before merging" and select your CI workflow.

Now it is impossible to merge code that fails tests.

Complete Production Workflow

yaml
name: CI/CD

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

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
      - run: npm run lint
      - run: npm run typecheck
      - run: npm test -- --coverage
      - run: npm run build

  deploy-staging:
    needs: ci
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    environment: staging
    steps:
      - uses: actions/checkout@v4
      - 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 }}

Key Takeaways

  • GitHub Actions workflows are YAML files in .github/workflows/ that trigger on GitHub events
  • Jobs run in parallel by default; use needs to create sequential dependencies
  • Store secrets in repository settings and access them with ${{ secrets.NAME }}
  • Use npm ci instead of npm install in CI — it is faster and uses the lockfile exactly
  • Protect your main branch to require CI to pass before any code can be merged

Example

yaml
# .github/workflows/ci.yml — production-ready workflow
name: CI

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

jobs:
  quality:
    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
      - run: npm run typecheck
      - run: npm test -- --coverage --reporter=verbose
      - run: npm run build

  security:
    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 audit --audit-level=high
Try it yourself — YAML

Docker, AWS, Vercel, Netlify, GitHub, GitHub Actions are trademarks of Docker, Inc., Amazon.com, Inc., Vercel, Inc., Netlify, Inc., Microsoft Corporation. DevForge Academy is not affiliated with, endorsed by, or sponsored by these companies. Referenced for educational purposes only. See full disclaimers