Syntax reference

The syntax the exam expects you to read cold, in one place. Keep it open while you practise: workflow, job and step keys, which contexts each key can see, the expression language, workflow commands and environment files, the default variables and the three shapes of action.yml.

Page
Deep dive
Exam
GH-200
Skills measured
January 2026
Sections
13

How to use this page

This is a lookup page. The reasons behind each key — and the traps — are on the skill pages, linked from each section. Search (Ctrl/⌘+K) finds any key on this page by its exact spelling.

A workflow with every top-level key

.github/workflows/tour.yml
name: Full syntax tour
run-name: ${{ github.workflow }} · ${{ github.event_name }} · ${{ github.ref_name }}

on:
  push:
    branches: [main]
  workflow_dispatch:
    inputs:
      level:
        type: choice
        options: [quick, full]
        default: quick

permissions:
  contents: read

env:
  TZ: UTC

defaults:
  run:
    shell: bash

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

jobs:
  build:
    name: Build on ${{ matrix.os }}
    runs-on: ${{ matrix.os }}
    timeout-minutes: 20
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-latest, windows-latest]
    outputs:
      version: ${{ steps.ver.outputs.version }}
    env:
      CI: 'true'
    steps:
      - uses: actions/checkout@v7
      - id: ver
        run: echo "version=$(cat VERSION)" >> "$GITHUB_OUTPUT"
      - name: Full suite only when asked
        if: inputs.level == 'full'
        run: make test-all
        timeout-minutes: 15
        continue-on-error: true
        working-directory: app

  publish:
    needs: build
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    environment:
      name: registry
      url: https://registry.example.com
    permissions:
      contents: read
      packages: write
    container:
      image: node:24
    services:
      cache:
        image: redis:8
    steps:
      - run: echo "Publishing ${{ needs.build.outputs.version }}"

  shared:
    needs: build
    uses: ./.github/workflows/notify.yml
    with:
      version: ${{ needs.build.outputs.version }}
    secrets: inherit

Workflow keys

KeyPurpose
nameShown in the Actions tab and in badges; defaults to the file path
run-nameName of each run; can use github, inputs and vars
onEvents, activity types and filters; no expressions allowed
permissionsDefault GITHUB_TOKEN scopes for every job
envVariables for every step of every job
defaults.runshell and working-directory for run steps
concurrencygroup, cancel-in-progress, queue (single or max)
jobsThe jobs, keyed by an ID made of letters, digits, - and _, starting with a letter or _

Job keys

KeyPurpose
nameDisplay name (also the required-check name)
needsJobs that must succeed first
ifCondition; can read github, needs, vars, inputs
runs-onLabels, group or both
permissionsReplaces the workflow's token scopes for this job
environmentName, or name and url
concurrencyAs at workflow level, per job
outputsValues for jobs that need this one
env · defaultsJob-level variables and run defaults
timeout-minutesDefault 360
strategymatrix, fail-fast, max-parallel
continue-on-errorLet the run pass if this job fails
containerRun the steps in a container: image, credentials, env, ports, volumes, options
servicesSidecar containers, with the same sub-keys
uses · with · secretsCall a reusable workflow instead of running steps
stepsThe steps

Step keys

KeyPurpose
idNeeded to read the step's outputs, outcome and conclusion
nameDisplay name
ifCondition; adds success() && unless a status function is used
usesAn action: owner/repo@ref, owner/repo/path@ref, ./path, docker://image
withAction inputs; args and entrypoint for Docker actions
runA script
shellbash, sh, pwsh, powershell, cmd, python, or a custom template with {0}
envStep variables — highest precedence
working-directoryFor run
continue-on-error · timeout-minutesFailure handling for the step

Context availability

Which contexts each key can read. Anything not listed is not available there — the most common reason an expression "does nothing". Explained in Foundations.

KeyAvailable contexts
run-name, workflow concurrencygithub, inputs, vars
Workflow envgithub, secrets, inputs, vars
jobs.<id>.ifgithub, needs, vars, inputs
jobs.<id>.strategygithub, needs, vars, inputs
runs-on, job name, environment, timeout-minutes, continue-on-error, services, containergithub, needs, strategy, matrix, vars, inputs
Job envThe above, plus secrets
environment.urlThe above, plus job, runner, env, steps
Step ifgithub, needs, strategy, matrix, job, runner, env, vars, steps, inputs — not secrets
Step run, with, env, name, working-directoryEverything, including secrets
Job outputsEverything — but an output whose value contains a secret is redacted and not passed on
on.workflow_call.outputs.<id>.valuegithub, jobs, vars, inputs

hashFiles() works only in step-level keys; the status functions only in if.

Expression operators and functions

