Computer scienceSystem administration and DevOpsCI/CD processesGitHub Actions

Expressions

10 minutes read

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.

The ${{ }} 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: true or false.
  • 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.

Using double quotes " 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.

GitHub actions ignores case when comparing strings. For example, 'Hello' and 'hello' are considered equal.

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.

6 learners liked this piece of theory. 0 didn't like it. What about you?
Report a typo