Skip to content

deployment.yml doesn't use its INPUTS input, and custom workflows share the caller's concurrency group #3474

Open

Description

@Ekhorn

Found while reviewing #3448 (inputs, concurrency).

Problems

  • deployment.yml doesn't use its INPUTS input. The Process inputs step only reads it when GITHUB_EVENT_NAME is workflow_call, but in a called workflow that is the caller's event, so the check never matches. It reads the triggering workflow's github.event.inputs instead. The current callers (custom-staging.yml, custom-deploy.yml and deploy.yml) pass those same values as INPUTS, so deployments get the right inputs either way, but the INPUTS plumbing goes unused.
  • In a called workflow github.workflow is the caller's name, so custom-review.yml, custom-staging.yml and custom-deploy.yml use the calling workflow's concurrency group. A caller that sets the common ${{ github.workflow }}-${{ github.ref }} group shares it with them, and a single calling workflow that uses two of them on the same ref cancels its own runs. No custom project sets a group today; the headers warn about it instead.

Proposed fix

The attached patch applies on top of #3448 and is untested:

  • deployment.yml reads only INPUTS. Booleans and numbers from toJSON(inputs) are written as JSON (true, not True), so the outputs match what github.event.inputs gave.
  • The three custom workflows prefix their concurrency group with their own name, and the header warnings are removed.
  • After merging, the deadlock paragraph in the CI/CD pipeline guide can go.

To verify: a manual deploy.yml run with CLEAN_INSTALL on and off, a custom-deploy.yml run from a custom project, and a custom-staging.yml push with a deploy entry.

Activity

  1. added
    InfrastructureTopic Infrastructure: all features related to infrastructure features
    on Oct 23, 2026
  2. Ekhorn commented on Oct 23, 2026

    @Ekhorn
    ContributorAuthor
    Proposed patch

    diff --git a/.github/workflows/custom-deploy.yml b/.github/workflows/custom-deploy.yml
    index 5d515de55..7f22451ec 100644
    --- a/.github/workflows/custom-deploy.yml
    +++ b/.github/workflows/custom-deploy.yml
    @@ -17,8 +17,7 @@
     # - OR_HOSTNAME           - FQDN of host to deploy to (e.g. demo.openremote.app)
     # - OR_ADMIN_PASSWORD     - The admin password to set for clean installs
     #
    -# The calling workflow must be on the default branch before it can be triggered from the GitHub UI, and must not use
    -# the same concurrency group as this workflow, GitHub cancels the run as a deadlock.
    +# The calling workflow must be on the default branch before it can be triggered from the GitHub UI.
     # ---------------------------------------------------------------------------------------------------------------------
     
     name: Custom deploy
    @@ -30,7 +29,8 @@ on:
             type: string
     
     concurrency:
    -  group: ${{ github.workflow }}-${{ github.ref }}
    +  # In a called workflow github.workflow is the caller's name, the prefix keeps this group apart from the caller's
    +  group: custom-deploy-${{ github.workflow }}-${{ github.ref }}
       cancel-in-progress: true
     
     jobs:
    diff --git a/.github/workflows/custom-review.yml b/.github/workflows/custom-review.yml
    index 425c63130..13c10016d 100644
    --- a/.github/workflows/custom-review.yml
    +++ b/.github/workflows/custom-review.yml
    @@ -7,8 +7,6 @@
     # - Running unit and integration tests
     #
     # Jobs are skipped based on which files changed (see the detect-changes job).
    -#
    -# The calling workflow must not use the same concurrency group as this workflow, GitHub cancels the run as a deadlock.
     # ---------------------------------------------------------------------------------------------------------------------
     
     name: Custom review
    @@ -20,7 +18,8 @@ on:
             type: string
     
     concurrency:
    -  group: ${{ github.workflow }}-${{ github.ref }}
    +  # In a called workflow github.workflow is the caller's name, the prefix keeps this group apart from the caller's
    +  group: custom-review-${{ github.workflow }}-${{ github.ref }}
       cancel-in-progress: true
     
     jobs:
    diff --git a/.github/workflows/custom-staging.yml b/.github/workflows/custom-staging.yml
    index f415dfb49..e461118c4 100644
    --- a/.github/workflows/custom-staging.yml
    +++ b/.github/workflows/custom-staging.yml
    @@ -8,8 +8,6 @@
     # Without such an entry the deployment job is skipped, use custom-deploy.yml for manual deployments. An entry without
     # an environment, e.g. { "push": { "main": { "deploy": {} } } }, only uses '.ci_cd/env/.env' and secrets without an
     # environment prefix.
    -#
    -# The calling workflow must not use the same concurrency group as this workflow, GitHub cancels the run as a deadlock.
     # ---------------------------------------------------------------------------------------------------------------------
     
     name: Custom staging
    @@ -21,7 +19,8 @@ on:
             type: string
     
     concurrency:
    -  group: ${{ github.workflow }}-${{ github.ref }}
    +  # In a called workflow github.workflow is the caller's name, the prefix keeps this group apart from the caller's
    +  group: custom-staging-${{ github.workflow }}-${{ github.ref }}
       cancel-in-progress: true
     
     jobs:
    diff --git a/.github/workflows/deployment.yml b/.github/workflows/deployment.yml
    index 455508434..4649db279 100644
    --- a/.github/workflows/deployment.yml
    +++ b/.github/workflows/deployment.yml
    @@ -147,24 +147,22 @@ jobs:
               import json
     
               # Overlay all inputs and secrets onto this jobs outputs
    -          callerInputs = os.getenv("CALLER_INPUTS")
               inputs = os.getenv("INPUTS")
    -          eventName = os.getenv("GITHUB_EVENT_NAME")
     
               if inputs is not None and inputs != '':
                 inputs = json.loads(inputs)
    -          if eventName == 'workflow_call' and callerInputs is not None and callerInputs != 'null':
    -            os.system(f"echo 'Processing caller inputs'")
    -            inputs = json.loads(callerInputs)
     
    -          if inputs is not None and 'INPUTS' in inputs:
    +          if isinstance(inputs, dict) and 'INPUTS' in inputs:
                 os.system("echo 'Processing inputs from caller'")
    -            inputs = json.loads(inputs['INPUTS'])
    +            inputs = json.loads(inputs['INPUTS'] or 'null')
     
               # Iterate over inputs and assign them as outputs on this step
     
    -          if inputs is not None and inputs != 'null':
    +          if isinstance(inputs, dict):
                 for key, value in inputs.items():
    +              # Output booleans and numbers as JSON, e.g. 'true' rather than 'True'
    +              if not isinstance(value, str):
    +                value = json.dumps(value)
                   os.system(f"echo 'Outputting input: {key}'")
                   lines = len(value.split("\n"))
                   if lines > 1:
    @@ -172,8 +170,7 @@ jobs:
                   else:
                       os.system(f"echo '{key}={value}' >> $GITHUB_OUTPUT")
             env:
    -          CALLER_INPUTS: ${{ inputs.INPUTS }}
    -          INPUTS: ${{ toJSON(github.event.inputs) }}
    +          INPUTS: ${{ inputs.INPUTS }}
     
           - name: Public IP
             id: ip-address
    

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    InfrastructureTopic Infrastructure: all features related to infrastructure features

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions