Variables, contexts and passing data between jobs
Most workflow questions are about moving values around: into steps, between steps, and between jobs.
Environment variables
Use env at three levels. The closest level wins.
| Level | Where | Scope |
|---|---|---|
| Workflow | top of file | all jobs |
| Job | under the job | all steps in that job |
| Step | under the step | that step only |
env:
LEVEL: workflow
jobs:
test:
runs-on: ubuntu-latest
env:
LEVEL: job
steps:
- run: echo "$LEVEL $RUN_NUMBER"
env:
RUN_NUMBER: ${{ github.run_number }}
The smallest scope is a step. Read a variable in a shell with $NAME. Read it in YAML expressions with ${{ env.NAME }}.
Contexts and default variables
Expressions use contexts: github, env, vars, secrets, needs, matrix, runner, steps. For example ${{ github.run_number }}. The same data also exists as default environment variables such as GITHUB_RUN_NUMBER, GITHUB_ACTOR and GITHUB_REPOSITORY. GITHUB_ACTOR is the user or app that started the run.
Configuration variables (vars context) hold non-sensitive values and are set at repository, environment or organization level. Use secrets for anything sensitive.
Step outputs and files
Workflow commands write to files. The old set-output command is deprecated.
steps:
- id: color
run: echo "action_color=purple" >> "$GITHUB_OUTPUT"
- run: echo "${{ steps.color.outputs.action_color }}"
- run: echo "MODE=fast" >> "$GITHUB_ENV"
GITHUB_ENV sets a variable for later steps. GITHUB_OUTPUT sets an output for the step with that id.
Passing data between jobs
Jobs run on separate runners. There are two ways to share data.
- Job outputs: for small strings. Map a step output to
jobs.<id>.outputs, then read it asneeds.<id>.outputs.<name>. - Artifacts: for files. Upload with
actions/upload-artifactand download in another job withactions/download-artifact.
jobs:
job1:
runs-on: ubuntu-latest
outputs:
output1: ${{ steps.s.outputs.val }}
steps:
- id: s
run: echo "val=42" >> "$GITHUB_OUTPUT"
job2:
needs: job1
runs-on: ubuntu-latest
steps:
- run: echo "${{ needs.job1.outputs.output1 }}"
Jobs run in parallel unless needs creates an order. A matrix such as strategy.matrix.node: [18, 20, 22] creates one job per value.
Exam traps
variables:andenvironment:are not valid keys for variables. The key isenv, withNAME: value, neverNAME = value.${{ GITHUB_RUN_NUMBER }}is invalid. Use${{ github.run_number }}or$GITHUB_RUN_NUMBERin a shell.- Cache is not a way to pass data between jobs. Use outputs or artifacts.
- A
needsreference only works for jobs listed inneeds. GITHUB_ENVandGITHUB_OUTPUTare different: environment variable versus step output.
Practice questions
Choose an answer, then review the explanation to check your understanding.
1. Which step correctly sets and uses a custom variable named dbserver?
Show answer
Answer: B.env is a map of key-value pairs. It can be set at workflow, job or step level.
2. A dependent job must read output1 produced by an upstream job named job1. Which expression is correct?
Show answer
Answer: C.Use the needs context, and the job must list job1 under needs. The path is needs.<job>.outputs.<name>.
3. A step must publish a value so later steps can read it as a step output. Which command does that?
Show answer
Answer: A.$GITHUB_OUTPUT sets step outputs. GITHUB_ENV sets an environment variable for later steps instead.
4. A build must run against Node 18, 20 and 22. What do you use?
Show answer
Answer: A.A matrix strategy creates one job per value in the list.