Lesson 2 of 6

Workflow Anatomy

Every workflow file has the same skeleton, and once you can read it, you can read anyone's CI. Here's a complete, real workflow — we'll take it apart piece by piece:

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

on:
  push:
    branches: [main]

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

      - uses: actions/setup-node@v4
        with:
          node-version: 22

      - run: npm ci
      - run: npm test

💡 The Bottom Line

A workflow (one YAML file) fires on an event (on:) and contains jobs; each job gets a fresh runner VM and runs steps in order. A step either uses: a published action or run:s a shell command. That's the whole vocabulary.

The hierarchy, top to bottom

uses vs run

Two kinds of step, visually distinct:

And the non-obvious essential: the runner starts empty — your code is not there until actions/checkout clones it. That's why practically every job on earth starts with that step; forgetting it is everyone's first red run ("no such file package.json").

🙋 There Are No Dumb Questions

Q: Do my steps share anything between them? Do my jobs?
A: Steps: yes — same VM, same working directory, so files written by npm ci are there for npm test. Jobs: no — each job is a separate fresh machine. Passing files between jobs takes explicit upload-artifact/download-artifact steps (Lesson 5), and that distinction — steps share, jobs don't — resolves about half of all beginner confusion.

⚠️ Watch Out

YAML indentation is structure, and workflow files punish it: an extra two spaces turns a step into gibberish, and the error surfaces as "workflow file invalid" at the top of the Actions tab — or worse, the workflow silently not appearing at all. Use an editor with YAML support, keep indentation to consistent 2-space levels, and when a workflow "doesn't exist," check the Actions tab for a parse-error banner first.

🔧 Try It Yourself

Write the minimal workflow by hand — no starter template. Create .github/workflows/hello.yml in a scratch repo:

name: Hello
on: push
jobs:
  greet:
    runs-on: ubuntu-latest
    steps:
      - run: echo "Hello from a runner"
      - run: uname -a && node --version

Push it, open the Actions tab, and read the logs — note that Node was already installed (runners come with common toolchains preloaded) and that the machine is a throwaway Ubuntu VM you never had to create.

✅ Quick Check

Why does nearly every job begin with uses: actions/checkout@v4?


Next up: everything that can go after on: — pushes, PRs, cron schedules, and the manual-run button.

← Back to
Next →