Skip to main content
DevopsBeginner12 min readUpdated March 2025

GitHub Actions

GitHub Actions is a CI/CD platform built directly into GitHub. It automates build, test, and deployment workflows triggered by repository events like pushes, pull requests, and releases — all defined in YAML files.

What is GitHub Actions?

GitHub Actions is an automation platform that lets you run workflows in response to events in your GitHub repository. It is tightly integrated with GitHub, making it the easiest CI/CD solution for projects hosted there.

Key concepts: - Workflow — An automated process defined in a YAML file (.github/workflows/) - Event — A trigger that starts a workflow (push, pull_request, schedule, etc.) - Job — A set of steps that run on the same runner - Step — An individual task (run a command or use an Action) - Action — A reusable unit of code from the GitHub Marketplace - Runner — A server that executes the workflow (GitHub-hosted or self-hosted)

Workflow Syntax

Workflows are YAML files stored in .github/workflows/. Here is the anatomy of a workflow:

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

# ---- Triggers ----
on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]
  schedule:
    - cron: '0 2 * * *'    # Run at 2 AM daily
  workflow_dispatch:         # Manual trigger from GitHub UI

# ---- Environment variables ----
env:
  NODE_VERSION: '20'
  REGISTRY: ghcr.io
  IMAGE_NAME: DOLLAR{{ github.repository }}

# ---- Jobs ----
jobs:
  # Job 1: Test
  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: DOLLAR{{ env.NODE_VERSION }}
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Run linter
        run: npm run lint

      - name: Run tests
        run: npm test -- --coverage

      - name: Upload coverage
        uses: codecov/codecov-action@v4
        with:
          token: DOLLAR{{ secrets.CODECOV_TOKEN }}

  # Job 2: Build (runs after test)
  build:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Build application
        run: npm run build
      - name: Upload build artifact
        uses: actions/upload-artifact@v4
        with:
          name: build-output
          path: dist/

CI/CD Pipeline: Test, Build, and Deploy

A complete pipeline that tests, builds a Docker image, pushes to a registry, and deploys to a server:

yaml
# .github/workflows/deploy.yml
name: Build and Deploy

on:
  push:
    branches: [main]

jobs:
  build-and-push:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write

    steps:
      - uses: actions/checkout@v4

      - name: Log in to GitHub Container Registry
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: DOLLAR{{ github.actor }}
          password: DOLLAR{{ secrets.GITHUB_TOKEN }}

      - name: Extract metadata for Docker
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ghcr.io/DOLLAR{{ github.repository }}
          tags: |
            type=sha,prefix=sha-
            type=raw,value=latest

      - name: Build and push Docker image
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: DOLLAR{{ steps.meta.outputs.tags }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

  deploy:
    needs: build-and-push
    runs-on: ubuntu-latest
    environment: production     # Requires manual approval

    steps:
      - name: Deploy to server via SSH
        uses: appleboy/ssh-action@v1
        with:
          host: DOLLAR{{ secrets.SERVER_HOST }}
          username: DOLLAR{{ secrets.SERVER_USER }}
          key: DOLLAR{{ secrets.SSH_PRIVATE_KEY }}
          script: |
            docker pull ghcr.io/DOLLAR{{ github.repository }}:latest
            docker compose -f /app/docker-compose.yml up -d --no-deps web

Secrets and Environments

GitHub Actions provides a secure way to store sensitive values:

Repository secrets — Available to all workflows in the repo. Set in Settings > Secrets and variables > Actions.

Environment secrets — Scoped to a specific deployment environment (production, staging). Can require manual approval before a job runs.

GITHUB_TOKEN — Automatically created for each workflow run. Has permissions to read/write to the repository (push commits, create releases, publish packages).

Access secrets with: DOLLAR{{ secrets.SECRET_NAME }}

Matrix Builds and Reusable Workflows

Matrix strategy runs a job multiple times with different configurations (e.g., test on Node 18, 20, 22 simultaneously):

yaml
jobs:
  test:
    runs-on: DOLLAR{{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
        node: [18, 20, 22]
      fail-fast: false    # Don't cancel other jobs if one fails

    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: DOLLAR{{ matrix.node }}
      - run: npm ci && npm test
      # Runs 9 jobs in parallel (3 OS x 3 Node versions)

Key Takeaways

  • GitHub Actions workflows are YAML files in .github/workflows/ triggered by repository events.
  • Jobs run in parallel by default; use needs: to create sequential dependencies.
  • The GitHub Marketplace has thousands of pre-built Actions for common tasks (checkout, setup, deploy).
  • Store sensitive values as repository or environment secrets — never hardcode them in workflows.
  • Matrix builds test across multiple OS/version combinations simultaneously, catching compatibility issues early.

Contact Us

Have a question or feedback? Fill out the form below or reach us directly at support@nvaitraining.com