如何在带有变量组的 azure DevOps yaml 管道变量中使用 IF ELSE?

how can I use IF ELSE in variables of azure DevOps yaml pipeline with variable group?

除了变量组之外,我正在尝试将 2 个值之一分配给变量,但找不到如何使用 IF ELSE 的参考。

基本上我需要将这个 jerkins 逻辑转换为 azure DevOps。

詹金斯

if (branch = 'master') { 
   env = 'a'
} else if (branch = 'dev'){
    env ='b'
}

我从以下参考中找到了 1 个参考,但如果变量部分没有变量组,这个参考似乎有效。

但是在我的管道中,我已经有一个秘密变量组,所以我必须使用 name/value 约定并且该示例不适用于 expected a mapping 或 [=14 之类的错误=] 或 Unexpected value 'env'

variables:
- group: my-global
- name: env
  value:
    ${{ if eq(variables['Build.SourceBranchName'], 'master') }}: 
      env: a
    ${{ if eq(variables['Build.SourceBranchName'], 'dev') }}: 
      env: b

variables:
- group: my-global
- name: env
  value:
    ${{ if eq(variables['Build.SourceBranchName'], 'master') }}: a
    ${{ if eq(variables['Build.SourceBranchName'], 'dev') }}: b

我认为您现在需要使用任务来自定义 name/value 语法变量和条件变量值。正如您所指出的,name/value 语法的对象结构似乎破坏了表达式的解析。

对我来说,以下是一个相当干净的实现,如果你想将它从管道中抽象出来,似乎一个简单的模板供你使用的许多管道应该满足对中央"global" 位置.

variables:
  - group: FakeVarGroup
  - name: env
    value: dev

steps:
  - powershell: |
      if ($env:Build_SourceBranchName -eq 'master') {
        Write-Host ##vso[task.setvariable variable=env;isOutput=true]a
        return
      } else {
        Write-Host ##vso[task.setvariable variable=env;isOutput=true]b
      }      
    displayName: "Set Env Value"

据我所知,构建条件分支的最佳方式是在 YAML 中使用 "trigger",而不是实施复杂的 "if-else"。它也更安全,并且您对分支触发器有更明确的控制,而不是依赖 CI 变量。

示例:

# specific branch build 
jobs:
- job: buildmaster
  pool:
    vmImage: 'vs2017-win2016'
  trigger:
    - master

  steps:
  - script: |
      echo "trigger for master branch"

- job: buildfeature
  pool:
    vmImage: 'vs2017-win2016'
  trigger:
    - feature

  steps:
  - script: |
      echo "trigger for feature branch"

要使用包含和排除分支的触发器,您可以使用包含和排除分支的更复杂的触发器语法。

示例:

