GitHub Actions Context and Expressions Explained
Master the GitHub Actions github, secrets, inputs, matrix and needs contexts, plus the expression functions that power every if condition and output.
Netbay Cloud Team
Netbay Engineering
On this page
Every value your workflow can read — the branch it ran on, the author of the commit, a secret, an input, a matrix value, a previous job's result — lives somewhere in GitHub Actions' context object model. Master these named contexts and the small expression language that reads them, and workflows stop being guesswork. This post maps the contexts you will actually use and shows the syntax for reading and transforming their values.
The expression syntax
GitHub expressions are written with the dollar-double-brace form: in the workflow file they appear as ${{ expression }}, where inside the braces you write a pure expression — property access, function calls, and operators. The parser evaluates the expression only at runtime; the expression itself never directly runs shell code. That separation is a key safety property. Still, never place untrusted raw input into a run line without quoting, because that line does reach the shell.
Expressions support simple operators (==, !=, &&, ||, !), literals (booleans, numbers, strings), and a library of functions like contains, startsWith, endsWith, format, join, and github object access.
steps:
- name: Check branch
if: github.ref == 'refs/heads/main' && success()
run: echo on main
- name: Name the build
run: echo "Building ${{ github.sha }}"The github context
The github context is the richest and most frequently read. It holds metadata about the current event and run: github.repository, github.ref, github.sha, github.actor (who triggered it), github.event_name, github.workflow, and github.run_id. Under github.event you get the full payload of the triggering webhook, so a pull_request event exposes github.event.pull_request.number, its title, its base branch and more.
These values drive most if conditions and label build artifacts. Use github.repository to name artifacts, github.sha (shortened via github.sha.substr(0,7)) for unique build tags, and github.actor to fetch per-author secrets if needed.
The secrets, inputs and env contexts
The secrets context holds every secret available to the run. Reference them as secrets.NAME. They are never echoed to logs when used in env blocks, but if you put a secret into a run line GitHub masks its value in the logs. The inputs context holds workflow_dispatch inputs (inputs.NAME). The env context holds environment variables set with the env key or from a matrix-provided environment — read them as env.NAME within steps.
Secrets and inputs differ in one security-relevant way: secrets are masked in logs and cannot be passed to run in ways that expose them; inputs are visible and should be treated as untrusted.
on:
workflow_dispatch:
inputs:
target:
description: where to deploy
required: true
jobs:
deploy:
runs-on: ubuntu-22.04
steps:
- name: Echo target
run: echo "target is ${{ inputs.target }}"
- name: Use a secret
env:
API_KEY: ${{ secrets.DEPLOY_KEY }}
run: ./deploy.sh --key "$API_KEY"The matrix, needs and jobs contexts
Inside a matrix job, the matrix context holds the current combination (matrix.os, matrix.node). The needs context lets a job read outputs from jobs it depends on, via needs.<job>.outputs.<name>. That is the standard mechanism for passing data between sequential jobs. The jobs context provides the result and outputs of all jobs that ran before.
To pass output between jobs, the producing job declares outputs and sets them with set-output (modern syntax uses echo "name=value" >> "$GITHUB_OUTPUT"), then the consumer reads needs:
jobs:
build:
runs-on: ubuntu-22.04
outputs:
tag: ${{ steps.tagstep.outputs.tag }}
steps:
- id: tagstep
run: echo "tag=v1-${{ github.run_number }}" >> "$GITHUB_OUTPUT"
deploy:
runs-on: ubuntu-22.04
needs: build
steps:
- run: echo "deploying ${{ needs.build.outputs.tag }}"Functions worth knowing
You will rely on a handful of functions. Use contains() to check if a string or array contains a value (it also works on branch names). Use startsWith/endsWith for ref patterns. Use format() to build strings with placeholders. Use always(), success(), failure(), and cancelled() for status logic. When comparing, remember null behaves as an empty string in many comparisons.
Two traps: a missing context returns null (renders as empty), and error()/failure() helpers force the workflow to halt or mark a step failed respectively. Use error() to stop early when a precondition is violated, which is far cleaner than chaining if conditions.
Takeaway
The contexts are the object model of a run: github for event metadata, secrets/inputs for configuration, matrix/needs for orchestrating parallel and sequential work. Learn the ${'/'} expression syntax and the top functions, and you can write conditions, name artifacts, and pass data between jobs with confidence. To give those workflows a real deploy target, a Netbay Ubuntu VPS is ready in under a minute — netbayhosts.in.
Keep reading
Follow along on a real VPS
Deploy Linux in under 60 seconds
These guides are written against Ubuntu, Debian, and RHEL-family images — the same ones on NetBay.
Deploy an instance