Workflow foundations
Map events, workflows, jobs, and steps
Understand which repository event starts which jobs and how ordered steps run on a runner.
GitHub Actions is event-driven. Something happens in the repository, and that event can start a workflow.
A push to main might start tests. A pull request might start the same tests plus a preview deploy. A tag push might start a release workflow. The event is the front door.
The four layers
Think of four nested layers:
event
-> workflow run
-> job
-> step
-> step
-> job
-> step
An event is what GitHub noticed: a push, a pull request, a schedule tick, a manual button.
A workflow is one YAML file in .github/workflows/. The on: block lists which events start it.
A workflow run is one execution of that file for one event.
A job is a group of steps that run on one runner (one virtual machine).
A step is one command or one action call inside a job.
What shares state and what does not
This boundary matters every day you write YAML.
Steps inside one job share the same runner workspace. If step one writes dist/index.html, step two can read it on the same machine.
Jobs do not share files. Each job gets a fresh runner. Job B cannot see files from job A unless you pass them with artifacts, cache, or an external store.
Jobs can run in parallel unless you connect them with needs:.
A minimal workflow
Here is a workflow with one job and two steps:
name: CI
on:
pull_request:
push:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm test
When someone opens a pull request, GitHub starts one workflow run. That run has one job called test. The job has three steps on one Ubuntu runner.
If you add a second job without needs:, both jobs start at the same time on separate machines.
Draw the graph before you copy YAML
My advice: write the event and job graph in plain language before you paste a big workflow from a blog post.
Example notes:
pull_requeststarts lint + unit testspushtomainstarts build after tests pass- deploy waits for build and environment approval
Then encode the smallest workflow that matches those notes. Keep version one small enough that you can explain every trigger, every job, and every shared boundary without scrolling.
Try this on your own project: sketch the graph on paper, then compare it to your existing .github/workflows/ files. Change one if: condition on purpose, predict what will run, and check the Actions tab.
Lesson completed