Skip to main content

GitHub Actions Basics

GitHub Actions is a CI/CD platform built directly into GitHub. You define workflows in YAML files, and GitHub runs them on every push, pull request, or schedule you configure.

How GitHub Actions Works

Every GitHub Actions automation is defined by a workflow file stored in .github/workflows/ in your repository. When a triggering event occurs (like a push), GitHub spins up a virtual machine, checks out your code, and executes the steps you defined.

Key Terminology

TermMeaning
WorkflowA YAML file that defines an automation
EventWhat triggers the workflow (push, PR, schedule)
JobA set of steps that run on the same machine
StepAn individual task within a job
ActionA reusable unit of code (from the marketplace or custom)
RunnerThe virtual machine that executes the job

Your First Workflow

Create .github/workflows/hello.yml:

name: Hello World

on:
  push:
    branches: [main]

jobs:
  greet:
    runs-on: ubuntu-latest
    steps:
      - name: Say hello
        run: echo "Hello from GitHub Actions!"

      - name: Show system info
        run: |
          echo "Runner OS: $RUNNER_OS"
          echo "Date: $(date)"
          echo "User: $(whoami)"

Push this file to your repository, and GitHub will automatically run the workflow.

Workflow Triggers (on)

The on key defines what events trigger the workflow:

# Trigger on push to main
on:
  push:
    branches: [main]

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

# Trigger on both push and PR
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

# Trigger on a schedule (cron syntax)
on:
  schedule:
    - cron: "0 6 * * 1"  # Every Monday at 6 AM UTC

# Trigger manually from the GitHub UI
on:
  workflow_dispatch:

# Trigger on tag creation
on:
  push:
    tags:
      - "v*"

You can filter by file paths to only run when relevant files change:

on:
  push:
    branches: [main]
    paths:
      - "src/**"
      - "package.json"
      - ".github/workflows/**"
    paths-ignore:
      - "docs/**"
      - "*.md"

Jobs

Jobs define what work to perform. By default, jobs run in parallel. Use needs to make them sequential:

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Build the app
        run: echo "Building..."

  test:
    runs-on: ubuntu-latest
    needs: build  # Waits for build to finish
    steps:
      - name: Run tests
        run: echo "Testing..."

  deploy:
    runs-on: ubuntu-latest
    needs: [build, test]  # Waits for both
    steps:
      - name: Deploy
        run: echo "Deploying..."

Runners

Runners are the machines that execute your jobs. GitHub provides hosted runners:

RunnerOS
ubuntu-latestUbuntu Linux (most common)
ubuntu-22.04Ubuntu 22.04 specifically
windows-latestWindows Server
macos-latestmacOS
jobs:
  linux-job:
    runs-on: ubuntu-latest
    steps:
      - run: echo "Running on Linux"

  windows-job:
    runs-on: windows-latest
    steps:
      - run: echo "Running on Windows"

Steps

Steps are the individual tasks within a job. Each step either runs a shell command or uses a pre-built action:

steps:
  # Run a shell command
  - name: Print greeting
    run: echo "Hello"

  # Run multiple commands
  - name: Setup environment
    run: |
      echo "Installing dependencies"
      npm install
      echo "Done"

  # Use a pre-built action
  - name: Checkout code
    uses: actions/checkout@v4

  # Use an action with inputs
  - name: Setup Node.js
    uses: actions/setup-node@v4
    with:
      node-version: "20"

Essential Actions

These actions are used in almost every workflow:

steps:
  # Checkout your repository code
  - uses: actions/checkout@v4

  # Setup Node.js
  - uses: actions/setup-node@v4
    with:
      node-version: "20"
      cache: "pnpm"

  # Setup Python
  - uses: actions/setup-python@v5
    with:
      python-version: "3.12"

  # Cache dependencies
  - uses: actions/cache@v4
    with:
      path: ~/.pnpm-store
      key: ${{ runner.os }}-pnpm-${{ hashFiles('pnpm-lock.yaml') }}

  # Upload build artifacts
  - uses: actions/upload-artifact@v4
    with:
      name: build-output
      path: dist/

Environment Variables

Set environment variables at different levels:

# Workflow-level (available to all jobs)
env:
  NODE_ENV: production

jobs:
  build:
    runs-on: ubuntu-latest
    # Job-level (available to all steps in this job)
    env:
      CI: true

    steps:
      # Step-level
      - name: Build
        env:
          API_URL: https://api.example.com
        run: |
          echo "NODE_ENV: $NODE_ENV"
          echo "CI: $CI"
          echo "API_URL: $API_URL"

Secrets

Never hardcode sensitive values. Use GitHub Secrets instead:

steps:
  - name: Deploy
    env:
      DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}
      AWS_ACCESS_KEY: ${{ secrets.AWS_ACCESS_KEY_ID }}
    run: ./deploy.sh

Add secrets in your repository: Settings > Secrets and variables > Actions > New repository secret.

Expressions and Context

GitHub Actions provides context objects and expressions:

steps:
  - name: Show context info
    run: |
      echo "Repository: ${{ github.repository }}"
      echo "Branch: ${{ github.ref_name }}"
      echo "Commit: ${{ github.sha }}"
      echo "Actor: ${{ github.actor }}"
      echo "Event: ${{ github.event_name }}"
      echo "Runner OS: ${{ runner.os }}"

Conditional steps:

steps:
  - name: Deploy to production
    if: github.ref == 'refs/heads/main'
    run: ./deploy-prod.sh

  - name: Deploy to staging
    if: github.ref == 'refs/heads/develop'
    run: ./deploy-staging.sh

  - name: Only on pull request
    if: github.event_name == 'pull_request'
    run: echo "This is a PR"

A Complete CI Workflow

Here is a practical workflow that checks out code, installs dependencies, lints, and tests:

name: CI

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

jobs:
  ci:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup pnpm
        uses: pnpm/action-setup@v4
        with:
          version: 9

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: "20"
          cache: "pnpm"

      - name: Install dependencies
        run: pnpm install --frozen-lockfile

      - name: Lint
        run: pnpm lint

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

      - name: Run tests
        run: pnpm test

      - name: Build
        run: pnpm build

Viewing Workflow Results

After pushing a workflow, go to the Actions tab in your repository to see:

  • A list of all workflow runs
  • The status of each job (success, failure, in progress)
  • Detailed logs for each step
  • Duration and resource usage

A green checkmark means the workflow passed. A red X means it failed. Click into a run to see the logs and identify what went wrong.

Summary

GitHub Actions workflows are YAML files that automate tasks on events like pushes and pull requests. You learned about triggers, jobs, steps, runners, environment variables, and secrets. In the next lesson, you will build a real CI pipeline with testing, linting, and build verification.