Skip to main content

Syntax Reference

Top-Level Structureโ€‹

name: Workflow Name # displayed in GitHub UI

on: ... # triggers (see core-concepts)

env: # workflow-level env vars (available to all jobs)
NODE_ENV: production

jobs:
job-id: # arbitrary key, used in needs:
runs-on: ubuntu-latest
env: ... # job-level env vars
steps: ...

Contextsโ€‹

Contexts are objects available inside ${{ }} expressions.

ContextDescription
github.shaThe commit SHA that triggered the workflow
github.refBranch or tag ref (e.g. refs/heads/main)
github.actorUsername that triggered the run
github.event_nameEvent that triggered (e.g. push, pull_request)
github.repositoryowner/repo string
env.MY_VAREnv var set at workflow/job/step level
secrets.NAMESecret from repo/org settings
needs.job-id.outputs.keyOutput from an upstream job
matrix.<property>Current matrix dimension value (e.g. matrix.os, matrix.node-version)

Expressionsโ€‹

Use ${{ }} anywhere in YAML values:

- name: Print branch
run: echo "Branch is ${{ github.ref }}"

- name: Use secret
env:
TOKEN: ${{ secrets.MY_TOKEN }}
run: curl -H "Authorization: Bearer $TOKEN" ...

Built-in Functionsโ€‹

FunctionExampleReturns
contains(str, substr)contains(github.ref, 'main')bool
startsWith(str, prefix)startsWith(github.ref, 'refs/tags/')bool
endsWith(str, suffix)endsWith(github.actor, 'bot')bool
format(str, args...)format('Hello {0}', github.actor)string
join(array, sep)join(matrix.os, ', ')string
toJSON(value)toJSON(github.event)string
fromJSON(string)fromJSON(steps.parse.outputs.result)object

if: Conditionalsโ€‹

Apply to jobs or individual steps. Uses expression syntax without ${{ }}.

jobs:
deploy:
if: github.ref == 'refs/heads/main' # only run on main
runs-on: ubuntu-latest
steps:
- name: Only on push (not PR)
if: github.event_name == 'push'
run: echo "deploying"

- name: Only on failure of previous step
if: failure()
run: echo "something went wrong"

Common condition functions:

FunctionWhen it's true
success()All previous steps passed (default)
failure()Any previous step failed
always()Regardless of previous step results
cancelled()Workflow was cancelled

Job Outputsโ€‹

Pass data from one job to another using outputs + needs.

jobs:
build:
runs-on: ubuntu-latest
outputs:
image-tag: ${{ steps.tag.outputs.tag }} # expose step output as job output
steps:
- name: Set image tag
id: tag
run: echo "tag=${{ github.sha }}" >> $GITHUB_OUTPUT

deploy:
needs: build
runs-on: ubuntu-latest
steps:
- name: Use tag from build job
run: echo "Deploying ${{ needs.build.outputs.image-tag }}"

Reusable Workflowsโ€‹

Split common pipelines into shared workflow files called by other workflows.

Caller (/.github/workflows/ci.yml):

jobs:
security:
uses: ./.github/workflows/security-scan.yml # local reusable workflow
with:
environment: production
secrets: inherit # pass all secrets through

Called workflow (/.github/workflows/security-scan.yml):

on:
workflow_call: # makes this workflow callable
inputs:
environment:
required: true
type: string
secrets:
TOKEN:
required: false

jobs:
scan:
runs-on: ubuntu-latest
steps:
- run: echo "Scanning for ${{ inputs.environment }}"