管道条件

Azure DevOps Services |Azure DevOps Server |Azure DevOps Server 2022 |Azure DevOps Server 2020

本文介绍允许 Azure Pipelines 阶段、作业或步骤运行的不同条件,以及如何在 YAML 管道定义中设置这些条件。

注意

本文讨论 YAML 管道功能。 对于经典管道,可以在每个任务的控制选项中和发布管道中的作业的附加选项中,指定任务或作业运行的某些条件。

阶段、作业或步骤的运行条件

默认情况下,如果管道作业或阶段不依赖于任何其他作业或阶段,或者其所有依赖项都已完成且 成功,则运行该作业或阶段。 依赖项要求适用于直接依赖项及其间接依赖项,以递归方式计算。

默认情况下,如果作业中的任何步骤尚未失败,且前一步骤已经完成,则该步骤将运行。 有关阶段、作业和步骤的更多上下文,请参阅 Azure Pipelines 的关键概念

你可以通过设置阶段、作业或步骤来替代或自定义这些默认行为,即使以前的依赖项失败或有另一个结果也是如此。 还可以定义 自定义条件。 在 YAML 管道定义中,可以使用 condition 该属性指定阶段、作业或步骤可以运行的条件。

注意

条件适用于具有相同代理池的所有以前的直接和间接依赖项。 不同代理池中的阶段或作业同时运行。

基于以前的依赖项状态的条件包括:

  • 成功:仅当所有以前的依赖项都成功时运行。 如果未在 YAML 中设置任何条件,则此行为是默认值。 若要应用此条件,请指定 condition: succeeded()
  • 成功或失败:即使以前的依赖项失败,也运行,除非取消运行。 若要应用此条件,请指定 condition: succeededOrFailed()
  • 始终:即使以前的依赖项失败,运行也会失败,即使已取消运行。 若要应用此条件,请指定 condition: always()
  • 失败:仅在以前的依赖项失败时运行。 若要应用此条件,请指定 condition: failed()

重要

为阶段、作业或步骤指定 condition 属性时,将覆盖默认条件。 即使取消生成,你的阶段、作业或步骤也可能运行。 确保条件将父阶段或作业的状态考虑在内。

条件示例

以下 YAML 示例演示了和always()failed()条件。 作业 1 中的第一个脚本任务有一个 always 条件,因此即使依赖项失败或生成被取消,它也会运行。 在第二个脚本任务中,exit job1 强制 job1 作业失败。

默认情况下,管道阶段按顺序运行,但作业可以并行运行。 可以使用该 dependsOn 属性显式定义阶段或作业之间的依赖关系。

若要设置依赖于另一个作业结果的作业的条件,请使用 dependsOn 它来定义依赖项。 在以下示例中,由于失败,job2依赖于并运行job1job1

jobs:
- job: job1
  steps:
  - script: echo Hello!
    condition: always() # this step runs even if the build is canceled
  - script: |
      echo "This task will fail."
      exit job1 
- job: job2
  dependsOn: job1
  condition: failed() # this job runs only if job1 fails

注意

还可以使用 Azure Pipelines UI 在父阶段失败时手动运行依赖阶段。 有关详细信息,请参阅 父阶段失败时运行子阶段

自定义条件

如果内置条件不符合你的需求,则可以在 YAML 管道定义中将自定义条件指定为 表达式

代理从最内部的函数开始计算表达式,然后向外执行。 最终结果是一个布尔值,该值确定是否运行阶段、作业或步骤。 有关语法的完整指南,请参阅表达式

重要

通过评估条件来确定是否启动某个阶段、作业或步骤。 因此,在阶段、作业或步骤的运行时未计算任何内容可用于同一阶段、作业或步骤。 例如,如果使用具有 $[ ] 语法的运行时表达式在作业中设置变量,则无法在该作业内的条件下使用该变量。

条件中的变量

可以设置管道变量并在条件中使用它们。 以下管道设置一个 isMain 变量,并在仅在生成源分支 main为时运行阶段 B 的条件使用该变量。

variables:
  isMain: $[eq(variables['Build.SourceBranch'], 'refs/heads/main')]

stages:
- stage: A
  jobs:
  - job: A1
    steps:
      - script: echo Hello Stage A!
- stage: B
  condition: and(succeeded(), eq(variables.isMain, true))
  jobs:
  - job: B1
    steps:
      - script: echo Hello Stage B!
      - script: echo $(isMain)

如果变量为 null 或空字符串,则可以设置要运行的条件。 所有变量都被视为 Azure Pipelines 中的字符串,因此空字符串等效于 null 以下管道中:

variables:
- name: testEmpty
  value: ''

jobs:
  - job: A
    steps:
    - script: echo testEmpty is blank
    condition: eq(variables.testEmpty, '')

其他作业条件中使用的作业输出变量

可以在作业中创建一个变量,同一阶段中的其他作业可以在条件中指定。 可用于依赖作业的变量必须使用以下代码标记为isOutput=true

jobs:
- job: A
  steps:
  - bash: |
      echo "This is job A."
      echo "##vso[task.setvariable variable=doThing;isOutput=true]Yes" #set variable doThing to Yes
    name: DetermineResult
