指定条件

Azure DevOps Services | Azure DevOps Server 2022 - Azure DevOps Server 2019

可以指定每个阶段、作业或步骤运行的条件。 默认情况下,如果作业或阶段不依赖于任何其他作业或阶段,或者它们所依赖的所有作业或阶段都已成功完成,它们就会运行。 这不仅包括直接依赖项,还包括以递归方式计算的依赖项。 默认情况下,如果步骤的作业中没有任何内容失败并且紧接在其前面的步骤已完成,则该步骤将运行。 可以通过强制阶段、作业或步骤运行(即使先前的依赖项失败)或通过指定自定义条件来自定义此行为。

可以指定步骤、作业或阶段运行的条件。

  • 仅当具有相同代理池的所有先前直接和间接依赖项都成功时。 如果有不同的代理池,这些阶段或作业将并发运行。 如果 YAML 中没有设置条件,则这是默认值。

  • 即使以前的依赖项失败,除非运行已取消。 将 YAML 中的 succeededOrFailed() 用于此条件。

  • 即使以前的依赖项失败,即使运行已取消。 将 YAML 中的 always() 用于此条件。

  • 仅当以前的依赖项失败时。 将 YAML 中的 failed() 用于此条件。

  • 自定义条件

默认情况下,如果所有直接和间接依赖项都成功,将运行步骤、作业和阶段。 就如同是指定了“condition: succeeded()”(请参阅成功状态函数)。

jobs:
- job: Foo

  steps:
  - script: echo Hello!
    condition: always() # this step will always run, even if the pipeline is canceled

- job: Bar
  dependsOn: Foo
  condition: failed() # this job will only run if Foo fails

还可以在条件中使用变量。

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)

评估条件以决定是启动阶段、作业还是步骤。 这意味着,在该工作单元内运行时计算的内容将不可用。 例如,如果你有一个作业,该作业根据 $[ ] 语法使用运行时表达式来设置变量,那么你无法在自定义条件中使用该变量。

TFS 不支持 YAML。

注意

为阶段/作业/步骤指定自己的 condition 属性时,将覆盖其默认 condition: succeeded()。 这可能会导致阶段/作业/步骤始终会运行,即使已取消了生成。 在编写自己的条件时,请确保考虑到父阶段/作业的状态。

启用自定义条件

如果内置条件不能满足你的需求,可以指定自定义条件。

在 YAML 管道中,可以将条件写成表达式。 代理从最里面的函数开始计算表达式并计算其结果。最终结果是一个布尔值,用于确定任务、作业或阶段是否应该运行。 有关语法的完整指南,请参阅表达式一文。

你的任何条件是否使任务即使在用户取消生成后也可以运行? 如果是,那么为取消超时指定一个合理的值,以便在用户取消运行后有足够的时间完成这些类型的任务。

取消生成后的管道行为

取消生成时,并不意味着其所有阶段、作业或步骤都停止运行。 该决策取决于指定的阶段、作业或步骤conditions,以及在你取消生成时管道执行的进度。

如果你的条件不考虑阶段/作业/步骤的父级的状态,而且条件的计算结果为 true,则你的阶段、作业或步骤将运行(即使其父级被取消)。 如果跳过其父级,则阶段、作业或步骤不会运行。

让我们探讨一些示例。

默认情况下,在此管道中,stage2 依赖于 stage1,并且 stage2 设置了一个 condition。 仅当源分支为 mainstage2 才会运行。

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

如果在 main 分支上将某个生成进行排队,并且你在 stage1 仍在运行时取消了此生成,则 stage2 仍会运行,因为 eq(variables['Build.SourceBranch'], 'refs/heads/main') 的计算结果为 true

在此管道中,stage2 依赖于 stage1。 作业 B 已经为其设置了一个 condition

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

如果在 main 分支上将某个生成进行排队,并且你在 stage1 仍在运行的情况下取消了此生成,则 stage2 将不会运行,即使它包含的某个作业 B 的条件计算结果为 true。 这是因为 stage2 具有默认的 condition: succeeded(),而在 stage1 被取消时,后者的计算结果为 false。 因此,stage2 将被跳过,并且其作业都不会运行。

假设你有以下 YAML 管道。 请注意,默认情况下,stage2依赖于 stage1,而且 script: echo 2 为其设置了 condition

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')

如果在 main 分支上将某个生成进行排队,并且你在 stage1 仍在运行的情况下取消了此生成,则 stage2 将不会运行,即使它包含的作业 B 中的某一步骤的条件计算结果为 true。 原因是跳过了 stage2 以响应 stage1 被取消的操作。

若要防止在生成被取消时运行具有 conditions 的阶段、作业或步骤,请确保在编写 conditions 时考虑其父级的状态。 有关详细信息,请参阅作业状态函数

示例

针对主分支运行,即使已取消,即使失败

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')

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

如果变量设置为 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。

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

函数 eq('${{ parameters.doThing }}', true) 检查 doThing 参数是否等于 true。 由于 doThing 的默认值为 true,因此默认情况下,该条件将返回 true,除非在管道中设置了其他值。

有关更多模板参数示例,请参阅模板类型和用法

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

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

在你将参数传递给模板时,需要在模板中设置参数的值,或使用 templateContext 将属性传递给模板

# 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) }}
# azure-pipeline.yml
parameters:
- name: doThing
  default: true 
  type: boolean

trigger:
- none

extends:
  template: parameters.yml

此管道的输出是 I did a thing,因为参数 doThing 为 true。

在后续作业中使用作业的输出变量

可以将变量提供给将来的作业,并在条件中指定此变量。 必须使用 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."

在后续步骤中使用通过条件中的步骤创建的管道变量

可以将变量提供给将来的步骤,并在条件中指定此变量。 默认情况下,从步骤创建的变量可用于将来的步骤,并且不需要使用 isOutput=true 将其标记为多作业输出变量

有关上述方法和作用域,需要注意一些重要事项:

  • 在作业的步骤中创建的变量的作用域将限定为同一作业中的步骤。
  • 在步骤中创建的变量仅在后续步骤中作为环境变量提供。
  • 在某一步骤中创建的变量不能在定义这些变量的步骤中使用。

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

steps:

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

# This step is able to use doThing, so it uses it in its condition
- script: |
    # You can 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') 允许作业运行,因为作业 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

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

如果在阶段中配置的条件不包含作业状态检查函数,则会遇到此问题。 要解决此问题,请向条件中添加作业状态检查函数。 如果某个作业在队列中但未在运行,而此时你取消此作业,则会取消整个作业,包括所有其他阶段。

详细了解取消生成时管道的行为