I am trying to build a trivial workflow using GitHub Actions consisting of the following jobs:
- Infrastructure Provisioning
- Application Deployment
- Status Callback
Thereby, I am using a ternary operator to determine whether ‘OK’ or ‘NOK’ is passed as an input variable to the Status Callback job.
(offical documentation: https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/evaluate-expressions-in-workflows-and-actions#example)
However, my ternary operator seems to be behaving weirdly based on the following scenarios I have tested.
The code underlying my investigations/scenarios is always the same apart from the ternary operator determining the status:
Status-Callback-Generic-A:
# needs: [Infrastructure_Provisioning, Dummy_Application_Deployment]
needs: [Dummy_Application_Deployment]
if: ${{ always() }}
uses: ./.github/workflows/status-callback.yml
with:
connectionId: ${{ inputs.connectionId }}
status: ${{ needs.Dummy_Application_Deployment.outputs.is_success == 'true' && 'OK' || 'NOK' }}
Scenario A
status: ${{ needs.Dummy_Application_Deployment.outputs.is_success == 'true' && 'OK' || 'NOK' }}
Scenario B
status: ${{ needs.Dummy_Application_Deployment.outputs.is_success && 'OK' || 'NOK' }}
Scenario C
status: ${{ needs.Dummy_Application_Deployment.outputs.is_success == true && 'OK' || 'NOK' }}
Scenario D
status: ${{ needs.Dummy_Application_Deployment.outputs.is_success == 1 && 'OK' || 'NOK' }}
Scenario E
status: ${{ needs.Dummy_Application_Deployment.outputs.is_success == '1' && 'OK' || 'NOK' }}
Below table depicts the results from above scenario-specific code snippets. In summary: No single scenario seems to work properly.
is_success | expected status | status(A) | status(B) | status(C) | status(D) | status(E) |
---|---|---|---|---|---|---|
true | OK | NOK | OK | NOK | NOK | NOK |
false | NOK | NOK | OK | NOK | NOK | NOK |
Notes from official documentation:
(https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/evaluate-expressions-in-workflows-and-actions#operators)
- job outputs evaluate as strings
- GitHub transforms non-matching types to numbers. In case of Boolean:
- true returns 1
- false returns 0
Am I doing something fundamentally wrong with my conditionals?
The is_success is based on the steps in the respective workflow
name: Dummy Application Deployment
on:
workflow_call:
outputs:
is_success:
description: Dummy Description
value: ${{ jobs.build_and_deploy_dummy_application.outputs.is_success }}
jobs:
build_and_deploy_dummy_application:
runs-on: ubuntu-latest
outputs:
is_success: |
${{
steps.failing-step.outcome == 'success' &&
steps.build-dummy-application.outcome == 'success' &&
steps.deploy-dummy-application.outcome == 'success'
}}
steps:
# omitted
2
Answers
Excuse my belated response! Most likely I would never have figured this out on my own... (one reason more to not like YAML ;))
Learning: Do expression evaluations in a single line ;)
For everyone interested please find below the action files highlighting the issue
main.yml
job-1.yml
status-callback.yml
When
is_success
is set like this:using YAML’s literal style indicator i.e.
|
, there is always a trailing newline at the end which makes it a multiline output.Adding the strip block chomping indicator i.e.
-
should fix this:Better yet, to avoid scenarios where intermediate newlines may also cause such issues, prefer using fold with strip i.e.
>-
instead:You may experiment with https://yaml-multiline.info/ for different use cases of multiline strings.
With this fix, the comparison against string literal
'true'
or'false'
should work as expected:Given above, you may dry run all your scenarios to figure out why some comparisons work and others won’t. The comparison was being performed against a multiline string:
For scenario B:
needs.Dummy_Application_Deployment.outputs.is_success
is only being evaluated for a null or not-null value. It’s populated so it’s a not-null value that’s why it’s evaluated to true ('OK'
).