GitHub Actions Workflows: an Introduction
A GitHub Actions workflow is a script that lives in a repository and runs when something happens to that repository.
- Where it lives: a YAML file in the repository's
.github/workflows/directory, committed like any other file. - When it runs: on the events it names — a push, a pull request, a button press, a schedule.
- What it runs: shell commands, and ready-made steps called actions, on a fresh machine GitHub lends it.
- How it is organised: commands are grouped into jobs; each job runs on a machine of its own.
A first workflow
A workflow that greets every push and every pull request, in two jobs:
.github/workflows/hello.yml:
1name: hello
2
3on:
4 push:
5 pull_request:
6
7jobs:
8 greet:
9 runs-on: ubuntu-latest
10 steps:
11 - name: Say hello
12 run: echo "Hello from $GITHUB_ACTOR, on branch $GITHUB_REF_NAME"
13
14 - name: Look at the machine
15 run: |
16 whoami
17 pwd
18 ls -la
19
20 list-files:
21 runs-on: ubuntu-latest
22 needs: greet
23 steps:
24 - name: Check out the repository
25 uses: actions/checkout@v7
26
27 - name: List its files
28 run: ls -la
on:names the events that start it.jobs:holds two jobs,greetandlist-files.runs-on:picks the machine each job runs on.steps:are the commands, run one after the other.
Once this file is pushed, the push itself starts the first run.
Events: what starts a workflow
GitHub watches the repository, and starts every workflow whose on: names the event that just happened.
| Event | Happens when |
|---|---|
push | commits are pushed to a branch, or a tag is pushed |
pull_request | a pull request is opened, or new commits are pushed to it |
workflow_dispatch | someone presses Run workflow in the Actions tab |
schedule | a time comes round, written as a cron line: - cron: '0 6 * * 1' is Mondays at 06:00 UTC |
issues, release, … | an issue is opened, a release is published, and many more |
An event can be narrowed down:
1on:
2 push:
3 branches: [main] # pushes to main only
4 paths: ['app/**'] # and only when a file under app/ changed
The run knows what started it: which event, which commit, which branch, who pushed. It reads them as
environment variables ($GITHUB_SHA, $GITHUB_REF_NAME, $GITHUB_ACTOR) or as expressions in the YAML
(${{ github.sha }}).
Jobs: a group of steps on one machine
A job is a list of steps that run in order, on one machine, called a runner.
- The runner is a fresh virtual machine. GitHub creates it when the job starts and throws it away when the job
ends.
ubuntu-latest,windows-latestandmacos-latestare GitHub's own machines.- They come with the usual tools installed: git, docker, Python, Node, the AWS CLI, kubectl,
gh, and more.
- The runner starts empty: the repository is not on it.
- That is why most jobs begin with
actions/checkout, which clones the repository onto the runner.
- That is why most jobs begin with
- Jobs share nothing. Each has a machine of its own, so a file one job makes is not on the next one's.
- Jobs run in parallel, unless one names another in
needs:; then it waits for that job to succeed.- In the first workflow,
list-filesstarts only aftergreethas passed.
- In the first workflow,
- A failed step fails its job: a command exiting with a non-zero code stops the job there.
Steps: shell commands and actions
A step is one of two things.
run:— shell commands, run by bash on Linux and macOS, PowerShell on Windows.- Anything a shell can do on that machine: build, test, call
docker,curlan API, run a script from the repository. - A
|afterrun:holds several lines, run as one script.
- Anything a shell can do on that machine: build, test, call
uses:— an action: a ready-made step someone packaged, namedowner/repository@version.actions/checkout@v7clones the repository;docker/login-action@v4logs in to a registry.- Its inputs go under
with:. - Thousands are listed in the GitHub Marketplace.
1 - name: Log in to Docker Hub
2 uses: docker/login-action@v4
3 with:
4 username: alice
5 password: ${{ secrets.DOCKERHUB_TOKEN }}
Secrets and variables
A workflow is committed to the repository, so it must not hold a password. GitHub keeps such values in the repository's settings instead, under Settings → Secrets and variables → Actions.
- A secret is written once and never shown again; a run reads it as
${{ secrets.NAME }}, and GitHub masks it as***in the log. - A variable is a plain value, such as a user name or a region; a run reads it as
${{ vars.NAME }}.
Runs and their logs
Every time a workflow starts, GitHub records a run, under the repository's Actions tab.
- A run shows its jobs, each job its steps, and each step its output, line by line.
- A green tick or a red cross marks each run, and appears beside the commit and on the pull request that started it.
- A failed run can be started again with Re-run jobs, on the same commit.
For the first workflow, the greet job's log reads like this:
1Say hello
2 Hello from alice, on branch main
3Look at the machine
4 runner
5 /home/runner/work/hello-actions/hello-actions
6 total 8
7 drwxr-xr-x 2 runner docker 4096 Oct 15 09:12 .
8 drwxr-xr-x 3 runner docker 4096 Oct 15 09:12 ..
- The job runs as the user
runner, in a directory named after the repository. - The directory is empty:
greethas no checkout step. Thelist-filesjob, which checks out first, lists the repository's files there.
The Pipeline Identities labs build on this: a workflow whose job reaches Docker Hub, AWS, a Kubernetes cluster and Slack, and the credentials each one asks for.