Skip to content
Codeloom
CI/CD

CI/CD with GitHub Actions

Build CI/CD pipelines with GitHub Actions — workflows, jobs, steps, matrix builds, secrets, caching, and deploying to production.

·3 min read · By Codeloom
Intermediate 11 min read

What you'll learn

  • How GitHub Actions workflows, jobs, and steps work
  • Triggering workflows on push, PR, schedule, and manual dispatch
  • Matrix builds, secrets, caching, and artifacts
  • A complete CI/CD pipeline from test to deploy

Prerequisites

  • Basic Git and GitHub knowledge
  • Understanding of CI/CD concepts

GitHub Actions gives you CI/CD built into your repository. No separate service to configure. Define workflows in YAML, and GitHub runs them on every push, PR, or schedule.

Your first workflow

Create .github/workflows/ci.yml:

name: CI

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

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 test

Workflow anatomy

  • Triggers (on): what events start the workflow
  • Jobs: independent units of work that run in parallel by default
  • Steps: sequential commands within a job
  • Actions: reusable steps from the marketplace

Triggers

on:
  push:
    branches: [main, develop]
    paths: ['src/**', 'tests/**']

  pull_request:
    types: [opened, synchronize]

  schedule:
    - cron: '0 6 * * 1' # every Monday at 6 AM

  workflow_dispatch: # manual trigger
    inputs:
      environment:
        description: 'Deploy environment'
        required: true
        default: 'staging'

Matrix builds

Test across multiple versions and OS combinations.

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

This creates 6 jobs (2 OS × 3 Node versions) that run in parallel.

Secrets

Store sensitive values in Settings → Secrets → Actions.

steps:
  - name: Deploy
    env:
      AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
      AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
    run: aws s3 sync ./dist s3://my-bucket

Caching dependencies

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

For custom caching:

- uses: actions/cache@v4
  with:
    path: ~/.cache/pip
    key: pip-${{ runner.os }}-${{ hashFiles('requirements.txt') }}
    restore-keys: |
      pip-${{ runner.os }}-

Job dependencies

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npm test

  build:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npm run build
      - uses: actions/upload-artifact@v4
        with:
          name: dist
          path: dist/

  deploy:
    needs: build
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/download-artifact@v4
        with:
          name: dist
      - run: echo "Deploying..."

Environment protection rules

deploy:
  needs: build
  runs-on: ubuntu-latest
  environment:
    name: production
    url: https://myapp.com
  steps:
    - run: echo "Deploying to production"

Configure required reviewers, wait timers, and branch restrictions in the GitHub UI.

Docker build and push

jobs:
  docker:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: ghcr.io/${{ github.repository }}:${{ github.sha }}

Reusable workflows

Define a workflow that other workflows can call.

# .github/workflows/deploy.yml
name: Deploy
on:
  workflow_call:
    inputs:
      environment:
        required: true
        type: string
    secrets:
      deploy_key:
        required: true

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: ${{ inputs.environment }}
    steps:
      - run: echo "Deploying to ${{ inputs.environment }}"

Call it:

jobs:
  deploy-staging:
    uses: ./.github/workflows/deploy.yml
    with:
      environment: staging
    secrets:
      deploy_key: ${{ secrets.DEPLOY_KEY }}

Summary

GitHub Actions gives you CI/CD as YAML files in your repo. Use matrix builds for broad coverage, cache dependencies for speed, artifacts for passing data between jobs, and environments for deployment protection. Start with a simple test workflow and layer on deploy steps as your project matures.