GitHub Actions introduces expressions to evaluate data in workflows and actions. Expressions can manipulate data, make decisions based on conditions, and control the behavior of workflows dynamically. This topic will cover the syntax, literals, operators, and functions used in expressions, enabling you to effectively use expressions in your GitHub Actions workflows.
Syntax of expressions
In GitHub Actions, expressions are wrapped in ${{ }} to tell GitHub to evaluate the content as an expression rather than treating it as a string. This syntax allows you to use expressions in various parts of your workflow file. For example: in the if conditional, env for setting environment variables, and more.
Here's an example of how to use expressions in a workflow file:
name: Expression Syntax
on: push
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Set environment variable using expression
run: echo "Today is ${{ github.event.push.timestamp }}"
env:
MY_ENV_VAR: ${{ github.event.push.timestamp }}
In this example, the expression ${{ github.event.push.timestamp }} is evaluated and the result is used to set the value of the environment variable MY_ENV_VAR. The same expression is also used within the echo command in the run field.
${{ }} syntax is crucial for GitHub to recognize and evaluate the content as an expression. Without it, GitHub treats the content as a plain string.Literals
In GitHub Actions expressions, literals represent fixed values that are directly written in the expression. They are the most basic building blocks of expressions and can be of various types, such as string, number, boolean, and null.
- Boolean: This type can have two values:
trueorfalse. - Null: This type has only one value:
null. - Number: This can be any number format supported by JSON, including integer, float, hexadecimal, and exponential numbers.
- String: This is a sequence of characters. Strings must be enclosed in single quotes
'. If a string contains a single quote, use two single quotes as an escape sequence to represent it.
Here is a workflow file that demonstrates literal usage:
name: Literals
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Run expressions
run: |
echo "Event name: ${{ github.event_name }}" # String literal
echo "Is it a push event? ${{ github.event_name == 'push' }}" # Boolean literal
echo "Quote: ${{ 'It''s a sunny day' }}" # String literal with a single quote
echo "Number literal: ${{ 12345 }}"
echo "Is OPTIONAL_ENV_VAR null?: ${{ env.OPTIONAL_ENV_VAR == null }}"
env:
OPTIONAL_ENV_VAR: 'Test' # String literal
This example has a single job with one step that runs several echo commands. Each command prints the result of an expression. The expressions include string literals, a boolean literal resulting from a comparison, and a null literal.
" inside ${{ }} causes an error. Operators
In GitHub Actions expressions, operators are symbols that perform operations on one or more operands (values). We use operators to manipulate values and variables in expressions and make decisions based on certain conditions.
There are several types of operators:
- Logical grouping
( ): Changes the precedence of evaluation in expressions - Index
[ ]: Used to define an index - Property de-reference
.: Accesses a property of an object - Not
!: Reverses the boolean value of its operand - Comparison operators: This includes less than
<, less than or equal to<=, greater than>, greater than or equal to>=, equal to==, and not equal to!=. They compare two values and return a boolean result. - Logical operators: The AND operator
&&returns true if both operands are true. The OR operator||returns true if either or both operands are true.
Here's an example of how to use different types of operators in a workflow file:
name: Operators
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Run expressions
id: step1
run: |
echo "Repository owner: ${{ github.repository_owner }}" # Property de-reference
echo "myOutput=Hello, World" >> $GITHUB_OUTPUT
echo "Is it a push event? ${{ github.event_name == 'push' }}" # Equal
echo "Is it not a push event? ${{ !(github.event_name == 'push') }}" # Not
echo "Push by 'octocat'? ${{ (github.event_name == 'push') && (github.repository_owner == 'octocat') }}" # Logical
- name: Use output
run: |
echo "Previous step output: ${{ steps['step1'].outputs.myOutput }}" # index
This example uses different types of operators within its expressions. The ${{ }} syntax tells GitHub to evaluate the operators as part of expressions.
In addition to these standard operators, expressions in GitHub Actions also support a form of the ternary operator. It provides a compact way to handle conditional assignments. This allows you to set the value of a variable based on a condition.
name: Ternary operator
on:
push:
branches:
- master
- development
env:
BUILD_TYPE: ${{ github.ref == 'refs/heads/main' && 'production' || 'development' }}
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Print build type
run: echo "$BUILD_TYPE"
In this example, the only step is to print the value of the BUILD_TYPE environment variable, which is set based on the branch being built. If the branch is main, BUILD_TYPE is set to 'production'. Otherwise, it is set to 'development'.
Functions
GitHub Actions expressions provide a set of built-in functions that you can use to manipulate and evaluate data within your workflows. These functions allow you to perform common operations on strings, numbers, arrays, and other data types. Let's explore four specific functions: format, join, toJSON, and fromJSON.
format: The format function allows you to create a formatted string by replacing placeholders in a template string with specified values. It is useful when constructing messages or strings that include variable content.
The syntax for the format function is as follows: ${{ format('<template string>', <arguments>) }
Here's an example that demonstrates how you can use the format function:
name: Format
on: push
jobs:
example:
runs-on: ubuntu-latest
steps:
- name: Print formatted message
run: echo "${{ format('Hello, {0}!', 'World') }}"
This will print Hello, World! to the console.
join: The join function concatenates the elements of an array using the specified separator. This is particularly handy when you have a list of items that you want to present as a single string.
The syntax for the join function is: ${{ join(<array>,'<separator>') }
Let's look at an example in the context of GitHub Actions, assuming you want to work with the labels associated with an issue. This example demonstrates how one may use the join function to concatenate the names of all labels on an issue:
name: Label Handler
on:
issues:
types: [labeled]
jobs:
example:
runs-on: ubuntu-latest
steps:
- name: Print labels
run: echo "${{ join(github.event.issue.labels.*.name, ', ') }}"
Here, github.event.issue.labels.*.name uses the * wildcard to extract an array of label names from an issue. For example, with labels "bug" and "help wanted", the expression join(github.event.issue.labels.*.name, ', ') returns the string 'bug, help wanted'.
toJSON: The toJSON function converts a value to its JSON string representation. It is useful when you need to serialize data into a format that can be easily stored or transmitted.
The syntax for the toJSON function is: ${{ toJSON(<value>)
Here's an example that demonstrates how to use the toJSON function to convert the job object into a JSON string, focusing on the status:
name: Job Status Reporter
on: push
jobs:
example:
runs-on: ubuntu-latest
steps:
- name: Print job status
run: echo "${{ toJSON(job) }}"
In this example, the toJSON(job) expression in GitHub Actions converts the current job's context into a JSON string. If the job's status is "success", toJSON(job) will yield '{ "status": "success" }'.
fromJSON: By default, the expression steps.<step_id>.outputs.<output_name>, which is used to access the output of a specific step within a job in GitHub Actions, is evaluated as a string. This holds true even when the output value is a different type, such as a number or boolean. The fromJSON function parses a JSON string and returns the corresponding value. It is the counterpart to toJSON and allows you to deserialize data from a JSON string.
The syntax for the fromJSON function is: ${{ fromJSON('<json string>') }
Here's an example that demonstrates how to use the fromJSON function to evaluate a success flag within a job:
name: fromJson
on: push
jobs:
example:
runs-on: ubuntu-latest
steps:
- id: flag
run: echo "is_successful=true" >> $GITHUB_ENV
- name: Do something if successful
run: echo "It was successful!"
if: ${{ fromJSON(env.is_successful) }}
In the example, the step with id: flag sets an environment variable is_successful to the string "true". Then, the fromJSON function is used in the if condition of the next step to parse the string "true" into the boolean value true. This allows the step to run if is_successful is true.
Conclusion
In conclusion, GitHub Actions expressions offer a versatile toolkit for manipulating and evaluating data within workflows. Through the use of literals, operators, and built-in functions like format, join, toJSON, and fromJSON, developers can create dynamic and responsive automation processes. Understanding these elements empowers users to craft more efficient and adaptable workflows that enhance the automation capabilities within GitHub.