- job: B
  dependsOn: A
  condition: eq(dependencies.A.outputs['DetermineResult.doThing'], 'Yes') #map doThing and check the value
  steps:
  - script: echo "Job A ran and doThing is Yes."

后续步骤条件中使用的步骤变量

可以在一个步骤中创建变量,以便同一作业中的后续步骤可以在条件中指定。 默认情况下,从步骤创建的变量可用于作业中未来的步骤,无需标记为多作业输出变量。

在作业中的步骤中创建的变量具有以下限制:

  • 范围限定为同一作业中的步骤。
  • 仅在后续步骤中以 环境变量的形式提供。
  • 不能在定义它们的同一步骤中使用。

以下示例在步骤中创建管道变量,并在后续步骤的脚本条件中使用该变量。

steps:

# This step creates a new pipeline variable: doThing. This variable is available to subsequent steps.
- bash: |
    echo "##vso[task.setvariable variable=doThing]Yes"
  displayName: Step 1

# This step uses doThing in its condition
- script: |
    # Access the variable from Step 1 as an environment variable.
    echo "Value of doThing (as DOTHING env var): $DOTHING."
  displayName: Step 2
  condition: and(succeeded(), eq(variables['doThing'], 'Yes')) # or and(succeeded(), eq(variables.doThing, 'Yes'))

各种结果的条件设置

下表显示了 condition 生成各种所需结果的设置。

所需结果 示例条件设置
如果源分支为 main源分支,即使父级或上一阶段、作业或步骤失败或已取消,也运行。 eq(variables['Build.SourceBranch'], 'refs/heads/main')
如果源分支为 main 父分支或上一阶段、作业或步骤成功,则运行。 and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
如果源分支不是 main,并且父级或上一阶段、作业或步骤成功,则运行。 and(succeeded(), ne(variables['Build.SourceBranch'], 'refs/heads/main'))
如果父级或上一阶段、作业或步骤成功,则为 user 分支运行。 and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/heads/users/'))
如果父级或上一阶段、作业或步骤成功,则为持续集成 (CI) 生成运行。 and(succeeded(), in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI'))
如果拉取请求触发了生成,并且父阶段或上一阶段、作业或步骤失败,则运行。 and(failed(), eq(variables['Build.Reason'], 'PullRequest'))
即使父级或上一阶段、作业或步骤失败或已取消,也为计划生成运行。 eq(variables['Build.Reason'], 'Schedule')
如果变量设置为 />,即使父阶段或上一阶段、作业或步骤失败或已取消,也运行。 eq(variables['System.debug'], true)

注意

Release.Artifacts.{artifact-alias}.SourceBranch 等效于 Build.SourceBranch

取消生成时的条件结果

取消生成并不意味着其所有阶段、作业和步骤都停止运行。 哪些作业、阶段或步骤停止运行取决于指定的条件,以及取消生成管道的执行时间点。 如果跳过阶段、作业或步骤的父级,则无论其条件如何,阶段、作业或步骤都不会运行。

只要一个阶段、作业或步骤的条件评估为 true,它就会运行。 如果某个条件不考虑任务的父级的状态,则即使任务的父级被取消,该任务也可能运行。 若要控制在取消生成时是否运行作业、阶段或步骤,请在条件中包含 作业状态检查功能

如果在队列阶段但尚未运行时取消生成,则会取消整个运行,包括所有其他阶段。

注意

如果任一条件使任务即使在生成取消后也能运行,请指定 一个取消超时 值,该值为取消运行后的任务提供足够的时间。

示例阶段条件结果

以下示例显示了取消生成时在阶段设置的各种条件的结果。

阶段示例 1

在以下管道中,默认情况下 stage2 取决于 stage1 成功完成。 但是,无论状态如何, stage2 都有一个 condition 设置为在源分支 main处于运行状态时 stage1 运行。

如果在 main 分支上排队构建,然后在 stage1 仍在运行时取消构建,那么 stage2 仍会运行,因为 eq(variables['Build.SourceBranch'], 'refs/heads/main') 的计算结果为 true

stages:
- stage: stage1
  jobs:
  - job: A
    steps:
      - script: echo 1; sleep 30
- stage: stage2
  condition: eq(variables['Build.SourceBranch'], 'refs/heads/main')
  jobs:
  - job: B
    steps:
      - script: echo 2

阶段示例 2

在以下管道中,默认情况下 stage2 取决于 stage1 成功完成。 每当源分支为B时,作业中的stage2condition作业main都设置为运行。

如果在分支上 main 排队生成并在运行时取消生成 stage1stage2 并且其作业根本不运行,即使该阶段包含其条件的计算结果为 true的作业也是如此。

stages:
- stage: stage1
  jobs:
  - job: A
    steps:
      - script: echo 1; sleep 30
- stage: stage2
  jobs:
  - job: B
    condition: eq(variables['Build.SourceBranch'], 'refs/heads/main')
    steps:
      - script: echo 2

阶段示例 3

在以下管道中,默认情况下 stage2 取决于 stage1 成功完成。 每当源分支为B时,作业内的stage2condition步骤都有一main组要运行的步骤。

如果在分支上main排队生成并在运行时取消生成stage1stage2并且作业根本不运行,即使作业BB包含其条件计算结果为true的步骤也是如此。 Stage2 已完全跳过,因为 stage1 已取消。

stages:
- stage: stage1
  jobs:
  - job: A
    steps:
      - script: echo 1; sleep 30
- stage: stage2
  jobs:
  - job: B
    steps:
      - script: echo 2
        condition: eq(variables['Build.SourceBranch'], 'refs/heads/main')

示例作业条件结果

以下示例显示了在取消生成时针对作业设置的各种条件的结果。

作业示例 1

在以下 YAML 管道中,正在运行的作业 B 取决于正在运行的作业 A 。 每当源分支为B时,作业condition也具有一个main要运行的集合。

如果你在 main 分支上将某个构建排队,并且在作业 A 仍在运行时取消该构建,则作业 B 仍会运行,因为 condition: eq(variables['Build.SourceBranch'], 'refs/heads/main') 的计算结果为 true

jobs:
- job: A
  steps:
  - script: sleep 30
- job: B
  dependsOn: A 
  condition: eq(variables['Build.SourceBranch'], 'refs/heads/main')
  steps:
    - script: echo step 2.1

如果希望仅当作业成功且生成源为B时运行作业Amain,则必须将它conditionand(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))设置为 。

