Skip to content

Lesson 2 of 7 · 22m

GitHub Actions Anatomy: Events, Jobs, Runners

The three-level model — workflow triggers, parallel jobs, sequential steps — and the YAML structure behind every CI file you've seen.

GitHub Actions' model is three nested levels: workflows trigger on events, jobs run in parallel on separate runners, steps run sequentially inside a job. Everything else — caching, matrices, secrets — hangs off that skeleton.

A real workflow, annotated

name: ci
on:
  push: { branches: [main] }
  pull_request:

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 20, cache: npm }
      - run: npm ci          # lockfile install — reproducible
      - run: npm run lint
      - run: npm test

on: chooses events — push and pull_request cover 95% of CI needs. Each job gets a fresh VM (runs-on); jobs in one workflow run in parallel by default. Steps share the job's filesystem — a file written in step 3 exists in step 5.

The pieces that confuse people

  • uses: steps run someone else's action — pin to a major version (@v4), not main, for supply-chain sanity.
  • npm ci not npm install — lockfile-driven, fails on drift; npm install can silently update.
  • env: exists at workflow/job/step level — step-level scoping limits secret sprawl.
  • if: failure() and if: always() — steps run conditionally; cleanup steps need always().

Field notes

  • Job names show up in branch protection — name them like they'll be required checks.
  • workflow_dispatch: adds a manual trigger for ops tasks (deploy, rebuild).
  • One failing step fails the job; continue-on-error marks specific steps advisory — use for flaky external services, not tests.