# specific branch build
trigger:
  branches:
    include:
    - master
    - releases/*
    exclude:
    - releases/1.*

YAML 中 Azure DevOps Pipelines trigger 的官方文档是: Azure Pipelines YAML trigger documentation

更新 1:

我在这里重新发布我的评论并附加注释: 我在考虑拥有不同的管道,因为在 CI 变量 之间玩杂耍的复杂性并不比在一个带有触发器的 YAML 中拥有多个作业更易于维护 。具有触发器的多任务也迫使我们对分支管理有明确的区分和规定。由于这些可维护性优势,触发器和条件分支包含已被我的团队使用了一年。

尽管不同意,但对我来说,在任何步骤的任何脚本中都有一个嵌入式逻辑来检查哪个分支当前在会话中,然后执行任何进一步的操作,更像是临时解决方案。这之前给我和我的团队带来了维护问题。

特别是如果嵌入式逻辑倾向于通过检查其他分支来增长,那么复杂性比分支之间的明确分离更复杂。此外,如果 YAML 文件要长期维护,它应该有跨不同分支的明确规定和路线图。冗余是不可避免的,但分离特定逻辑的意图将在长期 运行 中为可维护性付出更多代价。

这就是为什么我在回答中也强调分支包含和排除:)

此代码有效。
我正在对参数做类似的事情。

variables:
  - name: var1
    ${{ if eq(parameters.var1, 'custom') }}:
      value: $(var1.manual.custom)
    ${{ if ne(parameters.var1, 'custom') }}:
      value: ${{ parameters.var1 }}

更新 2021 年 9 月 9 日

We have now natively if else expression 我们可以这样写

variables:
- group: PROD
- name: env
  ${{ if eq(variables['Build.SourceBranchName'], 'master') }}:
    value: a
  ${{ else }}:
    value: b

steps:
- script: | 
    echo '$(name)'
    echo '$(env)'

原回复

模板表达式的语法 ${{ if ...... }} 不仅限于 job/stage 级别。下面的两个管道做同样的事情并产生相同的输出:

stages:
- stage: One
  displayName: Build and restore
  variables:
  - group: PROD
  - name: env
    ${{ if eq(variables['Build.SourceBranchName'], 'master') }}:
      value: a
    ${{ if eq(variables['Build.SourceBranchName'], 'dev') }}:
      value: b
  jobs:
  - job: A
    steps:
    - script: | 
        echo '$(name)'
        echo '$(env)'
variables:
- group: PROD
- name: env
  ${{ if eq(variables['Build.SourceBranchName'], 'master') }}:
    value: a
  ${{ if eq(variables['Build.SourceBranchName'], 'dev') }}:
    value: b

steps:
- script: | 
    echo '$(name)'
    echo '$(env)'

我发现了一些对某些场景有用的技巧。例如,人们可能希望根据是否启用系统调试来调整任务输入。这不能使用“标准条件插入”(${{ if … }}:) 来完成,因为 System.Debug 不在模板表达式的范围内。所以,runtime expressions 来拯救:

- job:
  variables:
    VERBOSE_FLAG: $[
        replace(
          replace(
            eq(lower(variables['System.Debug']), 'true'),
            True,
            '--verbose'
          ),
          False,
          ''
        )
      ]
  steps:
    - task: cURLUploader@2
      inputs:
        # …
        options: --fail --more-curl-flags $(VERBOSE_FLAG)

请注意,在调用 replace 之前使用 eq 检查 System.Debug 的值并不是多余的:因为 eq 总是 returns 或者 TrueFalse,然后我们可以安全地使用 replace 将这些值分别映射到 '--verbose'''

一般来说,我强烈建议坚持使用布尔表达式(例如应用布尔值 function,如 eqgtin)作为内部 replace 应用程序的第一个参数。如果我们没有这样做,而是只是写成例如

        replace(
          replace(
            lower(variables['System.Debug']),
            'true',
            '--verbose'
          ),
          'false',
          ''
        )

那么,如果 System.Debug 被设置为例如footruebar, VERBOSE_FLAG 的值会变成 foo--verbosebar.

A​​zure YAML if-else 解决方案(当您定义了 group 之后需要使用 name/value 符号时。

variables:
- group: my-global
- name: env
  value: a       # set by default
- name: env
  ${{ if eq(variables['Build.SourceBranchName'], 'master') }}:
  value: b     # will override default

如果您没有定义 group

variables:
 env: a       # set by default
 ${{ if eq(variables['Build.SourceBranchName'], 'master') }}:
 env: b      # will override default

Microsoft 几周前发布了 YAML 管道的一项新功能,让您可以做到这一点:IF ELSE 表示法。

https://docs.microsoft.com/en-us/azure/devops/release-notes/2021/sprint-192-update#new-yaml-conditional-expressions

Writing conditional expressions in YAML files just got easier with the use of ${{ else }} and ${{ elseif }} expressions. Below are examples of how to use these expressions in YAML pipelines files.

steps:
- script: tool
  env:
    ${{ if parameters.debug }}:
      TOOL_DEBUG: true
      TOOL_DEBUG_DIR: _dbg
    ${{ else }}:
      TOOL_DEBUG: false
      TOOL_DEBUG_DIR: _dbg
variables:
  ${{ if eq(parameters.os, 'win') }}:
    testsFolder: windows
  ${{ elseif eq(parameters.os, 'linux' }}:
    testsFolder: linux
  ${{ else }}:
    testsFolder: mac