表达式

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

重要

从 Azure DevOps 内容版本选择器中选择一个版本。

选择与平台和版本相对应的本文的版本。 版本选择器位于目录上方。 查找 Azure DevOps 平台和版本

表达式可用于许多在创作管道时需要指定字符串、布尔值或数字值的位置。 如果表达式返回数组,则应用普通索引规则,且索引以 0 开头。

表达式的最常见用途是在确定是否应运行作业或步骤的条件中。

# Expressions are used to define conditions for a step, job, or stage
steps:
- task: ...
  condition: <expression>

表达式的另一个常见用途是定义变量。 表达式可以在编译时运行时进行求值。 编译时表达式可在任意位置使用;运行时表达式可用于变量和条件。 运行时表达式适合作为计算变量和状态的内容的方式(示例:condition)。

# Two examples of expressions used to define variables
# The first one, a, is evaluated when the YAML file is compiled into a plan.
# The second one, b, is evaluated at runtime.
# Note the syntax ${{}} for compile time and $[] for runtime expressions.
variables:
  a: ${{ <expression> }}
  b: $[ <expression> ]

运行时和编译时表达式语法之间的区别主要在于提供了哪些上下文。 在编译时表达式 (${{ <expression> }}) 中,可以访问 parameters 和静态定义的 variables。 在运行时表达式 ($[ <expression> ]) 中,可以访问更多 variables,但无法访问参数。

在此示例中,运行时表达式设置 $(isMain) 的值。 编译表达式中的静态变量设置 $(compileVar) 的值。

variables:
  staticVar: 'my value' # static variable
  compileVar: ${{ variables.staticVar }} # compile time expression
  isMain: $[eq(variables['Build.SourceBranch'], 'refs/heads/main')] # runtime expression

steps:
  - script: |
      echo ${{variables.staticVar}} # outputs my value
      echo $(compileVar) # outputs my value
      echo $(isMain) # outputs True

表达式可以是文本、对变量的引用、对依赖项的引用、函数或它们的有效嵌套组合。

文本

作为表达式的一部分,可以使用布尔值、null、数字、字符串或版本文本。

# Examples
variables:
  someBoolean: ${{ true }} # case insensitive, so True or TRUE also works
  someNumber: ${{ -1.2 }}
  someString: ${{ 'a b c' }}
  someVersion: ${{ 1.2.3 }}

Boolean

TrueFalse 是布尔文本表达式。

Null

Null 是一种从字典未命中返回的特殊文本表达式,例如 (variables['noSuch'])。 Null 可以是表达式的输出,但不能在表达式中直接调用。

Number

以“-”、“.”或“0”到“9”开头。

String

必须带单引号。 例如:'this is a string'

若要表达文本单引号,请使用单引号对其进行转义。 例如:'It''s OK if they''re using contractions.'

可以对多行字符串使用管道字符 (|)。

myKey: |
  one
  two
  three

版本

最多包含四个段的版本号。 必须以数字开头,并包含两个或三个句点 (.) 字符。 例如:1.2.3.4

变量

作为表达式的一部分,你可以使用以下两种语法之一访问变量:

  • 索引语法:variables['MyVar']
  • 属性取消引用语法:variables.MyVar

要使用属性解除参考语法,属性名称必须:

  • a-Z_ 开头
  • 后跟a-Z0-9_

根据执行上下文,可以使用不同的变量。

  • 如果使用 YAML 创建管道,则可以使用管道变量
  • 如果使用经典编辑器创建生成管道,则可以使用生成变量
  • 如果使用经典编辑器创建发布管道,则可以使用发布变量

变量始终为字符串。 如果要使用类型化值,则应改用参数

注意

通过变量选项卡 UI 设置此类变量时,将变量与经典管道和 YAML 管道的表达式结合使用是有限制的。 定义为表达式的变量不应依赖于值中具有表达式的另一个变量,因为不能保证这两个表达式都将得到正确计算。 例如,我们有一个变量 a,其值 $[ <expression> ] 用作变量 b 的值的一部分。 由于无法保证变量的处理顺序,因此变量 b 在计算后可能具有不正确的变量 a 值。

仅当通过 YAML 管道中的变量关键字设置变量时,才允许使用描述的构造。 需要按处理变量的顺序放置变量,以便在处理后获取正确的值。

函数

可在表达式中使用以下内置函数。

  • 如果所有参数都为 True,则计算结果为 True
  • 最小参数:2。 最大参数:N
  • 将参数强制转换为布尔值以进行计算
  • 第一个 False 之后的短路
  • 示例:and(eq(variables.letters, 'ABC'), eq(variables.numbers, 123))

