Writing a good condition in GitHub workflows

Juraj SimonJuraj Simon
3 min read

Conditions are used to control job or step execution and are defined by if YAML key like this:

jobs:
  first-job:
    if: ${{ github.event_name == 'workflow_dispatch' }} #<---- conditions
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

There are a few specifics that might not be obvious at first.

For example, I was used to always writing those opening and closing ${{ / }} without realizing I don’t really need them in most cases.

From the documentation mentioned above:

When you use expressions in an if conditional, you can, optionally, omit the ${{ }} expression syntax because GitHub Actions automatically evaluates the if conditional as an expression. However, this exception does not apply everywhere.

You must always use the ${{ }} expression syntax or escape with '', "", or () when the expression starts with !, since ! is reserved notation in YAML format.

That means this won’t work:

- name: worng syntax condition
  if: !startsWith(inputs.first_name, 'X') #<--- this won't work
  shell: bash
  run: |
    echo 'wrong syntax'

The YAML parser will start complaining, so you won't even be able to start the workflow.

To get around it, you have to use ${{ / }}, or escape it just like the documentation says.

# enclosed in parenthesis
- name: ok condition
  if: (!startsWith(inputs.first_name, 'X')) #<--- ok now!
  shell: bash
  run: |
    echo 'ok now'
# escaped with double quotes " "
- name: ok condition
  if: "!startsWith(inputs.first_name, 'X')" #<--- ok now!
  shell: bash
  run: |
    echo 'ok now'
# escaped with single quotes ' '
- name: ok condition
  if: '!startsWith(inputs.first_name, ''X'')' #<--- ok now!
# any exiting quotes have to be escaped ↑ ↑ with one extra ' 
  shell: bash
  run: |
    echo 'ok now'

Another annoying thing is how large some conditions can grow in complex workflows. They can easily be more than 80 characters long and hard to read:

- name: long condition
  if: fromJSON(steps.vars.outputs.custom_json).key == 'value' && (inputs.first_name == 'John' || inputs.last_name == 'Doe') && github.event_name == 'workflow_dispatch'
  shell: bash
  run: |
    echo 'multiline condition' >> $GITHUB_STEP_SUMMARY

One neat trick to keep conditions readable (and rows short) is to switch to multiline YAML strings:

- name: multiline condition
  if: |
    fromJSON(steps.vars.outputs.custom_json).key == 'value' && 
    (
      inputs.first_name == 'John' ||
      inputs.last_name == 'Doe'
    ) &&
    github.event_name == 'workflow_dispatch'
  shell: bash
  run: |
    echo ''
    echo 'multiline condition' >> $GITHUB_STEP_SUMMARY
💡
Now you can freely structure (indentation, parenthesis positions etc.) the condition as you want. One downside is that there is no formatter to do this automatically. 😥

One thing to watch for here is that you can’t start any single condition with ! as you might be used to from other programming languages. This is not a valid condition, for example:

- name: ivalid multiline condition
  if: |
    inputs.first_name == 'John' &&
    !inputs.last_name == 'Doe'

But this works fine:

- name: ivalid multiline condition
  if: |
    inputs.first_name == 'John' &&
    !(inputs.last_name == 'Doe')

Conclusion

Conditions in GitHub workflows and/or actions can easily become long and complex. Luckily, it is easy to format them as you wish using multiline YAML strings with | indicator.

That is all for now. Thank you. 👋

Happy coding :)

0
Subscribe to my newsletter

Read articles from Juraj Simon directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Juraj Simon
Juraj Simon

DevOps/FullStack node.js developer. Passionate about automation and efficiency. Learning how to write... :)