GitHub Actions Tutorial — Expression Syntax
Expression Syntax
Expressions are written inside ${{ }} and can appear in most workflow YAML fields. They evaluate to a value — a string, number, boolean, or null.
Where Expressions Can Be Used
jobs:
example:
runs-on: ubuntu-latest
if: ${{ github.ref == 'refs/heads/main' }} # job conditional
env:
TAG: ${{ github.sha }} # environment variable
steps:
- run: echo "${{ github.actor }}" # inline in run
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }} # action input
Note: In if: conditions, the ${{ }} wrapper is optional — GitHub evaluates the expression automatically:
# Both are equivalent
if: ${{ github.ref == 'refs/heads/main' }}
if: github.ref == 'refs/heads/main'
Literals
| Type | Examples |
|---|---|
| Boolean | true, false |
| Null | null |
| Number | 42, 3.14, -1 |
| String | 'hello' (single quotes only) |
Strings must use single quotes inside expressions:
if: github.event_name == 'push' # ✅ correct
if: github.event_name == "push" # ❌ won't work
Operators
Comparison Operators
| Operator | Meaning | Example |
|---|---|---|
== |
Equal | github.ref == 'refs/heads/main' |
!= |
Not equal | github.actor != 'dependabot[bot]' |
< |
Less than | github.run_number < 100 |
> |
Greater than | steps.test.outputs.count > 0 |
<= |
Less or equal | github.run_attempt <= 3 |
>= |
Greater or equal | needs.build.outputs.version >= '2.0' |
Logical Operators
| Operator | Meaning | Example |
|---|---|---|
&& |
AND | github.ref == 'refs/heads/main' && github.event_name == 'push' |
\|\| |
OR | github.event_name == 'push' \|\| github.event_name == 'workflow_dispatch' |
! |
NOT | !github.event.pull_request.draft |
Grouping
if: (github.event_name == 'push' && github.ref == 'refs/heads/main') || github.event_name == 'workflow_dispatch'
Type Coercion
GitHub Actions automatically converts types in comparisons:
| Comparison | Result |
|---|---|
null == '' |
true |
null == 0 |
true |
null == false |
true |
'' == 0 |
true |
'true' == true |
true (for if: specifically) |
'0' == false |
true |
This can cause surprises. Be explicit when checking for empty strings vs null:
# Checking if output is set (non-empty)
if: steps.check.outputs.value != ''
# Checking if output is truly empty
if: steps.check.outputs.value == ''
Property Access
Access nested properties with dot notation or bracket notation:
# Dot notation
${{ github.event.pull_request.head.ref }}
# Bracket notation (required for special characters)
${{ github.event['pull_request']['head']['ref'] }}
# Dynamic property access
${{ needs[env.JOB_NAME].outputs.result }}
Common Mistakes
| Mistake | Fix |
|---|---|
| Double quotes in expressions | Use single quotes: 'value' not "value" |
Missing ${{ }} in env: |
Required in env: and with:, optional in if: |
| Type coercion surprises | null == '' is true — be explicit in checks |
| Comparing numbers as strings | Outputs are always strings; use fromJSON() to convert |
Quick Reference
| Category | Syntax |
|---|---|
| Expression wrapper | ${{ expression }} |
| String literal | 'single quotes' |
| Comparison | ==, !=, <, >, <=, >= |
| Logical | &&, \|\|, ! |
| Property access | object.property or object['property'] |
| Implicit in if: | ${{ }} is optional in if: |
← Back to: Chapter 7: Expressions
Next: Contexts →