coalesce

  • 按顺序(从左到右)计算参数,并返回第一个不等于 null 的值或空字符串。
  • 如果参数值均为 null 或空字符串,则不返回任何值。
  • 最小参数:2。 最大参数:N
  • 示例:coalesce(variables.couldBeNull, variables.couldAlsoBeNull, 'literal so it always works')

contains

  • 如果左参数字符串包含右参数,则计算 True
  • 最小参数:2。 最大参数:2
  • 将参数强制转换为字符串以进行计算
  • 执行序号忽略大小写比较
  • 示例:contains('ABCDE', 'BCD')(返回 True)

containsValue

  • 如果左参数是数组,并且任何项等于右参数,则计算 True。 此外,如果左参数是对象,并且任何属性的值等于右参数,则计算 True
  • 最小参数:2。 最大参数:2
  • 如果左参数是数组,请转换每个项以匹配右参数的类型。 如果左参数是对象,请转换每个属性的值以匹配右参数的类型。 如果转换失败,则每个特定项的相等性比较将计算 False
  • 字符串的序号忽略大小写比较
  • 第一个匹配后的短路

注意

YAML 管道中没有用于指定数组的文本语法。 此函数在常规管道中用途有限。 它旨在与系统提供的数组(例如步骤列表)一起在管道修饰器上下文中使用。

可以使用 containsValue 表达式在对象中查找匹配值。 以下示例演示了如何在源分支列表中查找 Build.SourceBranch 的匹配项。

parameters:
- name: branchOptions
  displayName: Source branch options
  type: object
  default:
    - refs/heads/main
    - refs/heads/test

jobs:
  - job: A1 
    steps:
    - ${{ each value in parameters.branchOptions }}:
      - script: echo ${{ value }}

  - job: B1 
    condition: ${{ containsValue(parameters.branchOptions, variables['Build.SourceBranch']) }}
    steps:
      - script: echo "Matching branch found"

convertToJson

  • 获取复杂对象并将其输出为 JSON。
  • 最小参数:1。 最大参数:1。
parameters:
  - name: listOfValues
    type: object
    default:
      this_is:
        a_complex: object
        with:
          - one
          - two

steps:
- script: |
    echo "${MY_JSON}"
  env:
    MY_JSON: ${{ convertToJson(parameters.listOfValues) }}

脚本输出:

{
  "this_is": {
    "a_complex": "object",
    "with": [
      "one",
      "two"
    ]
  }
}

counter

  • 此函数只能在定义变量的表达式中使用。 它不能用作某个步骤、工作或阶段的条件的一部分。
  • 计算一个随着管道的每次运行而递增的数字。
  • 参数:2。 prefixseed
  • 前缀是字符串表达式。 针对前缀的每个唯一值跟踪计数器的单独值。 prefix 应使用 UTF-16 字符。
  • 种子是计数器的起始值

可以创建一个计数器,该计数器在管道的每次执行中自动递增 1。 定义计数器时,提供 prefixseed。 下面的示例演示了这一点。

variables:
  major: 1
  # define minor as a counter with the prefix as variable major, and seed as 100.
  minor: $[counter(variables['major'], 100)]

steps:
- bash: echo $(minor)

在上述示例中,管道第一次运行时 minor 的值为 100。 在第二次运行中,只要 major 的值仍然是 1,它的值就是 101。

如果编辑 YAML 文件,并将变量 major 的值更新为 2,则在管道的下一次运行中,minor 的值将为 100。 后续运行将计数器递增到 101、102、103...

稍后,如果编辑 YAML 文件,并将 major 的值重新设置为 1,则计数器的值将恢复为该前缀的保留位置。 在此示例中,它恢复为 102。

下面是设置变量充当计数器的另一个示例,计数器的起始值为 100,每次运行都递增 1,并且每天都会重置为 100。

注意

pipeline.startTime 在表达式之外不可用。 pipeline.startTimesystem.pipelineStartTime 格式化为日期和时间对象,使其可用于表达式。 pipeline.startTime 的默认时区为 UTC。 可以为组织更改时区

jobs:
- job:
  variables:
    a: $[counter(format('{0:yyyyMMdd}', pipeline.startTime), 100)]
  steps:
  - bash: echo $(a)

以下是计数器的示例,它会为 PR 和 CI 运行维护单独的值。

variables:
  patch: $[counter(variables['build.reason'], 0)]

