Azure 管道作业成功,某些任务成功

时间:2021-03-30 13:51:32

标签: azure-devops continuous-integration

我有以下工作结构:

  • 任务 1
  • 任务 2
  • 任务...
  • 任务 N
  • 发布工件

主要目标是运行发布工件,如果任何任务成功,如果所有任务都失败,发布工件不应该被执行,作业应该失败。

我尝试考虑的事情:

  • 发布工件 条件 SucceededorFailed 将不起作用,因为所有任务都可能失败并且如果所有任务都失败则无法发布。
  • 发布工件 条件失败将不起作用,因为任何任务都可能失败并触发此或全部成功但永远不会执行。
  • 发布工件条件成功也不会起作用,好像任何失败都不会执行一样。
  • 发布工件条件始终适用于其他情况
  • 我不介意使用 SucceedWithIssues 完成工作,只要 Task 1..N 的所有任务都失败了。
  • Task X 如果 Task X - 1 通过,则不需要执行,如果其中任何一个通过,我宁愿不运行其余任务。立>

我尝试过的解决方案:

我尝试在发布工件上使用自定义条件,之前询问所有任务的结果,但 azure 管道不保存任务的结果状态。

我还尝试在除最后一个任务(任务 N)之外的每个任务上使用 continueOnError 但如果此任务失败,即使其中一个成功,整个过程也会失败。

tried this solution但是这个解决方案中的脚本不知道我正在设置变量的任务是否实际通过,它只会认为任何任务失败,它不会确定哪个任务是失败的那个。

有什么关于我如何通过这次考试的建议吗?

3 个答案:

答案 0 :(得分:1)

我不确定我是否完全按照您的要求进行操作,但我认为这可能会满足您的需求。

假设我是正确的:

  1. 只有在前面的任务失败时才应运行任务。
  2. 如果上述任何任务成功,则发布任务应该运行
  3. 如果任务 N 失败,则发布任务不应运行且作业应失败

# Sample pipeline
# Inline bash scripts to simulate failing tasks.
# Comment out the 'exit 1' line to allow a task to succeed
# The Publish Artifact task below is just a fake for the purposes of testing

steps:
  - task: Bash@3
    name: task1
    displayName: Task 1
    continueOnError: true
    inputs:
      targetType: 'inline'
      script: |
        echo 'Task 1'
        exit 1
        echo "##vso[task.setvariable variable=task1_succeeded]true"
      failOnStderr: true
      
  - task: Bash@3
    name: task2
    displayName: Task 2
    condition: ne(variables.task1_succeeded, true)
    continueOnError: true
    inputs:
      targetType: 'inline'
      script: |
        echo 'Task 2'
        exit 1
        echo "##vso[task.setvariable variable=task2_succeeded]true"
      failOnStderr: true
      
  - task: Bash@3
    name: task3
    displayName: Task 3
    condition: |
      and(
        ne(variables.task1_succeeded, true),
        ne(variables.task2_succeeded, true)
      )
    continueOnError: true
    inputs:
      targetType: 'inline'
      script: |
        echo 'Task 3'
        exit 1
        echo "##vso[task.setvariable variable=task3_succeeded]true"
      failOnStderr: true

  - task: Bash@3
    name: taskN
    displayName: Task N
    condition: |
      and(
        ne(variables.task1_succeeded, true),
        ne(variables.task2_succeeded, true),
        ne(variables.task3_succeeded, true)
      )
    continueOnError: false
    inputs:
      targetType: 'inline'
      script: |
        echo 'Task N'
        exit 1
        echo "##vso[task.setvariable variable=taskN_succeeded]true"
      failOnStderr: true

  - task: Bash@3
    name: publishArtifact
    condition: |
      or(
        eq(variables.task1_succeeded, true),
        eq(variables.task2_succeeded, true),
        eq(variables.task3_succeeded, true),
        eq(variables.taskN_succeeded, true)
      )
    displayName: 'Publish Artifact'
    inputs:
      targetType: 'inline'
      script: |
        echo 'Pseudo Publish Artifact'
      failOnStderr: true

答案 1 :(得分:1)

  1. 如果任务 N 失败,则发布任务不应运行且作业应失败。
  2. 如果任务 N 成功并且另一个任务成功,则发布任务应该运行。

检查这个 YAML,我们可以添加一个 power shell 任务并调用 REST API 来检查任务结果。


