Expressions¶
You can evaluate expressions in workflows and hooks.
About expressions¶
You can use expressions to programmatically set variables in workflow files and access contexts. An expression can be any combination of literal values, references to a context, or functions. You can combine literals, context references, and functions using operators. For more information about contexts, see “Contexts.”
Expressions are commonly used with the conditional if
keyword in a workflow file to
determine whether a step should run. When an if
conditional is true
, the step will run.
You need to use specific syntax to tell the OpenTestFactory orchestrator to evaluate an expression rather than treat it as a string.
${{ <expression> }}
When you use expressions in an if
conditional, you may omit the expression syntax
(${{ }}
) because the orchestrator automatically evaluates the if
conditional as an
expression. However, this rule does not apply everywhere.
You must use the ${{ }}
expression syntax or escape with ''
, ""
, or ()
when the
expression starts with !
, since !
is reserved notation in YAML format.
Using the ${{ }}
expression syntax turns the contents into a string, and strings are truthy.
For example, if: true && ${{ false }}
will evaluate to true
. For more information about
if
conditionals, see “Workflow syntax for OpenTestFactory orchestrator.”
Warning
When creating workflows and provider tasks, you should always consider whether your code might execute untrusted input from possible attackers. Certain contexts should be treated as untrusted input, as an attacker could insert their own malicious content. For more information, see “Understanding the risk of script injections.”
Example expression in an if
conditional
steps:
- run: dir /s
if: runner.os == 'windows' # or: ${{ runner.os == 'windows' }}
Example setting an environment variable
variables:
my_env_var: ${{ <expression> }}
Literals¶
As part of an expression, you can use boolean
, null
, number
, or string
data types.
Boolean literals are not case-sensitive, so you can use true or True.
Data type | Literal value |
---|---|
boolean |
true or false |
null |
null |
number |
Any number format supported by JSON. |
string |
You must use single quotes. Escape literal single-quotes with a single quote. |
Example
variables:
myNull: ${{ null }}
myBoolean: ${{ false }}
myIntegerNumber: ${{ 711 }}
myFloatNumber: ${{ -9.2 }}
myHexNumber: ${{ 0xff }}
myExponentialNumber: ${{ -2.99-e2 }}
myString: ${{ 'Mona the Octocat' }}
myEscapedString: ${{ 'It''s open source!' }}
Operators¶
Operator | Description | Usage |
---|---|---|
( ) |
Logical grouping | ((foo == 4) || (bar == 5)) && (baz == 6) |
[ ] |
Index | variables['VAR'] |
. |
Property dereference | variables.VAR |
! |
Not | !failure() |
< |
Less than | 12 < 4 |
<= |
Less than or equal | 12 <= 4 |
> |
Greater than | 12 > 4 |
>= |
Greater than or equal | 12 >= 4 |
== |
Equal | variables.VAR == 'foo' |
!= |
Not equal | 12 != 4 |
~= |
Match regular expression | variables.VAR ~= '^abc.*' |
&& |
And | true && false |
|| |
Or | true || false |
The orchestrator performs loose equality comparisons.
-
If the types do not match, the orchestrator coerces the type to a number. The orchestrator casts data types to a number using these conversions:
Type Result Null 0
Boolean true
returns1
false
returns0
String Parsed from any legal JSON number format, otherwise NaN
.
Note: empty string returns0
.Array NaN
Object NaN
-
A comparison of one
NaN
to anotherNaN
does not result intrue
. For more information, see the “NaN Mozilla docs.” -
The orchestrator ignores the case when comparing strings.
-
For more information on the regular expression syntax, see “Python
re
module.” -
Objects and arrays are only considered equal when they are the same instance.
-
Expression evaluation is left-associative, and comparison operators have precedence over logical operators.
foo == 4 || bar == 5
is evaluated as(foo == 4) || (bar == 5)
.
Ternary operator-like behavior¶
The orchestrator offers ternary operator-like behavior that you can use in expressions. By using a ternary operator in this way, you can dynamically set the value of an environment variable based on a condition, without having to write separate if-else blocks for each possible option.
Example
variables:
MY_ENV_VAR: ${{ variables.GIT_REF == 'refs/heads/main' && 'value_for_main_branch' || 'value_for_other_branches' }}
In this example, we’re using a ternary operator to set the value of the MY_ENV_VAR
environment variable based on whether the Git reference is set to refs/heads/main
or not. If it is, the variable is set to value_for_main_branch
. Otherwise, it is
set to value_for_other_branches
. It is important to note that the first value
after the &&
condition must be truthy otherwise the value after the ||
will
always be returned.
Functions¶
The orchestrator offers a set of built-in functions that you can use in expressions. Some functions cast values to a string to perform comparisons. The orchestrator 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¶
contains( search, item )
Returns true
if search
contains item
. If search
is an array or an object, 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.
Example using a string
contains('Hello world', 'llo')
returns true
.
Example using an object
contains(variables, 'bug')
returns true
if a variable “bug” exists.
Example matching an array of strings
Instead of writing variables.event_name == 'push' || variables.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"]'), variables.event_name)
returns
true
if variables.event_name
is “push” or “pull_request”.
toJSON¶
toJSON( value )
Returns a pretty-print JSON representation of value
. You can use this function to debug the
information provided in contexts.
Example of toJSON
toJSON(job)
might return { "status": "success" }
.
fromJSON¶
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 environment variables from a string.
Example returning a JSON data type
This workflow uses fromJSON
to convert environment variables from a string to a Boolean or
integer.
variables:
continue: true
time: 3
jobs:
job1:
runs-on: linux
steps:
- continue-on-error: ${{ fromJSON(variables.continue) }}
timeout-minutes: ${{ fromJSON(variables.time) }}
run: echo ...
startsWith¶
startsWith( searchString, searchValue )
Returns true
when searchString
starts with searchValue
. This function is not
case-sensitive. Casts values to a string.
Example of startsWith
startsWith('Hello world', 'He')
returns true
.
endsWith¶
endsWith( searchString, searchValue )
Returns true if searchString
ends with searchValue
. This function is not case-sensitive.
Casts values to a string.
Example of endsWith
endsWith('Hello world', 'ld')
returns true
.
Date functions¶
Timestamps are strings that follow the ISO 8601 standard.
2011-11-04
20111104
2011-11-04T00:05:23
2011-11-04T00:05:23Z
20111104T000523
2011-W01-2T00:05:23.283
2011-11-04 00:05:23.283
year¶
year( timestamp )
Returns an integer or an empty string if the timestamp is invalid.
Examples of extracting the year of a timestamp
year('2023-06-30T12:34:56.789')
returns 2023
.
year(workflow.creationTimestamp)
returns 2023
(maybe).
month¶
month( timestamp )
Returns the month number (an integer from 1
to 12
) or an empty string if the timestamp is invalid.
Example of extracting the month of a timestamp
month('2023-06-30T12:34:56.789')
returns 6
.
day¶
day( timestamp )
Returns the day in the month (an integer from 1
to 31
) or an empty string if the timestamp
is invalid.
Example of extracting the day of a timestamp
day('2023-06-30T12:34:56.789')
returns 30
.
dayOfWeek¶
dayOfWeek( timestamp )
Returns the weekday (a string, from Monday
to Sunday
) or an empty string if the timestamp
is invalid.
Example using dayOfWeek
dayOfWeek('2023-06-30T12:34:56.789')
returns Friday
.
dayOfWeekISO¶
dayOfWeekISO( timestamp )
Returns the weekday number (an integer from 1
to 7
, 1
being Monday) or an empty string
if the timestamp is invalid.
Example using dayOfWeekISO
dayOfWeekISO('2023-06-30T12:34:56.789')
returns 5
.
hour¶
hour( timestamp )
Returns the hour (an integer from 0
to 23
) or an empty string if the timestamp is invalid.
If the timestamp only specifies a date, returns 0
.
Examples of extracting the hour of a timestamp
hour('2023-06-30T12:34:56.789')
returns 12
.
hour('2023-06-30')
returns 0
.
minute¶
minute( timestamp )
Returns the minutes (an integer from 0
to 23
) or an empty string if the timestamp is invalid.
If the timestamp only specifies a date, returns 0
.
Examples of extracting the minute of a timestamp
minute('2023-06-30T12:34:56.789')
returns 34
.
minute('2023-06-30')
returns 0
.
second¶
second( timestamp )
Returns the seconds (an integer from 0
to 59
) or an empty string if the timestamp is invalid.
If the timestamp only specifies a date, returns 0
.
Examples using second
second('2023-06-30T12:34:56.789')
returns 56
. second('not a timestamp')
returns ''
.
Status check functions¶
You can use the following status check functions as expressions in if
conditionals. If
your if
expression does not contain any of the status functions it will automatically
result in success()
. For more information about if
conditionals, see
“Workflow syntax for OpenTestFactory orchestrator.”
success¶
Returns true
when all previous steps have succeeded.
Example of `success`
steps:
# ...
- name: The job has succeeded
if: ${{ success() }}
always¶
Causes the step or job 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 hand 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() }}
.
Example of `always`
if: ${{ always() }}
cancelled¶
Returns true
if the workflow was canceled.
Example of `cancelled`
if: ${{ cancelled() }}
failure¶
Returns true
when any previous step of a job fails. If you have a chain of dependent jobs,
failure()
return true
if any ancestor job fails.
Example of `failure`
steps:
# ...
- name: The job has failed
if: ${{ failure() }}
failure with conditions
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 the if
conditions that don’t contain a status check function.
steps:
# ...
- name: Failing step
id: demo
run: exit 1
- name: The demo step has failed
if: ${{ failure() && steps.demo.conclusion == 'failure' }}