计数器的范围限定为管道。 换句话说,该管道的每次运行都会递增其值。 没有项目范围内的计数器。

endsWith

  • 如果左参数字符串以右参数结尾,则计算 True
  • 最小参数:2。 最大参数:2
  • 将参数强制转换为字符串以进行计算
  • 执行序号忽略大小写比较
  • 示例:endsWith('ABCDE', 'DE')(返回 True)

eq

  • 如果参数相等,则计算 True
  • 最小参数:2。 最大参数:2
  • 转换右参数以匹配左参数的类型。 如果转换失败,则返回 False
  • 字符串的序号忽略大小写比较
  • 示例:eq(variables.letters, 'ABC')

format

  • 计算尾随参数并将其插入到前导参数字符串中
  • 最小参数:1。 最大参数:N
  • 示例:format('Hello {0} {1}', 'John', 'Doe')
  • .NET 自定义日期和时间格式说明符用于日期格式设置(yyyyyyMMMdddHHHmmmsssfffffffK
  • 示例:format('{0:yyyyMMdd}', pipeline.startTime)。 在这种情况下,pipeline.startTime 是一个特殊的日期时间对象变量。
  • 通过加倍大括号进行转义。 例如: format('literal left brace {{ and literal right brace }}')

ge

  • 如果左参数大于或等于右参数,则计算 True
  • 最小参数:2。 最大参数:2
  • 转换右参数以匹配左参数的类型。 如果转换失败,则出现错误。
  • 字符串的序号忽略大小写比较
  • 示例:ge(5, 5)(返回 True)

gt

  • 如果左参数大于右参数,则计算 True
  • 最小参数:2。 最大参数:2
  • 转换右参数以匹配左参数的类型。 如果转换失败,则出现错误。
  • 字符串的序号忽略大小写比较
  • 示例:gt(5, 2)(返回 True)

in

  • 如果左参数等于右参数,则计算 True
  • 最小参数:1。 最大参数:N
  • 转换右参数以匹配左参数的类型。 如果转换失败,则相等性比较计算 False
  • 字符串的序号忽略大小写比较
  • 第一个匹配之后的短路
  • 示例:in('B', 'A', 'B', 'C')(返回 True)

join

  • 连接右参数数组中的所有元素,用左参数字符串分隔。
  • 最小参数:2。 最大参数:2
  • 数组中的每个元素将转换为字符串。 复杂对象将转换为空字符串。
  • 如果右边的参数不是数组,则右边的参数会被转换为字符串。

在此示例中,在数组中的每个项之间添加分号。 参数类型为对象。

parameters:
- name: myArray
  type: object
  default:
    - FOO
    - BAR
    - ZOO

variables:
   A: ${{ join(';',parameters.myArray) }}

steps:
  - script: echo $A # outputs FOO;BAR;ZOO

le

  • 如果左参数小于或等于右参数,则计算 True
  • 最小参数:2。 最大参数:2
  • 转换右参数以匹配左参数的类型。 如果转换失败,则出现错误。
  • 字符串的序号忽略大小写比较
  • 示例:le(2, 2)(返回 True)

length

  • 返回字符串或数组的长度,任何一个来自系统或参数
  • 最小参数:1。 最大参数 1
  • 示例:length('fabrikam') 返回 8

lower

  • 将字符串或变量值转换为所有小写字符
  • 最小参数:1。 最大参数 1
  • 返回字符串的小写等效形式
  • 示例:lower('FOO') 返回 foo

lt

  • 如果左参数小于右参数,则计算 True
  • 最小参数:2。 最大参数:2
  • 转换右参数以匹配左参数的类型。 如果转换失败,则出现错误。
  • 字符串的序号忽略大小写比较
  • 示例:lt(2, 5)(返回 True)

ne

  • 如果参数不相等,则计算 True
  • 最小参数:2。 最大参数:2
  • 转换右参数以匹配左参数的类型。 如果转换失败,则返回 True
  • 字符串的序号忽略大小写比较
  • 示例:ne(1, 2)(返回 True)

not

  • 如果参数为 False,则计算 True
  • 最小参数:1。 最大参数:1
  • 将值转换为布尔值以进行计算
  • 示例:not(eq(1, 2))(返回 True)

notIn

  • 如果左边的参数不等于右边的任何参数,则对 True 求值
  • 最小参数:1。 最大参数:N
  • 转换右参数以匹配左参数的类型。 如果转换失败,则相等性比较计算 False
  • 字符串的序号忽略大小写比较
  • 第一个匹配之后的短路
  • 示例:notIn('D', 'A', 'B', 'C')(返回 True)

  • 如果任何参数为 True,则计算 True
  • 最小参数:2。 最大参数:N
  • 将参数强制转换为布尔值以进行计算
  • 第一个 True 之后的短路
  • 示例:or(eq(1, 1), eq(2, 3))(返回 True,短路)

replace

  • 返回一个新字符串,其中当前实例中字符串的所有实例都替换为另一个字符串
  • 最小参数:3。 最大参数:3
  • replace(a, b, c):返回 a,并将 b 的所有实例替换为 c
  • 示例:replace('https://www.tinfoilsecurity.com/saml/consume','https://www.tinfoilsecurity.com','http://server')(返回 http://server/saml/consume

split

  • 根据指定的分隔字符将字符串拆分为子字符串
  • 最小参数:2。 最大参数:2
  • 第一个参数是要拆分的字符串
  • 第二个参数是分隔符
  • 返回子字符串数组。 当分隔符连续出现或在字符串末尾时,数组包含空字符串
  • 示例:
    variables:
    - name: environments
      value: prod1,prod2 
    steps:  
      - ${{ each env in split(variables.environments, ',')}}:
        - script: ./deploy.sh --environment ${{ env }}
    
  • 将 split() 与 replace() 配合使用的示例:
    parameters:
    - name: resourceIds
      type: object
      default:
      - /subscriptions/mysubscription/resourceGroups/myResourceGroup/providers/Microsoft.Network/loadBalancers/kubernetes-internal
      - /subscriptions/mysubscription02/resourceGroups/myResourceGroup02/providers/Microsoft.Network/loadBalancers/kubernetes
    - name: environments
      type: object
      default: 
      - prod1
      - prod2
    
    trigger:
    - main
    
    steps:
    - ${{ each env in parameters.environments }}:
      - ${{ each resourceId in parameters.resourceIds }}:
          - script: echo ${{ replace(split(resourceId, '/')[8], '-', '_') }}_${{ env }}
    

startsWith

  • 如果左参数字符串以右参数开头,则计算 True
  • 最小参数:2。 最大参数:2
  • 将参数强制转换为字符串以进行计算
  • 执行序号忽略大小写比较
  • 示例:startsWith('ABCDE', 'AB')(返回 True)

upper

  • 将字符串或变量值转换为所有大写字符
  • 最小参数:1。 最大参数 1
  • 返回字符串的大写等效形式
  • 示例:upper('bah') 返回 BAH

xor

  • 如果正好一个参数为 True,则计算 True
  • 最小参数:2。 最大参数:2
  • 将参数强制转换为布尔值以进行计算
  • 示例:xor(True, False)(返回 True)

作业状态检查函数

可以将以下状态检查函数用作条件中的表达式,但不能用作变量定义中的表达式。

通用

  • 计算结果始终为 True(即使取消时)。 注意:严重故障仍可能阻止任务运行。 例如,如果获取来源失败。

canceled

  • 如果取消了管道,则计算结果为 True

“失败”

  • 对于步骤,等效于 eq(variables['Agent.JobStatus'], 'Failed')
  • 对于作业:
    • 在没有参数的情况下,仅当依赖项关系图中以前的任何作业失败时,计算结果才为 True
    • 在作业名称作为参数的情况下,仅当这些作业中的任何一个失败时,计算结果才为 True

succeeded

  • 对于步骤,等效于 in(variables['Agent.JobStatus'], 'Succeeded', 'SucceededWithIssues')
  • 在处理作业时与 dependsOn 一起使用,并且你想要评估以前的作业是否成功。 作业设计为并行运行,而阶段按顺序运行。
  • 对于作业:
    • 在没有参数的情况下,仅当依赖项关系图中以前的所有作业都成功或部分成功时,计算结果才为 True
    • 在作业名称作为参数的情况下,如果所有这些作业都成功或部分成功,则计算结果是 True
    • 如果取消了管道,则计算结果为 False

succeededOrFailed

  • 对于步骤,等效于 in(variables['Agent.JobStatus'], 'Succeeded', 'SucceededWithIssues', 'Failed')

  • 对于作业:

    • 在没有参数的情况下,无论依赖项关系图中的任何作业是成功还是失败,计算结果都为 True
    • 在作业名称作为参数的情况下,无论这些作业中的任何一个是成功还是失败,计算结果都为 True
    • 当依赖项关系图中存在以前跳过的作业时,可能需要改用 not(canceled())

    这类似于 always(),不同之处在于它会在取消管道时计算 False

有条件插入

可以使用 ifelseifelse 子句有条件地分配变量值或设置任务的输入。 还可以在满足条件时有条件地运行步骤。

可以使用 if 有条件地分配变量值或设置任务的输入。 还可以在满足条件时有条件地运行步骤。

elseifelse 子句从 Azure DevOps 2022 开始可用,不适用于 Azure DevOps Server 2020 和早期版本的 Azure DevOps。

仅当使用模板语法时,条件才起作用。 详细了解变量语法

对于模板,可以在添加序列或映射时使用条件插入。 详细了解模板中的条件插入

有条件地分配变量

variables:
  ${{ if eq(variables['Build.SourceBranchName'], 'main') }}: # only works if you have a main branch
    stageName: prod

pool:
  vmImage: 'ubuntu-latest'

steps:
- script: echo ${{variables.stageName}}

有条件地设置任务输入

pool:
  vmImage: 'ubuntu-latest'

steps:
- task: PublishPipelineArtifact@1
  inputs:
    targetPath: '$(Pipeline.Workspace)'
    ${{ if eq(variables['Build.SourceBranchName'], 'main') }}:
      artifact: 'prod'
    ${{ else }}:
      artifact: 'dev'
    publishLocation: 'pipeline'

有条件地运行步骤

如果没有设置变量,或者 foo 的值与 if 条件不匹配,则运行 else 语句。 此处,foo 的值在 elseif 条件中返回 true。

variables:
  - name: foo
    value: contoso # triggers elseif condition

pool:
  vmImage: 'ubuntu-latest'

steps:
- script: echo "start"
- ${{ if eq(variables.foo, 'adaptum') }}:
  - script: echo "this is adaptum"
- ${{ elseif eq(variables.foo, 'contoso') }}: # true
  - script: echo "this is contoso" 
- ${{ else }}:
  - script: echo "the value is not adaptum or contoso"

Each 关键字

可以使用 each 关键字循环访问具有对象类型的参数。

parameters:
- name: listOfStrings
  type: object
  default:
  - one
  - two

steps:
- ${{ each value in parameters.listOfStrings }}:
  - script: echo ${{ value }}

此外,还可以循环访问对象中的嵌套元素。

parameters:
- name: listOfFruits
  type: object
  default:
  - fruitName: 'apple'
    colors: ['red','green']
  - fruitName: 'lemon'
    colors: ['yellow']
steps:
- ${{ each fruit in parameters.listOfFruits }} :
  - ${{ each fruitColor in fruit.colors}} :
    - script: echo ${{ fruit.fruitName}} ${{ fruitColor }}

依赖项

表达式可以使用依赖项上下文来引用以前的作业或阶段。 可以使用依赖项来:

  • 引用上一个作业的作业状态
  • 引用上一个阶段的阶段状态
  • 引用同一阶段的上一个作业中的输出变量
  • 引用某个阶段的上一个阶段中的输出变量
  • 引用以下阶段的上一个阶段的某个作业中的输出变量

上下文针对作业和阶段称为 dependencies,其工作原理与变量非常类似。 如果引用另一个阶段的某个作业中的输出变量,则上下文称为 stageDependencies

如果在输出变量中包含引号字符('")时遇到问题,请参阅此故障排除指南

依赖项语法概述

引用具有依赖项的输出变量的语法因具体情况而异。 以下是最常见应用场景的概述。 有时候,其他语法也可能有效。

类型

描述

引用条件 stages 中不同阶段某个作业先前阶段的输出变量。

  • 语法: and(succeeded(), eq(stageDependencies.<stage-name>.outputs['<job-name>.<step-name>.<variable-name>'], 'true'))
  • 示例: and(succeeded(), eq(stageDependencies.A.outputs['A1.printvar.shouldrun'], 'true'))

引用 stages 中同一阶段不同作业中的输出变量。

  • 语法: and(succeeded(), eq(dependencies.<job-name>.outputs['<step-name>.<variable-name>'], 'true'))
  • 示例: and(succeeded(), eq(dependencies.A.outputs['printvar.shouldrun'], 'true'))

引用 job 中不同阶段的输出变量。

  • 语法: eq(stageDependencies.<stage-name>.<job-name>.outputs['<step-name>.<variable-name>'], 'true')
  • 示例: eq(stageDependencies.A.A1.outputs['printvar.shouldrun'], 'true')

引用 stages 中不同阶段部署作业的输出变量。

  • 语法: eq(dependencies.<stage-name>.outputs['<deployment-job-name>.<deployment-job-name>.<step-name>.<variable-name>'], 'true')
  • 示例: eq(dependencies.build.outputs['build_job.build_job.setRunTests.runTests'], 'true')

阶段与阶段之间的依赖项(包含资源的部署作业)

引用 stages 中不同阶段包含资源的部署作业中的输出变量。

  • 语法: eq(dependencies.<stage-name>.outputs['<deployment-job-name>.<Deploy_resource-name>.<step-name>.<variable-name>'], 'true')
  • 示例: eq(dependencies.build.outputs['build_job.Deploy_winVM.setRunTests.runTests'], 'true')

部署作业中的输出变量也有不同的语法,具体取决于部署策略。 有关更多信息,请参阅部署作业

阶段到阶段依赖项

在结构上,dependencies 对象是作业和阶段名称到 resultsoutputs 的映射。 表示为 JSON,如下所示:

"dependencies": {
  "<STAGE_NAME>" : {
    "result": "Succeeded|SucceededWithIssues|Skipped|Failed|Canceled",
    "outputs": {
        "jobName.stepName.variableName": "value"
    }
  },
  "...": {
    // another stage
  }
}

注意

以下示例使用标准管道语法。 如果使用部署管道,变量和条件变量语法将有所不同。 有关要使用的特定语法的信息,请参阅部署作业

使用此形式的 dependencies 在阶段级别的变量或检查条件中映射。

在此示例中,有两个阶段:A 和 B。阶段 A 具有条件 false,因此永远不会运行。 如果阶段 A 的结果为 SucceededSucceededWithIssues 或者 Skipped,则阶段 B 运行。 之所以会运行 B 阶段,是因为跳过了 A 阶段。

stages:
- stage: A
  condition: false
  jobs:
  - job: A1
    steps:
    - script: echo Job A1
- stage: B
  condition: in(dependencies.A.result, 'Succeeded', 'SucceededWithIssues', 'Skipped')
  jobs:
  - job: B1
    steps:
    - script: echo Job B1

阶段还可以使用另一个阶段的输出变量。 在此示例中,也有两个阶段。 阶段 A 包括作业 A1,该作业将输出变量 shouldrun 设置为 trueshouldruntrue 时,阶段 B 运行。 因为 shouldruntrue,阶段 B 运行。

stages:
- stage: A
  jobs:
  - job: A1
    steps:
     - bash: echo "##vso[task.setvariable variable=shouldrun;isOutput=true]true"
     # or on Windows:
     # - script: echo ##vso[task.setvariable variable=shouldrun;isOutput=true]true
       name: printvar

- stage: B
  condition: and(succeeded(), eq(dependencies.A.outputs['A1.printvar.shouldrun'], 'true'))
  dependsOn: A
  jobs:
  - job: B1
    steps:
    - script: echo hello from Stage B

注意

默认情况下,管道中的每个阶段都依赖于 YAML 文件中与其紧邻的前面一个阶段。 如果需要引用不是紧邻当前阶段之前的阶段,可以通过向阶段添加 dependsOn 部分来重写此自动默认值。

一个阶段内的作业到作业依赖项

在单个阶段内的作业级别,dependencies 数据不包含阶段级别信息。

"dependencies": {
  "<JOB_NAME>": {
    "result": "Succeeded|SucceededWithIssues|Skipped|Failed|Canceled",
    "outputs": {
      "stepName.variableName": "value1"
    }
  },
  "...": {
    // another job
  }
}

在此示例中,有三个作业(a、b 和 c)。 作业 a 总是跳过,因为 condition: false。 作业 b 运行,因为没有关联的条件。 作业 c 运行,因为其所有依赖项成功运行(作业 b)或被跳过(作业 a)。

jobs:
- job: a
  condition: false
  steps:
  - script: echo Job a
- job: b
  steps:
  - script: echo Job b
- job: c
  dependsOn:
  - a
  - b
  condition: |
    and
    (
      in(dependencies.a.result, 'Succeeded', 'SucceededWithIssues', 'Skipped'),
      in(dependencies.b.result, 'Succeeded', 'SucceededWithIssues', 'Skipped')
    )
  steps:
  - script: echo Job c

在此示例中,作业 B 依赖于作业 A 中的输出变量。

jobs:
- job: A
  steps:
  - bash: echo "##vso[task.setvariable variable=shouldrun;isOutput=true]true"
  # or on Windows:
  # - script: echo ##vso[task.setvariable variable=shouldrun;isOutput=true]true
    name: printvar

- job: B
  condition: and(succeeded(), eq(dependencies.A.outputs['printvar.shouldrun'], 'true'))
  dependsOn: A
  steps:
  - script: echo hello from B

跨阶段的作业到作业依赖项

在作业级别,还可以引用上一阶段的某个作业中的输出。 这需要使用 stageDependencies 上下文。

"stageDependencies": {
  "<STAGE_NAME>" : {
    "<JOB_NAME>": {
      "result": "Succeeded|SucceededWithIssues|Skipped|Failed|Canceled",
      "outputs": {
          "stepName.variableName": "value"
      }
    },
    "...": {
      // another job
    }
  },
  "...": {
    // another stage
  }
}

在本例中,如果跳过作业 A1,则运行作业 B1。 任务 B2 会检查任务 A1 的输出变量值,以确定它是否运行。

stages:
- stage: A
  jobs:
  - job: A1
    steps:
     - bash: echo "##vso[task.setvariable variable=shouldrun;isOutput=true]true"
     # or on Windows:
     # - script: echo ##vso[task.setvariable variable=shouldrun;isOutput=true]true
       name: printvar

- stage: B
  dependsOn: A
  jobs:
  - job: B1
    condition: in(stageDependencies.A.A1.result, 'Skipped') # change condition to `Succeeded and stage will be skipped`
    steps:
    - script: echo hello from Job B1
  - job: B2
    condition: eq(stageDependencies.A.A1.outputs['printvar.shouldrun'], 'true')
    steps:
     - script: echo hello from Job B2

如果作业依赖于部署作业在不同阶段中定义的变量,则语法会有所不同。 在以下示例中,如果 build_job 部署作业将 runTests 设置为 true,则作业 run_tests 将运行。 请注意,用于 outputs 字典的键是 build_job.setRunTests.runTests

stages:
- stage: build
  jobs:
  - deployment: build_job
    environment:
      name: Production
    strategy:
      runOnce:
        deploy:
          steps:
          - task: PowerShell@2
            name: setRunTests
            inputs:
              targetType: inline
              pwsh: true
              script: |
                $runTests = "true"
                echo "setting runTests: $runTests"
                echo "##vso[task.setvariable variable=runTests;isOutput=true]$runTests"

- stage: test
  dependsOn:
  - 'build'
  jobs:  
    - job: run_tests
      condition: eq(stageDependencies.build.build_job.outputs['build_job.setRunTests.runTests'], 'true')
      steps:
        ...

部署作业输出变量

如果阶段依赖于部署作业在不同阶段中定义的变量,则语法会有所不同。 在以下示例中,阶段 test 取决于将 shouldTest 设置为 true 的部署 build_job。 请注意,在 test 阶段的 condition 中,build_job 出现两次。

stages:
- stage: build
  jobs:
  - deployment: build_job
    environment:
      name: Production
    strategy:
      runOnce:
        deploy:
          steps:
          - task: PowerShell@2
            name: setRunTests
            inputs:
              targetType: inline
              pwsh: true
              script: |
                $runTests = "true"
                echo "setting runTests: $runTests"
                echo "##vso[task.setvariable variable=runTests;isOutput=true]$runTests"

- stage: test
  dependsOn:
  - 'build'
  condition: eq(dependencies.build.outputs['build_job.build_job.setRunTests.runTests'], 'true')
  jobs:
    - job: A
      steps:
        - script: echo Hello from job A

在上面的示例中,条件引用环境,而不是环境资源。 若要引用环境资源,需要将环境资源名称添加到依赖项条件。 在以下示例中,条件引用名为 vmtest 的环境虚拟机资源。

stages:
- stage: build
  jobs:
  - deployment: build_job
    environment:
      name: vmtest
      resourceName: winVM2
      resourceType: VirtualMachine
    strategy:
      runOnce:
        deploy:
          steps:
          - task: PowerShell@2
            name: setRunTests
            inputs:
              targetType: inline
              pwsh: true
              script: |
                $runTests = "true"
                echo "setting runTests: $runTests"
                echo "##vso[task.setvariable variable=runTests;isOutput=true]$runTests"

- stage: test
  dependsOn:
  - 'build'
  condition: eq(dependencies.build.outputs['build_job.Deploy_winVM2.setRunTests.runTests'], 'true')
  jobs:
  - job: A
    steps:
     - script: echo Hello from job A

筛选的数组

在对项集合进行操作时,可以使用 * 语法来应用筛选的数组。 筛选的数组返回所有对象/元素,而不考虑其名称。

例如,考虑名为 foo 的对象数组。 我们希望获取数组的每个对象中 id 属性值的数组。

[
    { "id": 1, "a": "avalue1"},
    { "id": 2, "a": "avalue2"},
    { "id": 3, "a": "avalue3"}
]

可以执行以下操作:

foo.*.id

这会告知系统作为筛选的数组对 foo 进行操作,然后选择 id 属性。

这会返回:

[ 1, 2, 3 ]

类型强制转换

表达式中的值可以在计算表达式时从一种类型转换为另一种类型。 计算表达式时,参数合并为相关数据类型,然后重新转换为字符串。

例如,在此 YAML 中,当计算表达式时,值 TrueFalse 将转换为 10。 当左参数小于右参数时,函数 lt() 将返回 True

variables:
  firstEval: $[lt(False, True)] # 0 vs. 1, True
  secondEval: $[lt(True, False)] # 1 vs. 0, False

steps:
- script: echo $(firstEval)
- script: echo $(secondEval)

使用 eq() 表达式计算等价时,值将隐式转换为数字(false0 数字和 true 数字 1)。

variables:
  trueAsNumber: $[eq('true', true)] # 1 vs. 1, True
  falseAsNumber: $[eq('false', true)] # 0 vs. 1, False

steps:
- script: echo $(trueAsNumber)
- script: echo $(falseAsNumber)

在下一个示例中,值 variables.emptyString 和空字符串都计算为空字符串。 函数 coalesce() 会按顺序计算参数,并返回第一个不等于 null 的值或空字符串。

variables:
  coalesceLiteral: $[coalesce(variables.emptyString, '', 'literal value')]

steps:
- script: echo $(coalesceLiteral) # outputs literal value

下面进一步列出了详细的转换规则。

转自/转为 Boolean Null Number 字符串 版本
布尔值 - - -
Null - -
数字 - - 部分
字符串 部分 部分 - 部分
Version - - -

布尔

转为数字:

  • False0
  • True1

转为字符串:

  • False'False'
  • True'True'

Null

  • 转为布尔值:False
  • 转为数字:0
  • 转为字符串:''(空字符串)

Number

  • 转为布尔值:0False,任何其他数字 → True
  • 转为版本:必须大于零,并且必须包含一个非零的小数。 必须小于 Int32.MaxValue(也有小数部分)。
  • 转为字符串:将数字转换为没有千位分隔符和小数点分隔符的字符串。

字符串

  • 转为布尔值:''(空字符串)→ False,任何其他字符串 → True
  • 转为 null:''(空字符串)→ Null,任何其他不可转换的字符串
  • 转为数字:''(空字符串)→ 0,否则,使用 InvariantCulture 和以下规则运行 C# 的 Int32.TryParse:AllowDecimalPoint | AllowLeadingSign | AllowLeadingWhite | AllowThousands | AllowTrailingWhite。 如果 TryParse 失败,则无法转换。
  • 要版本:运行 C# 的 Version.TryParse。 必须至少包含主要和次要组件。 如果 TryParse 失败,则无法转换。

版本

  • 转为布尔值:True
  • 转为字符串:Major.Minor 或 Major.Minor.Build 或 Major.Minor.Build.Revision。

常见问题解答

我想做一些表达式不支持的事情。 我有哪些用于扩展管道功能的选项?

可以使用包含表达式的脚本自定义管道。 例如,此代码片段采用 BUILD_BUILDNUMBER 变量并使用 Bash 进行拆分。 此脚本为主运行数和次运行数输出两个新变量 $MAJOR_RUN$MINOR_RUN。 然后,这两个变量用于通过 task.setvariable 创建两个管道变量 $major$minor。 这些变量可用于下游步骤。 若要跨管道共享变量,请参阅变量组

steps:
- bash: |
    MAJOR_RUN=$(echo $BUILD_BUILDNUMBER | cut -d '.' -f1)
    echo "This is the major run number: $MAJOR_RUN"
    echo "##vso[task.setvariable variable=major]$MAJOR_RUN"

    MINOR_RUN=$(echo $BUILD_BUILDNUMBER | cut -d '.' -f2)
    echo "This is the minor run number: $MINOR_RUN"
    echo "##vso[task.setvariable variable=minor]$MINOR_RUN"

- bash: echo "My pipeline variable for major run is $(major)"
- bash: echo "My pipeline variable for minor run is $(minor)"