actions/runner

Boolean inputs are not actually booleans in composite actions

flobernd opened this issue ยท 15 comments

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.

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.

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.

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.

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:') }}
sremp commented

+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.

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

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.

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.

Also worth nothing that if expressions are always evaluated to strings https://docs.github.com/en/actions/learn-github-actions/expressions#about-expressions

Then it seems unclear how this will ever work for passing variables from workflow call/dispatch. E.g.

workflow dispatch with boolean 
  -> calls composite action
      with:
        variable: ${{ inputs.from_workflow_dispatch_boolean }}   <--- this could start as a boolean from the dispatch but be coerced to a string by ${{

I am also running into this and trying to sort out how to work around this.

I would suggest not doing a workaround that involves using type: boolean but then treating it as a string, such as if: ${{ inputs.some-input-value == 'true' }}. My reasoning is that if GitHub actually fixes this issue, those workarounds would no longer be expected to function. After a fix to this issue, comparing a boolean == 'true' would return false, which is not what you want in the long-term.

I have been thinking about this more lately and I'm starting to think that this is not a bug and instead is us using yaml syntax that is not actually supported or documented by GitHub.

workflow_call and workflow_dispatch have inputs sections documented that include the type field, so it led me and others to think that composite actions can have that field too.

However, the inputs documentation for composite actions doesn't document the type field at all. So when we put type: boolean in there, we are just adding a field that GitHub doesn't know about just like if I was to add someflagijustmadeup: true, so it is ignored.

@grossag, while I think that you're correct in that this is simply not a supported feature of GHActions, I think the rest of this thread makes it very clear that people would REALLY like if it we're supported. The fact that it's nearly identical to other mechanisms for declaring inputs except that it's missing type is confusing on it's own and probably worth doing something about.

This bug just cost me half of a work day. Why isn't anyone addressing this?

it 2024 and GH actions doesn't treat booleans as booleans in any other software... that's a shame.