# Starter pipeline
# Start with a minimal pipeline that you can customize to build and deploy your code.
# Add steps that build, run tests, deploy, and more:
# https://aka.ms/yaml

trigger: none

pool:
  vmImage: ubuntu-latest

steps:
- task: PowerShell@2
  displayName: task1
  inputs:
    targetType: 'inline'
    script: 'Write-Host "Hello World"'

- task: PowerShell@2
  displayName: task2
  inputs:
    targetType: 'inline'
    script: 'Write-Host "Hello World" $(xasda)'

- task: PowerShell@2
  displayName: taskn
  inputs:
    targetType: 'inline'
    script: 'Write-Host "Hello World"' 
  condition: always()


- task: PowerShell@2
  inputs:
    targetType: 'inline'
    script: |
      $PAT="{pat}"
      $base64AuthInfo= [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$($PAT)"))
      
      #List all build timeline via build ID
      $ListAllBuildTimeLineURL="https://dev.azure.com/{Org name}/{Project name}/_apis/build/builds/$(Build.BuildId)/timeline?api-version=6.1-preview.2"
      $ListAllBuildTimeLineResult = Invoke-RestMethod -Uri $ListAllBuildTimeLineURL -Headers @{Authorization = "Basic {0}" -f $base64AuthInfo} -Method get
      $ListAllBuildTimeLineResult.records.Count
      
      #Check task result and set variable
      foreach($Task in $ListAllBuildTimeLineResult.records){
              if($Task.name -eq "taskn"){
                  #write-host $Task.state
                  #write-host $Task.id
                  if($Task.result -eq "succeeded"){
                      Write-Host "##vso[task.setvariable variable=taskn.status]Success"
                  }
              }
              if($Task.name -eq "task1"){
                  if($Task.result -eq "succeeded"){
                      Write-Host "##vso[task.setvariable variable=task.status]Success"
                  }
              }
              if($Task.name -eq "task2"){
                  if($Task.result -eq "succeeded"){
                      Write-Host "##vso[task.setvariable variable=task.status]Success"
                  }
              }
      }
  condition: always()

#
- task: Bash@3
  displayName: publishArtifact
  inputs:
    targetType: 'inline'
    script: 'printenv'
  condition: and(eq(variables['taskn.status'], 'success'),eq(variables['task.status'], 'success'))

结果:

注意:任务 Bash 应该是任务 Publish artifact

enter image description here

答案 2 :(得分:0)

有 2 个可能的答案,每个答案都有一些问题。但他们确实解决了这个问题。

让每项任务都成为工作

这将要求每个任务都被视为一个单独的作业,因此条件和依赖项取决于作业,并且应该在它们之间传递。


    # Sample pipeline
    # Avoiding setup and rest of variables

    - job: task_1
      steps:
 
        - task: Bash@3
          name: task1
          displayName: Task 1
          continueOnError: true
          inputs:
            targetType: 'inline'
            script: |
              echo 'Task 1'

        - task: Bash@1
            displayName: 'Publish Artifact'
            inputs:
            targetType: 'inline'
            script: |
              echo "Publish artifact"


    - job: task_2
      dependsOn: 
        - task_1
      condition: eq(dependencies.task_1.result,'SucceededWithIssues')
      steps:
      
        - task: Bash@3
          name: task2
          displayName: Task 2
          continueOnError: true
          inputs:
            targetType: 'inline'
            script: |
              echo 'Task 2'

        - task: Bash@1
            displayName: 'Publish Artifact'
            inputs:
            targetType: 'inline'
            script: |
              echo "Publish artifact"


    #Keep on doing tasks...

    - job: task_N
      dependsOn: 
        - task_N-1
      condition: eq(dependencies.task_N-1.result,'SucceededWithIssues')
      steps:
    
        - task: Bash@3
          name: task_N
          displayName: Task N
          inputs:
            targetType: 'inline'
            script: |
              echo 'Task N'

        - task: Bash@1
            displayName: 'Publish Artifact'
            inputs:
            targetType: 'inline'
            script: |
              echo "Publish artifact"

注意:

  • 最后一个任务(示例中的任务 N)没有 ContinueOnError 标志是必要的,所以你知道如果最后一个任务失败了未执行。
  • 第一个任务没有依赖

优点:

  • 我们避免在其中一个任务成功时调用任务,避免执行不需要的代码

缺点:

  • 这将多次运行发布工件任务,因为 ContinueOnError 成功并且发布将继续发生。
  • 如果在剩下的任务之后需要完成另一项工作,这将取决于每项工作,并且条件必须包括 SuccessWithIssues,并且将每个工作都写在
  • 如果任务需要获取一些数据或信息,则需要将其传递给每个作业,或下载每个作业所需的任何工件。对每个作业重复的附加步骤
  • 如果作业 SucceededOrFailed
  • ,管道将作为警告返回

使用脚本打印变量

我没有设法在我需要的代码上测试这个例子,但看起来很合乎逻辑,我不明白为什么它会失败。这是基于@Vito Liu-MSFT 的回答

    # Sample pipeline

    - job: task_1
      steps:
        - task: Bash@3
          name: task1
          displayName: Task 1
          inputs:
            targetType: 'inline'
            script: |
              echo 'Task 1'
          failOnStderr: true

        - task: Bash@3
          name: task2
          displayName: Task 2
          condition: failed()
          inputs:
            targetType: 'inline'
            script: |
              echo 'Task 1'
          failOnStderr: true

        # Keep doing tasks
        - task: Bash@3
          name: taskN
          displayName: Task N
          inputs:
            targetType: 'inline'
            script: |
              echo 'Task 1'
          failOnStderr: true
          condition: failed()

         - task: PowerShell@2
           inputs:
             targetType: 'inline'
            script: |
              $PAT="{pat}"
              $base64AuthInfo= System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$($PAT)"))
    
              #List all build timeline via build ID
              $ListAllBuildTimeLineURL="https://dev.azure.com/{Org name}/{Project name}/_apis/build/builds/$(Build.BuildId)/timeline?api-version=6.1-preview.2"
              $ListAllBuildTimeLineResult = Invoke-RestMethod -Uri $ListAllBuildTimeLineURL -Headers @{Authorization = "Basic {0}" -f $base64AuthInfo} -Method get
              $ListAllBuildTimeLineResult.records.Count
      
              #Check task result and set variable
              foreach($Task in $ListAllBuildTimeLineResult.records){
                if($Task.name -eq "task1"){
                  if($Task.result -eq "succeeded"){
                      Write-Host "##vso[task.setvariable variable=task1.status]Success"
                  }
                }
                if($Task.name -eq "task2"){
                  if($Task.result -eq "succeeded"){
                      Write-Host "##vso[task.setvariable variable=task2.status]Success"
                  }
                }
                # Keep printing each task if succeed

                if($Task.name -eq "taskN"){
                  if($Task.result -eq "succeeded"){
                      Write-Host "##vso[task.setvariable variable=taskN.status]Success"
                  }
                }
              }
          condition: SucceededOrFailed()

        - task: Bash@1
          displayName: 'Publish Artifact'
          inputs:
            targetType: 'inline'
            script: |
              echo "Publish artifact"
           condition: |
             or(
               SucceededOrFailed(),
               or(
                 eq(variables['task1.status'], 'success'),
                 or(
                   eq(variables['task2.status'], 'success'),
                   or(
                     # Keep writing ors
                     eq(variables['taskN-1.status'], 'success')
                     eq(variables['taskN.status'], 'success')
                   )
                 )
               )
             )

注意事项:

  • 打印变量的脚本必须包含所有任务。它应该被执行为 SucceededOrFailed
  • 第一个任务在运行时没有条件。

优点:

  • 我们只需要为一项工作下载依赖项,不需要将所有需要的东西都发送到每个单独的任务
  • 我们不必每次都重复发布工件任务。

缺点:

  • 如果第一个任务失败,则执行所有任务。
  • 需要一个用于写入变量的脚本来写入每个任务的状态。
  • 需要一个 URL 来访问构建状态和任务名称。如果 URL 更改,管道将需要更改。
  • 扩展条件 Publish Artifact 需要碰巧知道一切正常。
  • 作业中的未来任务将需要 SucceededOrFail 才能继续运行。

我没有对此进行测试,我不确定,但是由于任务失败,我相信作业的状态将失败,未来的依赖项将不得不处理这种情况,这将不确定作业是否真的失败了。管道可能会作为错误返回。使用 ContinueOnError 更改失败时可以获得更好的结果,并且作业将返回 SucceededWithIssues

相关问题