logo
podcast Podcast
get help Get Unstuck

Understand multi-line commands in GitHub Actions

Evaluation may not always be what you expect

Joel Clermont
Joel Clermont
2024-12-12

Yesterday, I shared a tip on a two-stage GitHub Action that was running on the "wrong" branch.

The solution involved looking at a different field in the event payload to get the branch that triggered the workflow_run event, but I didn't tell the whole story.

In that second workflow, we also had a way to manually trigger the deploy workflow, which happens with the workflow_dispatch event.

Because these two events have two different payloads, I needed a more complex if check to decide whether to deploy to staging or production.

Here's what the first version of the if check looked like:

- name: Deploy to staging if develop branch
  if: |
    ${​{
      (github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/develop') ||
      (github.event.name == 'workflow_run' && github.event.workflow_run.head_branch == 'develop')
    }}
  run: some-deploy-command-here

I then had a similar block after this that would deploy to production if the branch was main, with the same multi-line if condition.

But when I tried this out, I noticed that it was always deploying to both staging and production for every run. Clearly something was wrong with my logic, but what was it?

It's a mix of how YAML handles multi-line strings and whitespace, and how GitHub Actions parses expressions.

When you split a string across multiple lines, YAML preserves the line breaks in the resulting string. Normally this wouldn't matter, but we're also using the ${​{ expression }} syntax that GitHub Actions provides.

Because I had a line break inside the expression, it was evaluating that as a string literal, causing my check which I intended to resolve to true or false to actually resolve to a multi-line string 'false\n'.

And GitHub happily considered this multi-line string to be "truthy", so my if check was always passing.

The solution was to avoid using the ${​{ expression }} syntax altogether. In this particular case, GitHub already evaluates the if condition as an expression.

So by eliminating expression evaluation, I was left with a pure boolean check, and now my logic works as expected.

Here to help,

Joel

P.S. As developers, we run into weird problems like this all the time. That's why it's a good thing we don't charge by the hour when we work with clients.

Toss a coin in the jar if you found this helpful.
Want a tip like this in your inbox every weekday? Sign up below 👇🏼
email
No spam. Only real-world advice you can use.