Skip to content

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 returns 1
    false returns 0
    String Parsed from any legal JSON number format, otherwise NaN.
    Note: empty string returns 0.
    Array NaN
    Object NaN
  • A comparison of one NaN to another NaN does not result in true. 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' }}