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.
Last updated: August 2026

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

ContextDescription & ScopeKey Properties & Common Exam Usage
githubMetadata 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.
envEnvironment variables defined at workflow, job, or step level.${{ env.TARGET_ENV }}, ${{ env.APP_PORT }}. Note: Not available in runs-on or workflow-level on:.
varsNon-sensitive configuration variables configured in repository/org settings.${{ vars.DOCKER_REGISTRY }}, ${{ vars.NODE_VERSION }}, ${{ vars.ENABLE_FEATURE_X }}.
secretsEncrypted secrets configured in repo/org/environment settings. Masked in logs.${{ secrets.PROD_API_KEY }}, ${{ secrets.GITHUB_TOKEN }}, ${{ secrets.AWS_ROLE_ARN }}.
stepsInformation about steps executed within the current job.steps.<step_id>.outputs.<name>, steps.<step_id>.conclusion, steps.<step_id>.outcome.
runnerInformation about the physical/virtual runner executing the job.runner.os (Linux, Windows, macOS), runner.arch (X64, ARM64), runner.temp, runner.tool_cache.
needsOutputs and execution status of upstream jobs defined in needs:.needs.<job_id>.outputs.<output_name>, needs.<job_id>.result (success, failure, cancelled, skipped).
matrixProperties of the active matrix permutation. Available only in matrix jobs.matrix.os, matrix.node-version, matrix.custom_property.
strategyInformation about the matrix execution strategy.strategy.fail-fast, strategy.max-parallel, strategy.job-index, strategy.job-total.
jobInformation about the currently running job.job.status (success, failure, cancelled).
inputsInputs 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:

  • secrets and vars cannot be accessed in the concurrency: or on: trigger blocks.
  • env defined at the job level cannot be accessed in the job's runs-on: block (use matrix or literal values instead).
  • steps context is only accessible within the steps: 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

  1. contains(search, item) Returns true if search contains item. If search is an array, it checks for item presence. If search is 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')
    
  2. 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')
    
  3. 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) }}"
    
  4. 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

  1. 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) }}'
    
  2. 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):

  1. () (Grouping)
  2. ! (Logical NOT)
  3. <, <=, >, >= (Relational operators)
  4. ==, != (Equality operators)
  5. && (Logical AND)
  6. || (Logical OR)

Expression Evaluation & Truthiness Rules

ExpressionEvaluation ResultExplanation
null == ''falsenull is distinct from an empty string.
'hello' == 'HELLO'trueString equality comparisons in expressions are strictly case-insensitive.
!nulltruenull coerces to falsy; NOT null evaluates to true.
!''trueEmpty string '' coerces to falsy; NOT '' evaluates to true.
!'false'falseCritical Exam Trap: Non-empty string 'false' is truthy! Use boolean false or fromJSON('false').
contains('Hello World', 'world')trueSubstring 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"
Test Your Knowledge

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?

A
B
C
D
Test Your Knowledge

Which of the following statements is TRUE regarding expression syntax inside an if: conditional key on a job or step?

A
B
C
D
Test Your Knowledge

A workflow step utilizes key: ${{ runner.os }}-cache-${{ hashFiles('**/non-existent-lockfile.json') }}. If no files match the glob pattern, what does hashFiles() return?

A
B
C
D