Find information for expressions in GitHub Actions.
As part of an expression, you can use boolean, null, number, or string data types.
| Data type | Literal value |
|---|---|
boolean |
true or false
|
null |
null |
number |
Any number format supported by JSON. |
string |
You don't need to enclose strings in ${{ and }}. However, if you do, you must use single quotes (') around the string. To use a literal single quote, escape the literal single quote using an additional single quote (''). Wrapping with double quotes (") will throw an error. |
Note that in conditionals, falsy values (false, 0, -0, "", '', null) are coerced to false and truthy (true and other non-falsy values) are coerced to true.
env:
myNull: ${{ null }}
myBoolean: ${{ false }}
myIntegerNumber: ${{ 711 }}
myFloatNumber: ${{ -9.2 }}
myHexNumber: ${{ 0xff }}
myExponentialNumber: ${{ -2.99e-2 }}
myString: Mona the Octocat
myStringInBraces: ${{ 'It''s open source!' }}
| Operator | Description |
|---|---|
( ) |
Logical grouping |
[ ] |
Index |
. |
Property de-reference |
! |
Not |
< |
Less than |
<= |
Less than or equal |
> |
Greater than |
>= |
Greater than or equal |
== |
Equal |
!= |
Not equal |
&& |
And |
|| |
Or |
Note
steps.<step_id>.outputs.<output_name> evaluates as a string. You need to use specific syntax to tell GitHub to evaluate an expression rather than treat it as a string. For more information, see Contexts reference.fromJSON() function can be used to convert a string to a number. For more information on the fromJSON() function, see fromJSON.GitHub performs loose equality comparisons.
If the types do not match, GitHub coerces the type to a number. GitHub casts data types to a number using these conversions:
| Type | Result |
|---|---|
| Null | 0 |
| Boolean |
true returns 1 false returns 0
|
| String | Parsed from any legal JSON number format, otherwise NaN. Note: empty string returns 0. |
| Array | NaN |
| Object | NaN |
When NaN is one of the operands of any relational comparison (>, <, >=, <=), the result is always false. For more information, see the NaN Mozilla docs.
GitHub ignores case when comparing strings.
Objects and arrays are only considered equal when they are the same instance.
GitHub offers a set of built-in functions that you can use in expressions. Some functions cast values to a string to perform comparisons. GitHub casts data types to a string using these conversions:
| Type | Result |
|---|---|
| Null | '' |
| Boolean |
'true' or 'false'
|
| Number | Decimal format, exponential for large numbers |
| Array | Arrays are not converted to a string |
| Object | Objects are not converted to a string |
contains( search, item )
Returns true if search contains item. If search is an array, this function returns true if the item is an element in the array. If search is a string, this function returns true if the item is a substring of search. This function is not case sensitive. Casts values to a string.
contains('Hello world', 'llo') returns true.
contains(github.event.issue.labels.*.name, 'bug') returns true if the issue related to the event has a label "bug".
For more information, see Object filters.
Instead of writing github.event_name == "push" || github.event_name == "pull_request", you can use contains() with fromJSON() to check if an array of strings contains an item.
For example, contains(fromJSON('["push", "pull_request"]'), github.event_name) returns true if github.event_name is "push" or "pull_request".
startsWith( searchString, searchValue )
Returns true when searchString starts with searchValue. This function is not case sensitive. Casts values to a string.
startsWith
startsWith('Hello world', 'He') returns true.
endsWith( searchString, searchValue )
Returns true if searchString ends with searchValue. This function is not case sensitive. Casts values to a string.
endsWith
endsWith('Hello world', 'ld') returns true.
format( string, replaceValue0, replaceValue1, ..., replaceValueN)
Replaces values in the string, with the variable replaceValueN. Variables in the string are specified using the {N} syntax, where N is an integer. You must specify at least one replaceValue and string. There is no maximum for the number of variables (replaceValueN) you can use. Escape curly braces using double braces.
format
format('Hello {0} {1} {2}', 'Mona', 'the', 'Octocat')
Returns 'Hello Mona the Octocat'.
format('{{Hello {0} {1} {2}!}}', 'Mona', 'the', 'Octocat')
Returns '{Hello Mona the Octocat!}'.
join( array, optionalSeparator )
The value for array can be an array or a string. All values in array are concatenated into a string. If you provide optionalSeparator, it is inserted between the concatenated values. Otherwise, the default separator , is used. Casts values to a string.
join
join(github.event.issue.labels.*.name, ', ') may return 'bug, help wanted'
toJSON(value)
Returns a pretty-print JSON representation of value. You can use this function to debug the information provided in contexts.
toJSON
toJSON(job) might return { "status": "success" }
fromJSON(value)
Returns a JSON object or JSON data type for value. You can use this function to provide a JSON object as an evaluated expression or to convert any data type that can be represented in JSON or JavaScript, such as strings, booleans, null values, arrays, and objects.
This workflow sets a JSON matrix in one job, and passes it to the next job using an output and fromJSON.
name: build
on: push
jobs:
job1:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
- id: set-matrix
run: echo "matrix={\"include\":[{\"project\":\"foo\",\"config\":\"Debug\"},{\"project\":\"bar\",\"config\":\"Release\"}]}" >> $GITHUB_OUTPUT
job2:
needs: job1
runs-on: ubuntu-latest
strategy:
matrix: ${{ fromJSON(needs.job1.outputs.matrix) }}
steps:
- run: echo "Matrix - Project ${{ matrix.project }}, Config ${{ matrix.config }}"
This workflow uses fromJSON to convert environment variables from a string to a Boolean or integer.
name: print
on: push
env:
continue: true
time: 3
jobs:
job1:
runs-on: ubuntu-latest
steps:
- continue-on-error: ${{ fromJSON(env.continue) }}
timeout-minutes: ${{ fromJSON(env.time) }}
run: echo ...
The workflow uses the fromJSON() function to convert the environment variable continue from a string to a boolean, allowing it to determine whether to continue-on-error or not. Similarly, it converts the time environment variable from a string to an integer, setting the timeout for the job in minutes.
hashFiles(path)
Returns a single hash for the set of files that matches the path pattern. You can provide a single path pattern or multiple path patterns separated by commas. The path is relative to the GITHUB_WORKSPACE directory and can only include files inside of the GITHUB_WORKSPACE. This function calculates an individual SHA-256 hash for each matched file, and then uses those hashes to calculate a final SHA-256 hash for the set of files. If the path pattern does not match any files, this returns an empty string. For more information about SHA-256, see SHA-2.
You can use pattern matching characters to match file names. Pattern matching for hashFiles follows glob pattern matching and is case-insensitive on Windows. For more information about supported pattern matching characters, see the Patterns section in the @actions/glob documentation.
Matches any package-lock.json file in the repository.
hashFiles('**/package-lock.json')
Matches all .js files in the src directory at root level, but ignores any subdirectories of src.
hashFiles('/src/*.js')
Matches all .rb files in the lib directory at root level, including any subdirectories of lib.
hashFiles('/lib/**/*.rb')
Creates a hash for any package-lock.json and Gemfile.lock files in the repository.
hashFiles('**/package-lock.json', '**/Gemfile.lock')
Creates a hash for all .rb files in the lib directory at root level, including any subdirectories of lib, but excluding .rb files in the foo subdirectory.
hashFiles('/lib/**/*.rb', '!/lib/foo/*.rb')
case( pred1, val1, pred2, val2, ..., default )
Evaluates predicates in order and returns the value corresponding to the first predicate that evaluates to true. If no predicate matches, it returns the last argument as the default value.
env:
MY_ENV_VAR: ${{ case(github.ref == 'refs/heads/main', 'production', 'development') }}
Sets MY_ENV_VAR to production when the ref is refs/heads/main, otherwise sets it to development.
env:
MY_ENV_VAR: |-
${{ case(
github.ref == 'refs/heads/main', 'production',
github.ref == 'refs/heads/staging', 'staging',
startsWith(github.ref, 'refs/heads/feature/'), 'development',
'unknown'
) }}
Sets MY_ENV_VAR based on the branch: production for main, staging for staging, development for branches starting with feature/, or unknown for all other branches.
You can use the following status check functions as expressions in if conditionals. A default status check of success() is applied unless you include one of these functions. For more information about if conditionals, see Workflow syntax for GitHub Actions and Metadata syntax reference.
Outside if conditionals, you can use job.status to access the job status. For more information, see Contexts reference.
Returns true when all previous steps have succeeded.
success
steps:
...
- name: The job has succeeded
if: ${{ success() }}
Causes the step to always execute, and returns true, even when canceled. The always expression is best used at the step level or on tasks that you expect to run even when a job is canceled. For example, you can use always to send logs even when a job is canceled.
Warning
Avoid using always for any task that could suffer from a critical failure, for example: getting sources, otherwise the workflow may hang until it times out. If you want to run a job or step regardless of its success or failure, use the recommended alternative: if: ${{ !cancelled() }}
always
if: ${{ always() }}
Returns true if the workflow was canceled.
cancelled
if: ${{ cancelled() }}
Returns true when any previous step of a job fails. If you have a chain of dependent jobs, failure() returns true if any ancestor job fails.
failure
steps:
...
- name: The job has failed
if: ${{ failure() }}
You can include extra conditions for a step to run after a failure, but you must still include failure() to override the default status check of success() that is automatically applied to if conditions that don't contain a status check function.
failure with conditionssteps:
...
- name: Failing step
id: demo
run: exit 1
- name: The demo step has failed
if: ${{ failure() && steps.demo.conclusion == 'failure' }}
You can use the * syntax to apply a filter and select matching items in a collection.
For example, consider an array of objects named fruits.
[
{ "name": "apple", "quantity": 1 },
{ "name": "orange", "quantity": 2 },
{ "name": "pear", "quantity": 1 }
]
The filter fruits.*.name returns the array [ "apple", "orange", "pear" ].
You may also use the * syntax on an object. For example, suppose you have an object named vegetables.
{
"scallions":
{
"colors": ["green", "white", "red"],
"ediblePortions": ["roots", "stalks"],
},
"beets":
{
"colors": ["purple", "red", "gold", "white", "pink"],
"ediblePortions": ["roots", "stems", "leaves"],
},
"artichokes":
{
"colors": ["green", "purple", "red", "black"],
"ediblePortions": ["hearts", "stems", "leaves"],
},
}
The filter vegetables.*.ediblePortions could evaluate to:
[
["roots", "stalks"],
["hearts", "stems", "leaves"],
["roots", "stems", "leaves"],
]
Since objects don't preserve order, the order of the output cannot be guaranteed.