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?
${{ toJSON(steps.get-targets.outputs.json_list) }}
${{ join(steps.get-targets.outputs.json_list, ',') }}
${{ fromJSON(steps.get-targets.outputs.json_list) }}
${{ format(steps.get-targets.outputs.json_list) }}
Which of the following statements is TRUE regarding expression syntax inside an if: conditional key on a job or step?
The ${{ }} expression wrapper delimiters are optional inside if: keys; GitHub Actions evaluates the expression directly.
Expressions in if: keys MUST always be enclosed inside ${{ }} or the workflow parser throws a syntax error.
String comparisons inside if: expressions are strictly case-sensitive, so 'Main' == 'main' evaluates to false.
Environment variables from the env context cannot be referenced inside an if: conditional.
A workflow step utilizes key: ${{ runner.os }}-cache-${{ hashFiles('**/non-existent-lockfile.json') }}. If no files match the glob pattern, what does hashFiles() return?
It throws a fatal step error that terminates the job immediately.
It returns a default SHA-256 hash of 64 consecutive zeros (0000...0000).
It returns the literal string 'undefined'.
It returns an empty string "", resulting in the key resolving to 'Linux-cache-'.
Sections you finish are checked off in the contents.