What Is a Context?
In GitHub Actions, a context is a collection of properties related to a specific category — the runner environment, GitHub repository data, job information, secrets, and more. GitHub Actions provides these contexts automatically so you can read relevant data inside your workflows.
A context property is referenced using the expression syntax:
For example, ${{ github.actor }} reads the actor property from the github context, which returns the username of the user who triggered the workflow run. Properties are usually strings, but some may be objects.
Context Availability
Not all contexts are available at every point in a workflow. Availability depends on what is currently happening:
- The
secrets context is only available at certain points during job execution, not in every expression location. - The
matrix context is only available when the matrix job strategy is active. - The
jobs context is exclusive to reusable workflows.
The GitHub Actions documentation contains a detailed availability matrix for each context and its properties.
Available Contexts
The table below provides a high-level overview of all built-in contexts, their purpose, and example properties.
| Context | Purpose | Example properties |
|
|
github | Data attributes about the workflow run and the event that triggered it. | github.ref | github.event_name | github.repository |
env | Variables that have been set in a workflow, job, or step. | env.<env_name> |
|
|
vars | Configuration variables set for a repository, environment, or organization. | vars.<var_name> |
|
|
job | Information about the currently running job. | job.container | job.services | job.status |
jobs | Only available for reusable workflows. Used to set outputs from reusable workflows. | jobs.<job_id>.results | jobs.<job_id>.outputs |
|
steps | Contains information from a step that has an id and has already run. | steps.<step_id>.outcome | steps.<step_id>.outputs |
|
runner | Information about the runner executing the current job. | runner.name | runner.os | runner.arch |
secrets | Contains names and values of secrets. Not available in composite workflows but can be passed in. | secrets.GITHUB_TOKEN | secrets.<secret_name> |
|
strategy | When a matrix is used, contains information about the matrix strategy for the current job. | strategy.job-index | strategy.max-parallel |
|
matrix | For workflows using a matrix, contains the matrix properties that apply to the current job. | matrix.<property_name> |
|
|
needs | Used to collect output from other jobs; contains output from all jobs that are defined as a direct dependency of the current job. | needs.<job_id> | needs.<job_id>.outputs | needs.<job_id>.outputs.<output_name> |
inputs | Contains input properties passed to an action, a reusable workflow, or a manually triggered workflow. | inputs.<name> |
|
|
Using Contexts in Expressions
You can read a context property anywhere that accepts an expression, including step fields, environment variable values, and job-level settings:
run-name: Pipeline run by @${{ github.actor }}
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Print branch
run: echo "Running on branch ${{ github.ref }}"
Contexts can also power conditional expressions using the if: key. The following step only runs when the current branch is main:
steps:
- name: Deploy to production
if: ${{ github.ref == 'refs/heads/main' }}
run: ./deploy.sh
Untrusted Input in Contexts
Some context properties may contain values that were supplied by external users — for example, a pull request title, a branch name, or an input parameter. These values could contain injected code if a malicious actor crafts them deliberately.
Properties that are commonly considered untrusted input include:
github.event.pull_request.titlegithub.event.pull_request.bodygithub.head_ref- Any user-supplied
inputs.* values
Best practice: Never interpolate untrusted context values directly into run: shell commands. Instead, assign them to an intermediate environment variable first, which prevents shell injection:
# Unsafe — do not do this:
- run: echo "${{ github.event.pull_request.title }}"
# Safe — assign to env variable first:
- env:
PR_TITLE: ${{ github.event.pull_request.title }}
run: echo "$PR_TITLE"
More detail on security practices related to contexts is covered in the security chapter of this course.
The env Context and Custom Variables
While most contexts expose predefined data provided by GitHub, the env context lets you define your own data. You declare environment variables at the workflow, job, or step level, then read them back using ${{ env.MY_VAR }}:
env:
APP_ENV: production
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Show environment
run: echo "Deploying to ${{ env.APP_ENV }}"
This makes it easy to centralise configuration values and reuse them throughout a workflow without repeating string literals.
Key Takeaways
- Contexts are collections of properties GitHub Actions makes available inside workflows.
- Reference any property with
${{ context.property }}. - Context availability varies — some are only present under specific conditions.
- Contexts can be used in step fields, environment variables, and
if: conditions. - Treat user-supplied context values as untrusted input and never inject them directly into shell commands.
- The
env context lets you define and reuse your own custom variables.
Complete Example Workflow
The following is a real, runnable GitHub Actions workflow that demonstrates every concept covered in this lesson. Each line is annotated with a comment explaining why it is there, not just what it does.
# Lesson 06 — Contexts
# This workflow demonstrates how to read data from GitHub Actions contexts
# at runtime. Every context property below is explained so you understand
# why it is used, not just what it returns.
name: Lesson 06 - Contexts Demo
# Trigger on pushes to any branch AND on pull request events.
# Both triggers give us interesting context values to inspect
# (e.g. github.ref differs between a push and a pull_request event).
on:
push:
pull_request:
# A workflow-level environment variable defined with a hard-coded value.
# It is accessible via the 'env' context as ${{ env.APP_ENVIRONMENT }}
# anywhere in this workflow — in all jobs and all steps.
env:
APP_ENVIRONMENT: staging
jobs:
show-contexts:
# 'runs-on' determines which machine executes this job.
# 'runner.os' will return "Linux" for this runner type.
runs-on: ubuntu-latest
steps:
# --- github context ---
# The 'github' context contains data about the workflow run itself
# and the event that triggered it. These values are read-only.
- name: Print github context values
run: |
# github.actor — the username of whoever triggered this workflow run
echo "Triggered by: ${{ github.actor }}"
# github.event_name — tells us which event started this run (push, pull_request, etc.)
# Useful when a workflow listens to multiple triggers and needs to branch its logic.
echo "Event: ${{ github.event_name }}"
# github.ref — the full ref (branch or tag) that was pushed or targeted.
# Format: refs/heads/<branch> for branches, refs/tags/<tag> for tags.
echo "Ref: ${{ github.ref }}"
# github.sha — the exact commit SHA that triggered this run.
# Pinning deployments or build artifacts to a SHA makes them reproducible.
echo "Commit SHA: ${{ github.sha }}"
# github.repository — owner/repo-name; useful when calling the GitHub API
# from within a workflow step to refer back to this repository.
echo "Repository: ${{ github.repository }}"
# --- runner context ---
# The 'runner' context describes the machine executing the current job.
# This is helpful for conditional logic that should differ by OS or architecture.
- name: Print runner context values
run: |
# runner.os — the operating system of the runner (Linux, Windows, macOS).
echo "OS: ${{ runner.os }}"
# runner.arch — CPU architecture (X64, ARM64, etc.).
# Important when building or testing platform-specific binaries.
echo "Architecture: ${{ runner.arch }}"
# runner.name — the name of the runner instance handling this job.
echo "Runner name: ${{ runner.name }}"
# --- env context ---
# Variables declared under the top-level 'env:' key are available via the
# 'env' context. Here we read the APP_ENVIRONMENT variable defined above.
- name: Print env context value
run: |
# ${{ env.APP_ENVIRONMENT }} reads the workflow-level variable we declared.
# This avoids repeating the string literal in every step that needs it.
echo "Deploying to: ${{ env.APP_ENVIRONMENT }}"
# --- Conditional step using github context ---
# This step only runs when the triggering ref is the main branch.
# Using 'if:' with a context expression lets us skip steps that make
# sense only in certain situations (e.g. production deployments).
- name: Run only on main branch
if: ${{ github.ref == 'refs/heads/main' }}
run: |
echo "This is a main-branch push. Safe to deploy."
# --- steps context ---
# To read output from a previous step you must give that step an 'id'.
# The 'steps' context then exposes its outputs and outcome.
- name: Generate a value
id: generate # id is required so that later steps can reference this step's output
run: |
# We write to GITHUB_OUTPUT to create a named output for this step.
# Any subsequent step can read it via ${{ steps.generate.outputs.result }}.
echo "result=hello-from-step" >> "$GITHUB_OUTPUT"
- name: Read output from previous step
run: |
# steps.generate.outputs.result — reads the 'result' output we wrote above.
# steps.generate.outcome — tells us whether the previous step succeeded,
# failed, was skipped, or was cancelled.
echo "Output value : ${{ steps.generate.outputs.result }}"
echo "Step outcome : ${{ steps.generate.outcome }}"
# --- Untrusted input: safe pattern ---
# github.event.pull_request.title is set by whoever opens the pull request.
# A malicious actor could craft a PR title containing shell metacharacters
# or command injection sequences. Assigning the value to an environment
# variable first lets the shell treat it as plain data, not as code,
# completely neutralising the injection risk.
- name: Print PR title safely (untrusted input pattern)
if: ${{ github.event_name == 'pull_request' }}
env:
# Assigning the untrusted value to an env variable here — NOT interpolating
# it directly inside the 'run:' block — is the secure way to handle it.
PR_TITLE: ${{ github.event.pull_request.title }}
run: |
echo "PR title: $PR_TITLE"
Next topic: Environment Variables — defining and using variables at the workflow, job, and step level.