GitHub Actions Tutorial — Key Concepts
Key Concepts
Every GitHub Actions workflow is built from six core building blocks. Understanding these concepts is essential before writing your first workflow.
The Six Building Blocks
EVENT → WORKFLOW → JOB(S) → STEP(S) → ACTION / COMMAND
│
RUNNER (VM)
1. Event
An event is something that happens in your GitHub repository that triggers a workflow. Common events:
| Event | When It Fires |
|---|---|
push |
Code is pushed to any branch |
pull_request |
A PR is opened, updated, or merged |
schedule |
At a scheduled time (cron syntax) |
workflow_dispatch |
Manually triggered from the Actions tab |
release |
A release is published |
issues |
An issue is opened, closed, or labeled |
A single workflow can listen to multiple events:
on:
push:
branches: [main]
pull_request:
branches: [main]
2. Workflow
A workflow is an automated process defined in a YAML file inside .github/workflows/. Every repository can have multiple workflows.
my-repo/
├── .github/
│ └── workflows/
│ ├── ci.yml ← runs tests on every push
│ ├── deploy.yml ← deploys on release
│ └── stale.yml ← closes stale issues weekly
├── src/
└── package.json
Key points:
- One YAML file = one workflow
- A workflow contains one or more jobs
- Workflows are triggered by events
- File must be in
.github/workflows/(exactly this path)
3. Job
A job is a set of steps that run on the same runner. By default, jobs in a workflow run in parallel. You can make them sequential with needs:.
jobs:
build:
runs-on: ubuntu-latest
steps:
- run: echo "Building..."
test:
runs-on: ubuntu-latest
needs: build # waits for 'build' to finish
steps:
- run: echo "Testing..."
Parallel (default): Sequential (with needs:):
┌───────┐ ┌───────┐ ┌───────┐
│ build │ │ test │ │ build │
└───────┘ └───────┘ └───┬───┘
│
┌───▼───┐
│ test │
└───────┘
4. Step
A step is a single task within a job. Steps run sequentially (one after another) within a job. Each step is either:
- A shell command (
run:) - A pre-built action (
uses:)
steps:
- name: Check out code
uses: actions/checkout@v4 # pre-built action
- name: Install dependencies
run: npm install # shell command
- name: Run tests
run: npm test # shell command
5. Action
An action is a reusable unit of code that performs a specific task. Think of it as a function you can call in your workflow. Actions come from:
| Source | Example |
|---|---|
| Official (GitHub) |
actions/checkout@v4, actions/setup-node@v4
|
| Marketplace (community) | docker/build-push-action@v5 |
| Your own repo | ./my-local-action |
You reference actions with uses::
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
6. Runner
A runner is the virtual machine that executes your job. GitHub provides hosted runners for free:
| Runner | OS | Label |
|---|---|---|
| Ubuntu Linux | Ubuntu 22.04 | ubuntu-latest |
| Windows | Windows Server 2022 | windows-latest |
| macOS | macOS 14 (Sonoma) | macos-latest |
You specify the runner with runs-on::
jobs:
test:
runs-on: ubuntu-latest # GitHub provides the VM
You can also use self-hosted runners (your own machines) for custom hardware, private network access, or cost savings at scale.
How They Fit Together
Here’s a complete example showing all six concepts:
# WORKFLOW: defined in .github/workflows/ci.yml
name: CI Pipeline
# EVENT: triggered on push to main
on:
push:
branches: [main]
# JOBS: two jobs that run on RUNNERS
jobs:
build:
runs-on: ubuntu-latest # RUNNER: GitHub-hosted Ubuntu
steps: # STEPS: sequential tasks
- uses: actions/checkout@v4 # ACTION: pre-built
- run: npm install # STEP: shell command
- run: npm run build
test:
runs-on: ubuntu-latest
needs: build # JOB dependency
steps:
- uses: actions/checkout@v4
- run: npm install
- run: npm test
Quick Reference
| Concept | What It Is | YAML Key |
|---|---|---|
| Event | What triggers the workflow | on: |
| Workflow | Automated process (YAML file) | The file itself |
| Job | Set of steps on one runner |
jobs: → job-id:
|
| Step | Single task in a job |
steps: → - run: or - uses:
|
| Action | Reusable task unit | uses: |
| Runner | VM that runs the job | runs-on: |
← Previous: What is GitHub Actions?
Next: Actions vs Alternatives →