Search code examples
azure-pipelinesazure-pipelines-yaml

How to access and use dependent stage results in a different stage's job & task?


Use-case

In Azure pipelines, i'm trying to use previous stage results for the following:-

  • Set conditions for tasks/steps to run depending on previous stage results.
  • To create a formatted string which relies on the previous stage results.

Explanation of current structure:

  • Each stage is in it's own template file.
  • I've tried to save the dependencies results in a variable as I needed to access it multiple times and thought it'd be more readable this way. (If this is not possible i'm happy to change it)
  • Stage4 depends on Stage1-3, with a condition if any of the stages succeeds then Stage4 should run. So that means some of Stage4's dependencies will not have succeeded and I don't want to run certain tasks if specific stages have failed.
#main.yml
stages:
- stage: Stage1
  dependsOn: []
  jobs:
  - template: PipelineTemplates/first.yml
- stage: Stage2
  dependsOn: []
  jobs:
  - template: PipelineTemplates/second.yml
- stage: Stage3
  dependsOn: []
  jobs:
  - template: PipelineTemplates/third.yml
- stage: Stage4
  dependsOn: 
  - Stage1
  - Stage2
  - Stage3
  condition: or(succeeded('Stage1'),succeeded('Stage2'),succeeded('Stage3'))
  jobs:
  - template: PipelineTemplates/fourth.yml
#fourth.yml
jobs:
- job:
  pool:
    vmImage: 'windows-latest'
  variables:
    Friendly_Stage1_Name: 'Stage1'
    Friendly_Stage2_Name: 'Stage2'
    Friendly_Stage3_Name: 'Stage3'
    Stage1_IsSuccess: $[eq(stageDependencies.variables['Friendly_Stage1_Name'].result,'Succeeded')]
    Stage2_IsSuccess: $[eq(stageDependencies.variables['Friendly_Stage2_Name'].result,'Succeeded')]
    Stage3_IsSuccess: $[eq(stageDependencies.variables['Friendly_Stage3_Name'].result,'Succeeded')]
  steps:
    - task: PowerShell@2
      inputs:
        targetType: 'inline'
        script: |
          $Stage1_status = if ($env:Stage1_IsSuccess) {"✔"} else {"❌"}
          $Stage2_status = if ($env:Stage2_IsSuccess) {"✔"} else {"❌"}
          $Stage3_status = if ($env:Stage3_IsSuccess) {"✔"} else {"❌"}
          $Stages_Status = "{0}:{1} | {2}:{3} | {4}:{5}" -f 
          $env:Friendly_Stage1_Name,  $Stage1_status, `
          $env:Friendly_Stage2_Name,  $Stage2_status, `
          $env:Friendly_Stage3_Name, $Stage3_status 
          Write-Host "##vso[task.setvariable variable=Stages_Status]$Stages_Status"

    - script: echo "$Stages_Status"

    - task: SomeTask@1
      condition: eq($(Stage1_IsSuccess),true) # should run only if stage 1 succeeds
    - task: SomeTask@1
      condition: eq($(Stage2_IsSuccess),true) # should run only if stage 2 succeeds
    - task: SomeTask@1
      condition: eq($(Stage3_IsSuccess),true) # should run only if stage 3 succeeds

Current Result:

When I run the pipeline with stage 1-2 passing and stage3 failing the PowerShell task in fourth.yml file just outputs Stage1:✔ | Stage2:✔ | Stage3:✔ for Stages_Status, which means they're all true even when I run the stages with some failing. All the SomeTask@1's are skipped because they haven't met the conditions. Which means they're all false.

I've tried things like using dependencies instead, trying to pass down stageDependencies.result as a parameter from the main.yml file. Using stageDependencies.result on the task condition. None of that seems to work.


Solution

  • Based on your description, I generated the YAML pipeline below and reproduced the same issue. I had tested to use several different expressions like Stage1_Result: $[eq(dependencies.Stage1.result, 'Succeeded')], Stage1_IsSuccess: $[eq(stageDependencies.Stage1.result,'Succeeded')], Stage1_In: $[in(dependencies.Stage1.result, 'Succeeded')] and Stage1_S: $[succeeded('Stage1')], but all in vain. It seems that such expressions only work in condition but cannot be used to define variables, as discussed in this issue. Interestingly, the expression Stage1_Succeeded: $[eq(stageDependencies.Stage1.job1.result, 'Succeeded')] to check a job result works. I understand this may not be what you would expect. enter image description here

    You may consider report the behavior in Developer Community and see if the Product Group have a plan to fix this behavior.

    As of now, you can test with this API in a PowerShell script from Stage4 to check the results of previous stages as a workaround. Here is a sample for your reference.

    GET $(System.CollectionUri)/$(System.TeamProject)/_apis/build/builds/$(Build.BuildId)/Timeline?api-version=7.1-preview.2
    
    stages:
    - stage: Stage1
      dependsOn: []
      jobs:
      - job: Job1
        steps:
        - checkout: none
        - task: PowerShell@2
          inputs:
            targetType: 'inline'
            script: |
              Write-Host Stage - $(System.StageName)
    - stage: Stage2
      dependsOn: []
      jobs:
      - job: Job2
        steps:
        - checkout: none
        - task: PowerShell@2
          inputs:
            targetType: 'inline'
            script: |
              Write-Host Stage - $(System.StageName)
    - stage: Stage3
      dependsOn: []
      jobs:
      - job: Job3
        steps:
        - checkout: none
        - task: PowerShell@2
          inputs:
            targetType: 'inline'
            script: |
              Write-Host Stage - $(System.StageName)
              Write-Host "##vso[task.logissue type=error]log an error message of the task failure"
              Write-Host "##vso[task.complete result=Failed;]"
    - stage: Stage4
      variables:
        Stage1_Result: $[eq(dependencies.Stage1.result, 'Succeeded')]
        Stage1_In: $[in(dependencies.Stage1.result, 'Succeeded')]
        # Stage1_S: $[succeeded('Stage1')]
      dependsOn: 
      - Stage1
      - Stage2
      - Stage3
      condition: or(succeeded('Stage1'),succeeded('Stage2'),succeeded('Stage3'))
      # condition: in(dependencies.Stage1.result, 'Succeeded')
      # condition: eq(dependencies.Stage1.result, 'Succeeded')
      jobs:
      - job:
        pool:
          vmImage: 'windows-latest'
        variables:
          Stage1_Succeeded: $[eq(stageDependencies.Stage1.job1.result, 'Succeeded')]
          Stage1_IsSuccess: $[eq(stageDependencies.Stage1.result,'Succeeded')]
        steps:
          - checkout: none
          - powershell: |
              # URL for the Azure DevOps REST API
              $Url = "$(System.CollectionUri)/$(System.TeamProject)/_apis/build/builds/$(Build.BuildId)/Timeline?api-version=7.1-preview.2"
              # Use System.AccessToken of pipeline for authentication
              $headers = @{
                  'Authorization' = "bearer " + "$(System.AccessToken)"
                  'Content-Type' = 'application/json'
              }
              # Make the API request
              $response = Invoke-RestMethod -Uri $Url -Headers $headers -Method Get
              # Check if the request was successful
              if ($response -ne $null) {
                  # Iterate over each timeline record
                  foreach ($record in $response.records) {
                      # Check if the record type is "Stage"
                      if ($record.type -eq "Stage") {
                          # Dynamically create variables for stage results
                          $variableName = $record.identifier + "_Result"
                          New-Variable -Name $variableName -Value $record.result -Force
                          # Print the created variable's name and value
                          # Write-Host "$variableName is $($record.result)"
                      }
                  }
              }
              else {
                  Write-Host "Failed to retrieve timeline records."
              }
              # Print the result of each previous stage
              Write-Host Stage1_Result - $Stage1_Result
              Write-Host Stage2_Result - $Stage2_Result
              Write-Host Stage3_Result - $Stage3_Result
              $Stage1_status = if ($Stage1_Result -eq 'Succeeded') {"✔"} else {"❌"}
              $Stage2_status = if ($Stage2_Result -eq 'Succeeded') {"✔"} else {"❌"}
              $Stage3_status = if ($Stage3_Result -eq 'Succeeded') {"✔"} else {"❌"}
              Write-Host Stage1_status - $Stage1_status
              Write-Host Stage2_status - $Stage2_status
              Write-Host Stage3_status - $Stage3_status
    
    

    enter image description here