3

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.
DevOpsy
  • 667
  • 1
  • 8
  • 18

4 Answers4

5

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

Bright Ran-MSFT
  • 5,190
  • 1
  • 5
  • 12
  • This answer won't work, please note that @DevOpsy is using deployment jobs and you are using normal jobs. Their condition settings should be different. Please refer to the second note in [this document](https://learn.microsoft.com/en-us/azure/devops/pipelines/process/variables?view=azure-devops&tabs=yaml%2Cbatch#use-output-variables-from-tasks). – Oscar Mar 03 '22 at 02:41
1

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.

DevOpsy
  • 667
  • 1
  • 8
  • 18
0

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/')
Schamal
  • 121
  • 5
  • I tried that, didn't work, the stage still gets skipped. Is there any way to look at the dependencies structure? – DevOpsy Mar 02 '22 at 07:22
  • Change those jobs in both stages to deployment jobs and it stops working. See my example above in the original post for a sample. – DevOpsy Mar 02 '22 at 22:23
  • Okay, yeah didn't get it as well, why syntax is different in every possible case. – Schamal Mar 03 '22 at 14:01
  • yeah, there is a variation in syntax for various cases and more surprisingly it is not even covered in the documentation(which feels exhaustive but is still missing some scenarios). I believe Azure DevOps team has really messed this feature up, saying by the fact that I had to specify the name of the job twice for it to work and there is no way to even diagnose that. – DevOpsy Mar 04 '22 at 05:41
0

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)"
MeneerBij
  • 309
  • 2
  • 6