管道条件

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 来指定 mainBuild.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 排队生成并在运行时取消生成 stage1stage2 则不会运行,即使它包含其条件计算结果为 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 排队生成并在运行时取消生成 stage1stage2 则不会运行,即使它包含作业 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

如果希望仅在作业成功且生成源为分支时运行作业BAcondition则应是 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')
      

步骤示例

还可以对步骤施加条件。

在以下管道中,每当源分支为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: ${{ eq(parameters.doThing, true) }}

前面的 condition 管道中合并了两个函数: succeeded()eq('${{ parameters.doThing }}', true)succeeded() 函数检查上一步是否成功。 该 succeeded() 函数返回是因为 true 没有上一步。

eq('${{ parameters.doThing }}', true) 函数检查参数是否 doThing 等于 true。 由于默认值为 true<a0/a0>,因此条件默认返回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

我取消了我的生成,但它仍在运行。 为什么?

如果阶段中配置的条件不包含 作业状态检查函数,则可能会遇到此问题。 要解决此问题,请向条件中添加作业状态检查函数。

如果在作业处于队列阶段但未运行时取消作业,则会取消整个作业,包括所有其他阶段。 有关详细信息,请参阅 本文前面部分取消 生成时的条件结果。