I am trying to build a trivial workflow using GitHub Actions consisting of the following jobs:
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)
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
When is_success
is set like this:
is_success: |
${{
steps.failing-step.outcome == 'success' &&
steps.build-dummy-application.outcome == 'success' &&
steps.deploy-dummy-application.outcome == 'success'
}}
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:
is_success: |-
${{ ... }}
Better yet, to avoid scenarios where intermediate newlines may also cause such issues, prefer using fold with strip i.e. >-
instead:
is_success: >-
${{ ... }}
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:
status: ${{ needs.Dummy_Application_Deployment.outputs.is_success == 'true' && 'OK' || 'NOK' }}
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:
'true\n' == 'true' (false)
For scenario B:
status: ${{ needs.Dummy_Application_Deployment.outputs.is_success && 'OK' || 'NOK' }}
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'
).