azure-devopsazure-pipelinesazure-pipelines-yaml

Passing a variable computed in a stage as parameter to a template in a following stage


I have a pipeline that takes parameters when triggered manually.
Also, it can be triggered by merging a pull request. Now I am trying to recognize tags in the PR in order to override the parameters that, in the case of PR trigger, cannot be manually defined.

This question is very similar to Azure Pipelines: Passing a variable as a parameter to a template, however, here I don't have a global variable but one computed inside of a stage, which is causing me troubles passing as a parameter.

pipeline.yml:

parameters:
- name: doSomething
  type: boolean
  default: false

stages:
- stage: DetectPrTags
  displayName: Detect Pull Request Tags
  jobs:
  - job: DetectPrTags
    displayName: Detect Pull Request Tags
    pool:
      vmImage: 'ubuntu-latest'
    steps:
    - checkout: none
    - task: Bash@3
      name: DetectAndSetPrTags
      displayName: Detect Pull Request Tags
      env:
        SYSTEM_ACCESSTOKEN: $(System.AccessToken)
      inputs:
        targetType: 'inline'
        script: |
          # ... get tag from PR and write into variable $doSomethingFromTags ...
                
          # Compute effective values
          if [ "$doSomethingFromTags" == "true" ] || [ "${{ parameters.doSomething }}" == "true" ]; then
            doSomethingEffective="true"
          else
            doSomethingEffective="false"
          fi

          echo "Effective DoSomething: $doSomethingEffective"

          # Export outputs
          echo "##vso[task.setvariable variable=doSomethingEffective;isOutput=true]$doSomethingEffective"

          echo "##[endgroup]"

- stage: SecondStage
  displayName: Second Stage
  dependsOn: DetectPrTags
  variables:
    doSomethingEffective: $[ stageDependencies.DetectPrTags.DetectPrTags.outputs['DetectAndSetPrTags.doSomethingEffective'] ]
  jobs:
  - template: second-stage.yml
    parameters:
      doSomething: ${{ eq(variables.doSomething, 'true') }}

second-stage.yml:

parameters:
  doSomething: false

jobs:
- job: SecondStage
  displayName: Second Stage
  pool:
    vmImage: 'ubuntu-latest'
  steps:
  - script: |
      echo "PARAMETER doSomething: ${{ parameters.doSomething }}"
    displayName: Print input parameters

If I set the PR tag do-something, I'd expect the output in the second stage to tell me that doSomething is true, however it stays false.

This is the output I get in the pipeline:

I have tried several ways of passing the variable, I have tried accessing the variable directly in the second-stage template (it's empty), using a global variable in the pipeline and accessing it directly in the second-stage template (this works, but I can't overwrite it when loading the tags).


Solution

  • Parameters are only available at compile time. When parsing, the value of variables.doSomething is not yet set by your script and the expression

    doSomething: ${{ eq(variables.doSomething, 'true') }}
    

    gets executed before the variable gets an actual value at runtime.

    The solution for your problem is to skip parameters and stick to runtime variables and expressions wich get processed at runtime after your first script got executed:

      - stage: SecondStage
        displayName: Second Stage
        dependsOn: DetectPrTags
        variables:
              doSomething: $[ eq(stageDependencies.DetectPrTags.DetectPrTags.outputs['DetectAndSetPrTags.doSomethingEffective'], 'true') ]
        jobs:
          - template: second-stage.yml
    

    second-stage.yml:

    jobs:
    - job: SecondStage
      displayName: Second Stage
      pool:
        name: Testing
      steps:
      - script: |
          echo "PARAMETER doSomething: $(doSomething)"
        displayName: Print input parameters