Skip to content

Boolean inputs are not actually booleans in composite actions #2238

Open

Description

@flobernd

Describe the bug

I'm not sure if closed issues are monitored, as there was no reaction from official side at all. This is a new issue related to #1483.

For composite actions boolean inputs are not actually booleans.

To Reproduce

inputs:
  ...
  generate-release-notes:
    description: ...
    required: false
    type: boolean
    default: false

runs:
  using: composite
  steps:
    - name: Create Release
      uses: actions/github-script@v6
      with:
        script: |
          github.rest.repos.createRelease({
            owner: context.repo.owner,
            repo: context.repo.repo,
            ...
            generate_release_notes: ${{ inputs.generate-release-notes && 'true' || 'false' }}
          });

Caller:

- uses: flobernd/actions/github/create-release@master
  with:
    tag-name: v1.2.4
    generate-release-notes: true

This line always evaluates to false:

generate_release_notes: ${{ inputs.generate-release-notes && 'true' || 'false' }}

The explicit syntax does incorrectly evaluate to false as well:

generate_release_notes: ${{ inputs.generate-release-notes == true && 'true' || 'false' }}

Correct behavior is only observed when using string semantics:

generate_release_notes: ${{ inputs.generate-release-notes == 'true' && 'true' || 'false' }}

Expected behavior

generate_release_notes: ${{ inputs.generate-release-notes && 'true' || 'false' }}
generate_release_notes: ${{ inputs.generate-release-notes == true && 'true' || 'false' }}

Evaluates to true.

Runner Version and Platform

GitHub managed runners (latest version). All platforms.

Activity

  1. ChristopherHX commented on Nov 2, 2022

    @ChristopherHX
    Contributor

    Would be nice to have non string inputs in composite actions, especially the object type would be great in my opinion

    inputs:
      ...
      myobjinput:
        description: ...
        required: false
        type: object
    
    runs:
      using: composite
      steps:
      - run: |
          echo '${{ tojson(inputs.myobjinput) }}'

    Fun fact this composite action will also use the type string for myobjinput, because the type property is not parsed / supported in composite actions yet. Just the parser is instructed to be relaxed to not throw an error if you invent your own properties, I think you can add anything under an input yaml mapping.

    Action Inputs are still saved in a Dictionary<string, string>, which cannot even store a true boolean, I'm not affiliated with GitHub, only a contributor of one bug fix.

  2. ssallmen-pro commented on Nov 14, 2022

    @ssallmen-pro

    I bumped on this also today. I am calling my composite action from a workflow with a boolean input as follows:

    ...
    on:
      workflow_dispatch:
        inputs:
          realRun:
            description: Really run?
            default: false
            type: boolean
    ...
    jobs:
      run-tests:
          - uses: my-org/test-run@main
            with:
              realRun: ${{ inputs.realRun == true }}

    And this is how my composite action my-org/test-run@main looks like from some relevant parts (some debugging added and a lot of things omitted here):

    ...
    inputs:
      realRun:
        description: Really run?
        default: false
        type: boolean
    ...
    runs:
      using: composite
      steps:
        - name: Check realRun input value
          shell: bash
          run: echo "realRun==${{ inputs.realRun == true }}"
    
        - name: Run tests
          if: ${{ inputs.realRun == true }}
          shell: bash
          run: ${{ github.action_path }}/bin/run_tests.sh

    That will always echo realRun==false on the first step and will never run the second step, independent on whether the input value is given as boolean true or false. That is because it seems the realRun input value is treated as string in the composite action.
    I know I can get it working by using string 'true' in the expressions above, instead of boolean true, but it will work only as long as this bug with treating boolean inputs as strings in a composite action has been fixed.

  3. veleek commented on Nov 17, 2022

    @veleek

    I was hoping to do something similiar with the output of a composite action, but there doesn't seem to be any way to force it to be a boolean either. I'm using:

    outputs:
      image_exists:
        description: true if an image with the given exists, false otherwise
        value: ${{ fromJSON(steps.verify_image_exists.outputs.image_exists) }}
    

    This shouldn't be surprising given that it is documented but it's still frustrating and you end up with the same "always evaluates to false" problem.

  4. added
    keepLabel can be added as soon as we are sure the work on the issue is necessary
    triagedNeeds extra internal investigation before adding ready-for-dev
    on Nov 23, 2022
  5. machulav commented on Jan 18, 2023

    @machulav

    Agree with @veleek - this is very frustrating to have conditions like the following:

    if: needs.get-pr-labels.outputs.build-label-present == 'true' || needs.get-pr-labels.outputs.deploy-label-present == 'true'
    

    It would be nicer if the outputs were boolean, so the condition looks like this:

    if: needs.get-pr-labels.outputs.build-label-present || needs.get-pr-labels.outputs.deploy-label-present
    

    The outputs in the composite actions look like this:

    on:
      workflow_call:
        outputs:
          build-label-present:
            description: "cicd:build label is present in the PR"
            value: ${{ contains(github.event.pull_request.labels.*.name, 'cicd:build') }}
          deploy-label-present:
            description: "cicd:deploy:* label is present in the PR"
            value: ${{ contains(toJSON(github.event.pull_request.labels.*.name), 'cicd:deploy:') }}
    
  6. sremp commented on Jan 20, 2023

    @sremp

    +1 to this. Just recently ran into this exact issue, with the only reasonable workaround being to use string semantics as detailed in the original post.

  7. justinmchase commented on Apr 20, 2023

    @justinmchase

    This appears to be true for docker based actions as well.

  8. SatishReddyBethi commented on Apr 21, 2023

    @SatishReddyBethi

    +1

  9. agileshaw commented on Apr 28, 2023

    @agileshaw

    Just ran into this issue as well. There doesn't seem to be any related information on the official documentation, and it is very non-intuitive to have inputs support multiple types in workflows and "normal" actions but not in composite actions. I hope this behavior can be changed.

  10. wiegell commented on May 15, 2023

    @wiegell

    I just spend several hours trying to pass a true value... The way to do it is:

    Job:

    
    env:
      SOME_ENV: ${{ github.event_name == 'pull_request' }}
    
    jobs:
      somejob:
        name: Build
        runs-on: ubuntu-latest
    
        steps:
          - name: Set up build
            uses: ./.github/actions/xxx
            with:
              some-input-value: ${{ env.SOME_ENV == 'true' }}
    

    Composite:

    name: Set up build
    description: Steps before build
    
    inputs:
      some-input-value:
        description: "Holy moly this was difficult"
        required: false
        default: false
        type: boolean
    
    runs:
      using: composite
    
    steps:
        - name: "Input flag:"
          run: echo "Inputflag: " && echo ${{ inputs.some-input-value== 'true'  }}
          shell: bash
    
        - name: "Some action"
          uses: someorg/someaction@v999
          if: ${{ inputs.some-input-value == 'true' }}
    

    This makes no sense at all and is not documented.

  11. 27 remaining items

  12. RobbieMcKinstry commented on Jul 22, 2025

    @RobbieMcKinstry

    Any update on this?

  13. mara004 commented on Jun 30, 2026

    @mara004

    This is a very nasty limitation.

    I wonder how to best work around this though? Turn false into empty strings? Explicitly check == 'true' each time? Wrap in fromJSON()? Or ditch composite actions and use reusable workflows instead, accepting the need to allocate a new runner and transfer/recreate state?

    Tentatively I would go with the first option, turning false into empty strings. Let's see how that goes...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingkeepLabel can be added as soon as we are sure the work on the issue is necessarytriagedNeeds extra internal investigation before adding ready-for-dev

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions