github-actions

Ternary Operator evaluating wrongly?


I am trying to build a trivial workflow using GitHub Actions consisting of the following jobs:

  1. Infrastructure Provisioning
  2. Application Deployment
  3. 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)

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

Solution

  • 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').