ItemMeaning
( ) [ ] .Grouping, index (github['ref']), property
! < <= > >= == != && ||Logic and comparison; string comparison ignores case
contains(search, item)Substring in a string, or item in an array
startsWith(s, v) · endsWith(s, v)Case-insensitive prefix and suffix tests
format('{0}-{1}', a, b)String templating; {{ escapes a brace
join(array, sep)Join with a separator (default ,)
toJSON(v) · fromJSON(s)Serialise · parse — fromJSON turns a JSON output into a matrix, a number or a boolean
hashFiles(patterns…)SHA-256 of the matching files, for cache keys
a.*.bObject filter: every b under every item of a

Status check functions

FunctionTrue whenTypical use
success()No earlier failure or cancellation (the implicit default)Normal steps
failure()An earlier step or needed job failedDiagnostics, notifications
cancelled()The run was cancelledClean-up after a cancel
always()AlwaysSummaries, artifact uploads; prefer !cancelled()

Workflow commands

CommandEffect
::group::title · ::endgroup::Fold lines in the log
::error file=f,line=l,endLine=,col=,endColumn=,title=t::msgError annotation (also ::warning, ::notice)
::debug::msgShown only with step debug logging
::add-mask::valueMask a value in all later log output
::stop-commands::token · ::token::Pause and resume command processing
::echo::on · ::echo::offEcho commands into the log
::set-output, ::save-state, ::set-env, ::add-pathRetired — use the environment files below

Environment files

FileLine formatEffect
$GITHUB_ENVNAME=value or NAME<<EOF … EOFVariable for later steps of the job
$GITHUB_OUTPUTname=value (multiline form too)Step output
$GITHUB_PATHA directoryPrepended to PATH for later steps
$GITHUB_STATEname=valueState for an action's post step, read as $STATE_name
$GITHUB_STEP_SUMMARYMarkdownJob summary (1 MiB per step)

On Windows with PowerShell, append with "name=value" | Out-File -FilePath $env:GITHUB_OUTPUT -Append.

Default environment variables

VariableValue
CIAlways true
GITHUB_ACTIONStrue when running in Actions
GITHUB_WORKFLOW · GITHUB_WORKFLOW_REFWorkflow name · owner/repo/.github/workflows/file.yml@ref
GITHUB_RUN_ID · GITHUB_RUN_NUMBER · GITHUB_RUN_ATTEMPTUnique run ID · per-workflow counter · re-run attempt
GITHUB_JOB · GITHUB_ACTIONJob ID · current step or action ID
GITHUB_EVENT_NAME · GITHUB_EVENT_PATHThe event · path of the payload JSON on the runner
GITHUB_REPOSITORY · GITHUB_REPOSITORY_OWNER · GITHUB_REPOSITORY_IDowner/repo · owner · numeric ID
GITHUB_REF · GITHUB_REF_NAME · GITHUB_REF_TYPEFull ref · short name · branch or tag
GITHUB_HEAD_REF · GITHUB_BASE_REFPR head and base branch (pull request events only)
GITHUB_SHAThe commit the run uses
GITHUB_ACTOR · GITHUB_TRIGGERING_ACTORWho started the original run · who started this attempt
GITHUB_WORKSPACEThe checkout directory
GITHUB_SERVER_URL · GITHUB_API_URL · GITHUB_GRAPHQL_URLHost URLs (different on GHES and data residency)
RUNNER_OS · RUNNER_ARCH · RUNNER_NAMELinux, Windows, macOS · X64, ARM64 · runner name
RUNNER_TEMP · RUNNER_TOOL_CACHETemp directory emptied after each job · toolcache
RUNNER_DEBUG1 when debug logging is on

Filter patterns

PatternMatches
*Any characters except /
**Any characters, including /
? · +Zero or one · one or more of the preceding character
[0-9]One character from the set
!patternNegates; later patterns override earlier ones
'*.md'Quote patterns that start with *, [ or ! — they mean something else in YAML

Shells

shellCommand the runner uses
(unspecified, Linux/macOS)bash -e {0}, or sh -e {0} if bash is missing
bashbash --noprofile --norc -eo pipefail {0}
(unspecified, Windows) · pwshpwsh -command ". '{0}'"
powershell · cmdWindows PowerShell 5.1 · cmd /D /E:ON /V:OFF /S /C "CALL "{0}""
python · shpython {0} · sh -e {0}

pipefail only when you ask for bash

An unspecified shell on Linux is bash -e without pipefail, so npm test | tee log.txt passes even when the tests fail. Set shell: bash (or defaults.run.shell: bash) to get -o pipefail.

Action metadata

Top-level keys: name, description and runs (required), plus author, inputs, outputs and branding. The runs block depends on the type — details in Skill 3:

action.yml excerpt
runs:
  using: node24
  main: dist/index.js
  pre: dist/setup.js
  pre-if: runner.os == 'Linux'
  post: dist/cleanup.js
  post-if: always()
action.yml excerpt
runs:
  using: docker
  image: docker://ghcr.io/octo-org/tool:3.2   # or: Dockerfile
  pre-entrypoint: /setup.sh
  entrypoint: /main.sh
  post-entrypoint: /cleanup.sh
  args:
    - ${{ inputs.target }}
  env:
    MODE: strict
action.yml excerpt
runs:
  using: composite
  steps:
    - uses: actions/setup-node@v7
      with:
        node-version: ${{ inputs.node-version }}
    - name: Build
      id: build
      shell: bash
      working-directory: ${{ inputs.path }}
      env:
        TARGET: ${{ inputs.target }}
      run: make "$TARGET"