Azure DevOps Yaml pipelines: Stage condition variable syntax

6.7k Views Asked by At

I am working on a azure devops multi stage yaml pipeline. I set a variable in the first stage and then for the next stage, I have a condition based on that variable. I am also retrieving the variable value in the next stage. Apparently there is slight difference between the syntax for accessing inter-stage variables in the condition and at the stage level. I am not able to figure out the syntax I need to use in the condition. I have tried all possible variations but none seems to be working. In the example below, I am expecting the lint stage to run however it gets skipped. What should be exact syntax for the condition here?

stages:
- stage: build
  displayName: build
  pool:
    name: Azure Pipelines
    vmImage: ubuntu-latest
  dependsOn: []
  jobs:
  - deployment: build_job
    environment:
      name: "test"
    strategy:
      runOnce:
        deploy:
          steps:
          - task: PowerShell@2
            displayName: "get commitMessage variable"
            name: getCommitMessage
            inputs:
              targetType: inline
              pwsh: true
              script: |
                $commitMessage = "abcd_import/"
                echo "setting commitMessage: $commitMessage"
                echo "##vso[task.setvariable variable=commitMessage;isOutput=true]$commitMessage"

- stage: lint
  displayName: lint
  dependsOn:
  - 'build'
  condition: contains(stageDependencies.build.build_job.outputs['build_job.getCommitMessage.commitMessage'], 'import/')
  pool:
    name: Azure Pipelines
    vmImage: ubuntu-latest
  variables:
  - name: BUILD_STAGE_GET_COMMIT_MESSAGE
    value: $[stageDependencies.build.build_job.outputs['build_job.getCommitMessage.commitMessage']]
  jobs:
  - deployment: validate
    environment:
      name: "test"
    strategy:
      runOnce:
        deploy:
          steps:
          - task: PowerShell@2
            displayName: "commitMessage is empty"
            name: fail
            inputs:
              targetType: inline
              pwsh: true
              script: "echo $(BUILD_STAGE_GET_COMMIT_MESSAGE)"

Update(Answer): I raised a MS support case on this one and got a resolution. The right syntax is

condition: contains(dependencies.build.outputs['build_job.build_job.getCommitMessage.commitMessage'], 'import/')

A few points on this weird issue:

  • The syntax for using a stage variable is different for using it in a condition and using it to pass in as another variable.
  • Yet again, the syntax is different if the source stage is a job v/s if the source stage is a deployment job.
  • Most importantly, I couldn't find the correct syntax anywhere in the MS documentation.
  • I couldn't find any way to diagnose the issue for example: it would be really helpful to see the stage output json in the build logs.
  • Finally, I believe Azure DevOps team has really messed this feature up. The syntax should be consistent across the jobs/deployment jobs, conditions and variables assignments. It's really a pain in this current state.
4

There are 4 best solutions below

4
Schamal On

As described here: https://learn.microsoft.com/en-us/azure/devops/pipelines/process/expressions?view=azure-devops#dependencies i think your condition instead of this

condition: contains(stageDependencies.build.build_job.outputs['build_job.getCommitMessage.commitMessage'], 'import/')

should look like this:

condition: contains(dependencies.build.outputs['build_job.getCommitMessage.commitMessage'], 'import/')
1
Bright Ran-MSFT On

On a stage, to reference an output variable from another stage, you should use the following expression formats:

  • At the stage level, the format for referencing an output variable from another stage is dependencies.STAGE.outputs['JOB.TASK.VARIABLE'].
  • At the job level, the format for referencing an output variable from another stage is stageDependencies.STAGE.JOB.outputs['TASK.VARIABLE'].

For more details, you can see the document about "Use outputs in a different stage".

In addition, on a stage, if you set a stage-level variable using the output from another stage, you should use the format stageDependencies.STAGE.JOB.outputs['TASK.VARIABLE'] instead of dependencies.STAGE.outputs['JOB.TASK.VARIABLE']. See this document.

enter image description here

Below is an example as reference:

  1. azure-pipelines.yml
parameters:
- name: RunStgB
  type: string
  default: YesRun
  values:
  - YesRun
  - NoRun

stages:
- stage: A
  displayName: 'Stage A'
  pool:
    vmImage: ubuntu-latest
  jobs:
  - job: A1
    displayName: 'Job A1'
    steps:
    - task: Bash@3
      name: setOutput
      displayName: 'Set output variable'
      inputs:
        targetType: inline
        script: |
          echo "parameters.RunStgB = ${{ parameters.RunStgB }}"
          echo "##vso[task.setvariable variable=RunStgB;isoutput=true]${{ parameters.RunStgB }}"

- stage: B
  displayName: 'Stage B'
  dependsOn: A
  condition: eq(dependencies.A.outputs['A1.setOutput.RunStgB'], 'YesRun')
  variables:
  - name: Output_RunStgB
    value: $[ stageDependencies.A.A1.outputs['setOutput.RunStgB'] ]
  pool:
    vmImage: ubuntu-latest
  jobs:
  - job: B1
    displayName: 'Job B1'
    steps:
    - task: Bash@3
      displayName: 'show output variable'
      inputs:
        targetType: inline
        script: echo "Output_RunStgB = $(Output_RunStgB)"
  1. Result.

    enter image description here

    enter image description here

1
DevOpsy On

I raised a MS support case on this one and got a resolution. The right syntax is

condition: contains(dependencies.build.outputs['build_job.build_job.getCommitMessage.commitMessage'], 'import/')

I have updated the original question with the answer as well.

0
MeneerBij On

The stage dependencies are hard to debug. There are no logs of the evaluation, it's trail and error. The weird thing is the double reference of the job name?? Can someone confirm this if its also true for you (upvote)?

Stage condition: dependencies.STAGE.outputs['JOB.JOB.TASK.VARIABLE'].

Example

stages:
- stage: stage_A
  jobs:
  - job: job1
    steps:
      # Note: isOutput is set to true
    - bash: echo "##vso[task.setvariable variable=boolVar1;isOutput=true]true"
      name: task_1  # Note name is important for reference

- stage: stage_B  # no dependsOn, automatically depends on previous stage.
  condition: and(succeeded(), eq(dependencies.stage_A.outputs['job1.job1.task_1.boolVar1'], 'true'))
  jobs:
  - job: running
    variables: 
      boolVar1: $[ stageDependencies.stage_A.job1.outputs['job1.task_1.boolVar1']]
    steps:
    - bash: echo "Variable reference inside Job: $(boolVar1)"