当构建管道不生成工件时如何防止 YAML 部署管道 运行

How to prevent a YAML Deployment Pipeline from running when the Build Pipeline doesn't produce an artifact

我们有一个为 CI 构建触发的构建管道,它仅在构建成功时发布工件,而不是 PR 构建或计划构建。

当构建管道发布工件时,我们只希望下面定义的发布管道为 运行。如果它不发布工件,则触发的发布管道失败。

我认为我的理解来自阅读 docs.microsoft.com,如果资源不产生工件,它就不会触发资源触发器(我目前无法找到对该声明的支持)。我们刚刚为 运行ning 一些长时间的 运行ning 测试添加了计划的夜间构建到构建管道。根据配置,发布任务的条件阻止了工件的创建,但发布管道仍在触发并导致失败。

trigger: none
pr: none
resources:
  pipelines:
  - pipeline: API-Deployment 
    source: API-Build  
    trigger: 
      branches:
        include: 
        - refs/heads/main

当构建管道由计划构建或 PR 构建触发时,我们如何防止发布管道 运行ning?或者我们如何检查触发构建是否产生了工件,以便我们可以防止 stages/jobs/tasks 在工件不存在时 运行 失败?

感谢您在开发者社区发帖。

当您使用 资源时构建管道不产生工件时,恐怕没有这样的 out-of-the-box 方法来防止发布管道 运行管道触发器.

那是因为目前不支持工件资源触发器。你可以查看文档Define resources in YAML

当前支持的资源触发类型:

resources:
  pipelines: [ pipeline ]  
  builds: [ build ]
  repositories: [ repository ]
  containers: [ container ]
  packages: [ package ]
  webhooks: [ webhook ]

另一方面,当我们使用管道资源触发器时,我们没有工件过滤器来限制工件是否包含在资源中:

Evaluation of artifact version.

作为解决方法,我们可以使用经典管道而不是 YAML 作为发布管道,这样我们就可以 select 工件源:

如果您仍想使用 YAML,可以删除管道资源触发器,然后添加一个 powershell 任务来调用 REST API 以触发发布管道,条件为:

and(succeeded(), in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI'))

我们最终解决此问题的方法 thanks to the help of @Leo Liu-MSFT 是添加所有第一层阶段都依赖的 PrepStage,它使用 Azure DevOps REST API 调用触发管道的工件端点。然后设置hasArtifacts变量,用于依赖PrepStage的后续阶段条件。

下面是准备阶段的 YAML 和依赖它的阶段的示例。

stages:
  - stage: PrepStage
    displayName: Prep Stage
    jobs:
      - job: JA
        displayName: Deployment Prep Job
        cancelTimeoutInMinutes: 1
        steps:
          - checkout: none
          - task: PowerShell@2
            name: setHasArtifacts
            inputs:
              targetType: 'inline'
              script: |
                $url = "$($env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI)$env:SYSTEM_TEAMPROJECTID/_apis/build/builds/$(triggerPipeRunID)/artifacts?api-version=4.1"
                Write-Host "URL: $url"
                $artifacts = Invoke-RestMethod -Uri $url -Headers @{ Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN" }
                $artifactCount = $artifacts.count
                Write-Host "Artifact Count: $artifactCount"

                if ($artifactCount -gt 0) {
                    Write-Host "Set Variable hasArtifacts to True"
                    Write-Host "##vso[task.setvariable variable=hasArtifacts;isOutput=true]True"
                }
                else {
                    Write-Host "Set Variable hasArtifacts to False"
                    Write-Host "##vso[task.setvariable variable=hasArtifacts;isOutput=true]False"
                }
            env:
               SYSTEM_ACCESSTOKEN: $(System.AccessToken)
          - script: echo "hasArtifacts - $(setHasArtifacts.hasArtifacts)"
            name: echovar

  - stage: SB
    dependsOn: PrepStage
    condition: and(succeeded(), eq(dependencies.PrepStage.outputs['JA.setHasArtifacts.hasArtifacts'], 'True'))
    jobs:
      - job: JB
        condition: and(succeeded(), eq(stageDependencies.PrepStage.JA.outputs['setHasArtifacts.hasArtifacts'], True))
        variables:
          varFromStageA: $[ stageDependencies.PrepStage.JA.outputs['JA.setHasArtifacts.hasArtifacts'] ]
          varFromStageA2: $[ stageDependencies.PrepStage.JA.outputs['setHasArtifacts.hasArtifacts'] ]
        steps:
        - checkout: none
        - script: |
            echo "varFromStageA: $(varFromStageA)"      
            echo "varFromStageA2: $(varFromStageA2)"

一些值得注意的东西很难找到。首先注意在 stage SB 和 job JB 的条件中引用变量 hasArtifacts 的两种不同语法。我们只在阶段级别使用条件,但我将两者都放在这里是因为很难找到如何将一个阶段中的变量集引用为依赖于它的阶段的条件。这两个条件都有效。

第二个要注意的是变量 $(triggerPipeRunID),它在其余 api 请求的 url 中使用。它定义在阶段之上

trigger: none
pr: none
resources:
  pipelines:
  - pipeline: API-Deployment #Azure DevOps has lots of names for this. Most common seems to be "PipelineIdentifier" see variable below.  Another is less descriptive name is "Alias".
    source: API-Build  #this is the name of the build pipeline as specified in Azure DevOps Pipelines UI
    trigger: 
      branches:
        include: 
        - refs/heads/main

variables:
- name: triggerPipeRunID
  value: $(resources.pipeline.API-Deployment.runID)

这对我们有用。尽管即使 API-Build 未生成工件,它仍会触发发布管道,但会跳过部署阶段并且管道 运行 不会被记录为失败。