3.2 Contexts, Expressions & Functions
Key Takeaways
- GitHub Actions contexts are structured JSON objects providing access to workflow metadata, runner environments, repository variables, secrets, and execution results.
- Expressions are evaluated at workflow runtime using ${{ <expression> }}; inside if: conditionals, the ${{ }} wrapper syntax is optional.
- Built-in string and array functions (contains(), startsWith(), endsWith(), format(), join()) allow dynamic string manipulation and pattern matching directly in YAML.
- Data transformation functions toJSON() and fromJSON() enable object serialization, log inspection, and dynamic parsing of JSON arrays for matrix generation.
- The hashFiles() function calculates a single SHA-256 hash across one or more file glob patterns, providing deterministic cache keys and fingerprint verification.
Contexts, Expressions & Functions
GitHub Actions workflows are not static scripts; they are dynamic, programmable automation pipelines. Through contexts, expressions, and built-in functions, you can interrogate runtime metadata, evaluate boolean logic, manipulate strings and JSON payloads, and dynamically control step and job execution.
1. GitHub Actions Contexts Architecture
A context is a structured collection of variables and metadata exposed as a JSON object during workflow execution. Contexts provide information about the triggering webhook event, the repository, the runner environment, secrets, configuration variables, and upstream job/step outputs.
+-----------------------------------------------------------------------------+
| GITHUB ACTIONS CONTEXT MODEL |
| |
| [ Workflow Execution Engine ] |
| | |
| +---> github : Event payload, ref, actor, repository, SHA |
| +---> env : Workflow / Job / Step environment variables |
| +---> vars : Non-sensitive configuration variables |
| +---> secrets : Encrypted repository / org / env secrets |
| +---> steps : Step execution status and outputs |
| +---> runner : OS, architecture, temp path, tool cache |
| +---> needs : Upstream job outputs and status results |
| +---> matrix : Current matrix permutation properties |
| +---> strategy : Matrix fail-fast and max-parallel settings |
| +---> inputs : Workflow dispatch / reusable workflow inputs |
+-----------------------------------------------------------------------------+
Comprehensive Context Reference Table
| Context | Description & Scope | Key Properties & Common Exam Usage |
|---|---|---|
github | Metadata about the workflow run and triggering event. Available everywhere. | github.sha, github.ref, github.ref_name, github.event_name, github.event.*, github.actor, github.repository, github.run_id, github.run_number, github.token. |
env | Environment variables defined at workflow, job, or step level. | ${{ env.TARGET_ENV }}, ${{ env.APP_PORT }}. Note: Not available in runs-on or workflow-level on:. |
vars | Non-sensitive configuration variables configured in repository/org settings. | ${{ vars.DOCKER_REGISTRY }}, ${{ vars.NODE_VERSION }}, ${{ vars.ENABLE_FEATURE_X }}. |
secrets | Encrypted secrets configured in repo/org/environment settings. Masked in logs. | ${{ secrets.PROD_API_KEY }}, ${{ secrets.GITHUB_TOKEN }}, ${{ secrets.AWS_ROLE_ARN }}. |
steps | Information about steps executed within the current job. | steps.<step_id>.outputs.<name>, steps.<step_id>.conclusion, steps.<step_id>.outcome. |
runner | Information about the physical/virtual runner executing the job. | runner.os (Linux, Windows, macOS), runner.arch (X64, ARM64), runner.temp, runner.tool_cache. |
needs | Outputs and execution status of upstream jobs defined in needs:. | needs.<job_id>.outputs.<output_name>, needs.<job_id>.result (success, failure, cancelled, skipped). |
matrix | Properties of the active matrix permutation. Available only in matrix jobs. | matrix.os, matrix.node-version, matrix.custom_property. |
strategy | Information about the matrix execution strategy. | strategy.fail-fast, strategy.max-parallel, strategy.job-index, strategy.job-total. |
job | Information about the currently running job. | job.status (success, failure, cancelled). |
inputs | Inputs passed via workflow_dispatch, workflow_call, or composite action. | inputs.environment, inputs.deploy_tag, inputs.dry_run. |
[!WARNING] Context Availability Rules on the Exam: Not all contexts are available at all locations in a YAML file:
secretsandvarscannot be accessed in theconcurrency:oron:trigger blocks.envdefined at the job level cannot be accessed in the job'sruns-on:block (usematrixor literal values instead).stepscontext is only accessible within thesteps:array of that specific job.
2. Expression Syntax & Evaluation Rules
Expressions are written using the format ${{ <expression> }}. They can contain literal values, context references, mathematical operators, logical operators, and built-in functions.
steps:
- name: Conditional Execution Example
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
run: ./deploy.sh
The if: Condition Special Syntax Rule
In an if: key at the job or step level, the ${{ }} wrapper is optional unless you are concatenating an expression with literal strings:
# Both of these are 100% valid and identical in behavior:
if: github.event_name == 'pull_request' && !contains(github.event.pull_request.labels.*.name, 'do-not-merge')
if: ${{ github.event_name == 'pull_request' && !contains(github.event.pull_request.labels.*.name, 'do-not-merge') }}
Literal Data Types in Expressions:
- Boolean:
true,false(case-insensitive in expression engine) - Null:
null - Number:
42,3.14159,-10,0xFF - String: Single quotes must be used for literal strings:
'production','ubuntu-latest'. Double quotes inside${{ 'string' }}cause syntax errors. To escape a literal single quote inside a string, use two single quotes:'It''s a release build'.
3. Built-In Functions Catalog
GitHub Actions provides a rich suite of built-in functions for string matching, formatting, array inspection, JSON handling, and file fingerprinting.
String & Array Manipulation Functions
-
contains(search, item)Returnstrueifsearchcontainsitem. Ifsearchis an array, it checks for item presence. Ifsearchis a string, it checks for substring occurrence. Comparison is case-insensitive.# Checks if branch name contains 'feature/' if: contains(github.ref, 'feature/') # Checks if PR has 'security' label if: contains(github.event.pull_request.labels.*.name, 'security') -
startsWith(searchString, searchValue)&endsWith(searchString, searchValue)Checks if a string starts or ends with a given substring (case-insensitive).if: startsWith(github.ref, 'refs/tags/v') if: endsWith(github.actor, '-bot') -
format(string, val1, val2, ...)Replaces{0},{1}, etc., in the template string with supplied arguments.run: echo "${{ format('Deploying {0} to {1} cluster', github.sha, vars.CLUSTER_NAME) }}" -
join(array, optional_separator)Concatenates array elements into a single string. If separator is omitted, commas are used.# If matrix.targets = ['x86_64', 'arm64'], join yields 'x86_64;arm64' run: ./build --targets "${{ join(matrix.targets, ';') }}"
Data Transformation Functions: toJSON & fromJSON
-
toJSON(value)Converts any context, object, or array into a pretty-printed JSON string. Invaluable for debugging and inspecting webhook payloads.- name: Dump GitHub Context for Debugging run: echo '${{ toJSON(github) }}' -
fromJSON(str)Parses a JSON string into a structured data type (object or array), or converts JSON literals ('true','42') into boolean/numeric primitives.# Accessing properties from a JSON string output - name: Parse Metadata run: echo "Deployed to: ${{ fromJSON(needs.setup.outputs.config_json).region }}"
Integrity & Caching Function: hashFiles()
hashFiles(path1, path2, ...) accepts one or more comma-separated file paths or glob patterns and returns a single SHA-256 hash of the matched files' contents. If no files match or the path does not exist, it returns an empty string "".
- name: Cache Node Modules
uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json', '**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-node-
4. Operator Precedence & Evaluation Truth Table
GitHub Actions evaluates expressions using standard operator precedence (highest to lowest):
()(Grouping)!(Logical NOT)<,<=,>,>=(Relational operators)==,!=(Equality operators)&&(Logical AND)||(Logical OR)
Expression Evaluation & Truthiness Rules
| Expression | Evaluation Result | Explanation |
|---|---|---|
null == '' | false | null is distinct from an empty string. |
'hello' == 'HELLO' | true | String equality comparisons in expressions are strictly case-insensitive. |
!null | true | null coerces to falsy; NOT null evaluates to true. |
!'' | true | Empty string '' coerces to falsy; NOT '' evaluates to true. |
!'false' | false | Critical Exam Trap: Non-empty string 'false' is truthy! Use boolean false or fromJSON('false'). |
contains('Hello World', 'world') | true | Substring searching is case-insensitive. |
hashFiles('non-existent/**') | '' (empty string) | Unmatched file globs return an empty string. |
5. End-to-End Practical YAML Workflow
name: Advanced Context & Expression Pipeline
on:
pull_request:
types: [opened, synchronize, labeled]
push:
branches: [main]
jobs:
evaluate-environment:
runs-on: ubuntu-latest
outputs:
is_release: ${{ steps.check-release.outputs.is_release }}
target_env: ${{ steps.check-release.outputs.target_env }}
steps:
- name: Inspect Runner Context
run: |
echo "Operating System: ${{ runner.os }}"
echo "Architecture: ${{ runner.arch }}"
echo "Temp Directory: ${{ runner.temp }}"
- name: Determine Deployment Target
id: check-release
run: |
if [[ "${{ github.ref }}" == "refs/heads/main" && "${{ github.event_name }}" == "push" ]]; then
echo "is_release=true" >> "$GITHUB_OUTPUT"
echo "target_env=production" >> "$GITHUB_OUTPUT"
else
echo "is_release=false" >> "$GITHUB_OUTPUT"
echo "target_env=preview" >> "$GITHUB_OUTPUT"
fi
conditional-step-demo:
needs: evaluate-environment
runs-on: ${{ fromJSON('["ubuntu-latest", "ubuntu-24.04"]')[0] }}
steps:
- name: Production Only Step
# Demonstrates unquoted if expression with boolean coercion
if: needs.evaluate-environment.outputs.is_release == 'true' && github.actor != 'dependabot[bot]'
run: echo "Executing authorized production release triggered by ${{ github.actor }}"
- name: Formatting Notification Message
run: |
MSG="${{ format('Run {0} for commit {1} targeting {2}', github.run_number, github.sha, needs.evaluate-environment.outputs.target_env) }}"
echo "Formatted Message: $MSG"
A workflow author receives a JSON-encoded string array in a step output steps.get-targets.outputs.json_list containing '["prod-east", "prod-west"]'. Which expression correctly converts this string into an iterable array data structure in YAML?
Which of the following statements is TRUE regarding expression syntax inside an if: conditional key on a job or step?
A workflow step utilizes key: ${{ runner.os }}-cache-${{ hashFiles('**/non-existent-lockfile.json') }}. If no files match the glob pattern, what does hashFiles() return?