Search code examples
azure-devopsazure-pipelines-yaml

Access output from looped stages in devops pipelines


Baiscally I have the following workflow:

  • For each configured item, run a stage which does stuff, and outputs a string into a variable

  • After all prioir stages have elapsed, send a notification to teams/slack whatever with all collected variables.

Or as vastly simplified pipeline in part as pseudocode:

stages:
  - ${{ each stage in parameters.branchInfo }}:
  - stage: ${{stage.name}}
      displayName: Do stuff for ${{ stage.name }}
      dependsOn: []
      jobs:
        - job: foo
          displayName: Perform Foo
          steps:
            - checkout: ${{ stage.name }}
              displayName: Clone ${{ stage.name }}
              fetchDepth: 0
              lfs: true
              persistCredentials: true
              clean: true            
          
            - task: PythonScript@0
              inputs:
                scriptSource: 'filePath'
                scriptPath: './doo_foo.py'
 - stage: report
     displayName: Report success
      # TODO: something like this?  dependsOn: ${{ each stage in parameters.fooList }}
      jobs:
      - job: 
        displayName: Perform Branching
        steps:
          - task: Bash@3
            displayName: 'Notify someone'
            inputs:
              targetType: 'inline'
              script: |
                # pseudocode
                ${{ each stage in parameters.fooList }} 
                  echo $[ stageDependencies.{stage.name}.foo.outputs['{stage.name}.myVar'] ]
                {{ endfor }}
                             

where doo_foo.py, does this:

print(f'##vso[task.setvariable variable=myVar]myFancyValue')

How can this be done?


Solution

  • You can use variables from previous stages, but you cannot use variables from looped stages in devops pipelines.

    TODO: something like this? dependsOn: ${{ each stage in parameters.fooList }}

    In Azure DevOps, the dependsOn keyword doesn’t support looping through a list of parameters.

    The most nearby way is to use object type parameters like below:

    parameters:
      - name: fooList
        type: object
        default:
          - stage1
          - stage2
        values:
          - stage1
          - stage2
          - stage3 
    
    stages:
    - stage: stage1
      ...
    
    - stage: stage2
      dependsOn: []
      ...
    
    - stage: stage3
      dependsOn: []
      ...
    
    - stage: report
      dependsOn: ${{ parameters.fooList }}
    
    

    enter image description here

    However, to map variables from previous stage, you need to define the variable with isOutput=true, and map the variable in with detail stage name, cannot use parameter stage name.

    - stage: one
      jobs:
      - job: A
        steps:
        - task: Bash@3
          inputs:
              filePath: 'script-a.sh'
          name: setvar
        - bash: |
           echo "##vso[task.setvariable variable=skipsubsequent;isOutput=true]true"
          name: skipstep
    
    - stage: two
      jobs:
      - job: B
        variables:
          - name: skipMe
            value: $[ stageDependencies.one.A.outputs['skipstep.skipsubsequent'] ]
    

    You can check the doc Use outputs in a different stage for the details.

    Hence, you have to define the stage explicitly in last report stage if you want to get all variable output in previous stages:

    - stage: report
      dependsOn: # define the stage name explicitly
      - stage1
      - stage2
      jobs:
      - job: job3
        variables:
        - name: skipMe
          value: $[ stageDependencies.stage1.job1.outputs['skipstep.skipsubsequent'] ]
        - name: newtestvar
          value: $[ stageDependencies.stage2.job2.outputs['setvar.testvar'] ]
        steps:
          - bash: |
              echo $(skipMe)
              echo $(newtestvar)
    

    PS: if you prefer to use python script to define the variable, use format with isOutput=true: print(f'##vso[task.setvariable variable=myVar;isOutput=true]myFancyValue')