管道条件
Azure DevOps Services | Azure DevOps Server 2022 - Azure DevOps Server 2019
本文介绍 Azure Pipelines 阶段、作业或步骤运行的条件,以及如何指定不同的条件。 有关阶段、作业和步骤的更多上下文,请参阅 Azure Pipelines 的关键概念。
默认情况下,如果作业或阶段不依赖于任何其他作业或阶段,或者其所有依赖项都已完成且成功,则运行该作业或阶段。 此要求不仅适用于直接依赖项,也适用于以递归方式计算的间接依赖项。
默认情况下,如果作业中没有任何内容失败,并且紧靠在作业完成前的步骤,则步骤将运行。
可以强制运行阶段、作业或步骤(即使以前的依赖项失败)或通过指定自定义条件来替代或自定义此行为。
注意
本文讨论 YAML 管道功能。 对于经典管道,可以指定任务或作业在每个任务的“控制选项”和发布管道中作业的其他选项中运行的某些条件。
阶段、作业或步骤运行的条件
在管道定义 YAML 中,可以指定运行阶段、作业或步骤的以下条件:
仅当具有相同代理池的所有以前的直接和间接依赖项都成功时。 如果代理池不同,这些阶段或作业会同时运行。 如果未在 YAML 中设置任何条件,则此条件为默认值。
即使以前的依赖项失败,除非取消运行。 将 YAML 中的
succeededOrFailed()
用于此条件。即使以前的依赖项失败,即使已取消运行也是如此。 将 YAML 中的
always()
用于此条件。仅当以前的依赖项失败时。 将 YAML 中的
failed()
用于此条件。
- 自定义条件。
默认情况下,如果所有直接和间接依赖项都成功,则阶段、作业和步骤将运行。 此状态与指定 condition: succeeded()
相同。 有关详细信息,请参阅 成功的状态函数。
为阶段、作业或步骤指定 condition
属性时,将覆盖默认值 condition: succeeded()
。 指定自己的条件可能会导致阶段、作业或步骤运行,即使取消生成也是如此。 请确保写入的条件考虑到父阶段或作业的状态。
以下 YAML 示例显示了和always()
failed()
条件。 即使依赖项失败或生成被取消,第一个作业中的步骤也会运行。 仅当第一个作业失败时,第二个作业才会运行。
jobs:
- job: Foo
steps:
- script: echo Hello!
condition: always() # this step runs, even if the build is canceled
- job: Bar
dependsOn: Foo
condition: failed() # this job runs only if Foo fails
还可以在条件中设置和使用变量。 以下示例设置并使用变量 isMain
来指定 main
为 Build.SourceBranch
.
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)
重要
评估条件以确定是启动阶段、作业还是步骤。 因此,该工作单元内的运行时未计算出任何内容。 例如,如果作业使用具有语法的运行时表达式 $[ ]
设置变量,则不能在该作业的自定义条件中使用该变量。
自定义条件
如果内置条件不符合你的需求,则可以指定 自定义条件。 在 YAML 管道定义中将条件编写为表达式。
代理计算表达式的开头是最内部的函数,然后向外继续。 最终结果是一个布尔值,该值确定是否应运行任务、作业或阶段。 有关语法的完整指南,请参阅 表达式。
如果任一条件使任务即使在取消生成后也能运行,请指定一个合理的取消超时值,以便这些任务在用户取消运行后有足够的时间完成。
取消生成时的条件结果
取消生成并不意味着其所有阶段、作业或步骤都停止运行。 哪些阶段、作业或步骤停止运行取决于指定的条件,以及取消生成管道的执行时间点。 如果跳过阶段、作业或步骤的父级,则任务不会运行,而不考虑其条件。
每当阶段、作业或步骤的条件计算结果为 true
时,都会运行。 如果条件不考虑任务的父级的状态,即使任务父级被取消,任务也可能运行。 若要控制在取消生成时运行的阶段、作业或步骤,请确保 在条件中包含作业状态检查函数 。
以下示例显示了在取消生成时在阶段、作业或步骤上设置的各种条件的结果。
阶段示例 1
在以下管道中,默认情况下stage2
取决于stage1
,但无论condition
状态如何stage1
,只要源分支为main
源分支,就会stage2
设置运行。
如果在分支上 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
该管道。 stage2
作业B
已设置。condition
如果在分支上 main
排队生成并在运行时取消生成 stage1
, stage2
则不会运行,即使它包含其条件计算结果为 true
的作业也是如此。
这是因为 stage2
具有默认的 condition: succeeded()
,而在 stage1
被取消时,后者的计算结果为 false
。 因此,stage2
将被跳过,并且其作业都不会运行。
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
步骤有一个 condition
集。
如果在分支上 main
排队生成并在运行时取消生成 stage1
, stage2
则不会运行,即使它包含作业 B
中的步骤,其条件的计算结果为 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
,但每当源分支为main
时,作业B
都设置为condition
运行。 如果在分支上 main
对生成进行排队,并在作业 A
运行时取消生成,则作业 B
仍会运行,因为 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
A
,condition
则应是 and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
。main
作业示例 2
在以下管道中,作业 B
默认依赖于作业 A
。 如果在分支上 main
排队生成并在作业 A
运行时取消生成,则作业 B
不会运行,即使其步骤 condition
的计算结果为 true
。
这是因为作业 B
具有默认的 condition: succeeded()
,而在作业 A
被取消时,后者的计算结果为 false
。 因此,将跳过作业 B
,并且不会运行任何步骤。
jobs:
- job: A
steps:
- script: sleep 30
- job: B
dependsOn: A
steps:
- script: echo step 2.1
condition: eq(variables['Build.SourceBranch'], 'refs/heads/main', succeeded())
步骤示例
还可以对步骤施加条件。
在以下管道中,每当源分支为main
时,步骤 2.3 都有一个condition
要运行的集合。 如果在分支上 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
设置。
注意
Release.Artifacts.{artifact-alias}.SourceBranch
等效于 Build.SourceBranch
。
所需结果 | 示例条件设置 |
---|---|
如果源分支是主分支,即使父级或上一阶段、作业或步骤失败或已取消,也运行。 | eq(variables['Build.SourceBranch'], 'refs/heads/main') |
如果源分支是主分支,并且父级或上一阶段、作业或步骤成功,则运行。 | and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main')) |
如果源分支不是主分支,并且父分支或上一阶段、作业或步骤成功,则运行。 | and(succeeded(), ne(variables['Build.SourceBranch'], 'refs/heads/main')) |
如果父级或上一阶段、作业或步骤成功,则为用户主题分支运行。 | 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') |
如果变量设置为 true,则运行,即使父阶段或上一阶段、作业或步骤失败或已取消。 | eq(variables['System.debug'], true) |
注意
如果变量为 null(空字符串),则可以设置要运行的条件。 由于所有变量都被视为 Azure Pipelines 中的字符串,因此空字符串等效于 null
以下管道中:
variables:
- name: testEmpty
value: ''
jobs:
- job: A
steps:
- script: echo testEmpty is blank
condition: eq(variables.testEmpty, '')
条件中的参数
参数扩展在考虑条件之前发生。 因此,在与条件相同的管道中声明参数时,可以将参数嵌入条件中。 以下 YAML 中的脚本运行是因为 parameters.doThing
true。
parameters:
- name: doThing
default: true
type: boolean
steps:
- script: echo I did a thing
condition: and(succeeded(), ${{ eq(parameters.doThing, true) }})
前面的 condition
管道中合并了两个函数: succeeded()
和 eq('${{ parameters.doThing }}', true)
。 succeeded()
函数检查上一步是否成功。 该 succeeded()
函数返回是因为 true
没有上一步。
该 eq('${{ parameters.doThing }}', true)
函数检查参数是否 doThing
等于 true
。 由于默认值为 true
<true
,除非管道设置其他值。
条件中的模板参数
将参数传递给模板时,需要在模板中设置参数的值,或使用 templateContext 将参数传递给模板。
例如,以下 parameters.yml 文件声明 doThing
参数和默认值:
# parameters.yml
parameters:
- name: doThing
default: true # value passed to the condition
type: boolean
jobs:
- job: B
steps:
- script: echo I did a thing
condition: ${{ eq(parameters.doThing, true) }}
管道代码引用 parameters.yml 模板。 管道的输出是因为 I did a thing
参数 doThing
为 true。
# azure-pipeline.yml
parameters:
- name: doThing
default: true
type: boolean
trigger:
- none
extends:
template: parameters.yml
有关更多模板参数示例,请参阅 模板使用情况参考。
后续作业条件中使用的作业输出变量
可以将变量提供给将来的作业,并在条件中指定此变量。 可用于将来作业的变量必须使用以下isOutput=true
代码标记为多作业输出变量:
jobs:
- job: Foo
steps:
- bash: |
echo "This is job Foo."
echo "##vso[task.setvariable variable=doThing;isOutput=true]Yes" #set variable doThing to Yes
name: DetermineResult
- job: Bar
dependsOn: Foo
condition: eq(dependencies.Foo.outputs['DetermineResult.doThing'], 'Yes') #map doThing and check the value
steps:
- script: echo "Job Foo 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 is able to use doThing, so it 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'))
常见问题解答
如果以前的作业成功出现问题,如何触发作业?
可以在条件中使用上一个作业的结果。 例如,在以下 YAML 中,条件 eq(dependencies.A.result,'SucceededWithIssues')
允许作业 B
运行,因为作业 A
成功并出现问题。
jobs:
- job: A
displayName: Job A
continueOnError: true # next job starts even if this one fails
steps:
- script: echo Job A ran
- script: exit 1
- job: B
dependsOn: A
condition: eq(dependencies.A.result,'SucceededWithIssues') # targets the result of the previous job
displayName: Job B
steps:
- script: echo Job B ran
我取消了我的生成,但它仍在运行。 为什么?
如果阶段中配置的条件不包含 作业状态检查函数,则可能会遇到此问题。 要解决此问题,请向条件中添加作业状态检查函数。
如果在作业处于队列阶段但未运行时取消作业,则会取消整个作业,包括所有其他阶段。 有关详细信息,请参阅 本文前面部分取消 生成时的条件结果。