作业示例 2

在以下 YAML 管道中,作业 B 依赖于作业 A 成功。 每当作业B成功且生成源分支为condition时,作业A都有一组main要运行。

如果在分支上 main 排队生成并在作业 A 运行时取消它,则作业 B 不会运行,即使作业的 condition 计算结果为 true。 作业 B 条件的计算结果 false 为:作业 A 未成功。 因此,将跳过作业 B 及其步骤。

jobs:
- job: A
  steps:
  - script: sleep 30
- job: B
  dependsOn: A 
  steps:
    - script: echo step 2.1
  condition: and(eq(variables['Build.SourceBranch'], 'refs/heads/main'), succeeded())

示例步骤条件结果

还可以设置步骤的条件。 在以下管道中,每当源分支为 condition 时,步骤 2.3 都有一个要运行的 main 集。

如果你在 main 分支上将生成排队,并在步骤 2.1 或 2.2 运行时将其取消,则步骤 2.3 仍会运行,因为 eq(variables['Build.SourceBranch'], 'refs/heads/main') 计算结果为 true

steps:
  - script: echo step 2.1
  - script: echo step 2.2; sleep 30
  - script: echo step 2.3
    condition: eq(variables['Build.SourceBranch'], 'refs/heads/main')

条件中的参数

可以在条件中使用参数。 参数扩展发生在管道运行之前,并使用文本参数值替换括起来 ${{ }} 的值。 由于参数扩展发生在条件评估之前,因此可以在管道中声明参数,并将参数嵌入该管道中的任何条件中。

condition以下示例中合并了两个函数:succeeded()${{ eq(parameters.doThing, true) }}succeeded() 函数检查上一步是否成功。 如果没有上一步, true 此函数也会返回。

${{ eq(parameters.doThing, true) }}函数检查 doThing 参数是否等于 true。 以下示例中的脚本步骤会运行,因为没有上一步, parameters.doThing 默认情况下是 true

parameters:
- name: doThing
  default: true
  type: boolean

steps:
- script: echo I did a thing
  condition: and(succeeded(), ${{ eq(parameters.doThing, true) }})

条件中的模板参数

将参数传递到管道模板时,可以在模板文件中设置参数的值 ,或使用 templateContext 将参数传递给模板

以下parameters.yml模板文件使用默认值doThing声明true参数,并在作业条件中使用参数。

# parameters.yml
parameters:
- name: doThing
  default: true
  type: boolean

jobs:
  - job: B
    steps:
    - script: echo I did a thing
    condition: ${{ eq(parameters.doThing, true) }}

以下 azure-pipelines.yml 管道定义引用 parameters.yml 模板文件中的作业。 管道的输出是因为 I did a thing 参数 doThing 默认为 true。

# azure-pipelines.yml

extends:
  template: parameters.yml

有关更多模板参数示例,请参阅模板使用情况参考

FAQ

如果前一个作业成功但有问题,如何触发作业?

可以在条件中使用上一个作业的结果。 在以下 YAML 中,条件 eq(dependencies.A.result,'SucceededWithIssues') 设置作业 B 在作业 A 成功后运行并出现问题。

jobs:
- job: A
  steps:
  - script: echo Job A ran
- job: B
  dependsOn: A
  condition: eq(dependencies.A.result,'SucceededWithIssues') # targets the result of the previous job 
  steps:
  - script: echo Job A had issues

为什么在取消生成后仍在运行?

如果在阶段中配置的条件不包含作业状态检查函数,则会遇到此问题。 要解决此问题,请向条件中添加作业状态检查函数。 有关详细信息,请参阅 取消生成时的条件结果