模板使用情况参考

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

模板用于 YAML 管道中可重用的内容、逻辑和参数。 需要了解 Azure Pipelines 关键概念(如阶段、步骤和工作等)的基础知识才能有效地利用好模板。

模板可帮助你加快开发速度。 例如,可以在模板中包含一系列相同的任务,然后在 YAML 管道的不同阶段多次包含模板。

模板还可以帮助你确保管道的安全。 当模板控制管道中允许的内容时,该模板则定义另一个文件必须遵循的逻辑。 例如,你可能想要限制允许运行哪些任务。 此类情况下,可以使用模板阻止某人成功运行违反组织安全策略的任务。

有两种类型的模板:包含和扩展。

  • 包含模板支持在模板中插入可重用的内容。 如果使用模板来包含内容,它的作用类似于许多编程语言中的 include 指令。 一个文件中的内容会插入到另一个文件中。
  • 扩展模板可以控制管道中允许的内容。 当扩展模板控制管道中允许的内容时,该模板则定义另一个文件必须遵循的逻辑。

还应使用模板表达式模板参数来充分利用模板。

施加的限制

模板和模板表达式可能会导致管道的大小和复杂性出现爆炸性增长。 为了帮助防止失控增长,Azure Pipelines 施加了以下限制:

  • 最多只能包含 100 个单独的 YAML 文件(无论是直接还是间接)
  • 模板嵌套不能超过 20 个级别(模板包含其他模板)
  • 解析 YAML 时使用的内存不超过 10 MB(实际上,磁盘上 YAML 解析通常介于 600 KB - 2 MB 之间,具体取决于使用的特定功能)

使用模板,定义一次逻辑即可供多次重用。 模板将多个 YAML 文件的内容合并到一个管道中。 可以将参数从父管道传递到模板中。

从模板扩展

要提高安全性,可以强制管道从特定模板进行扩展。 文件 start.yml 定义参数 buildSteps,该参数随后在管道 azure-pipelines.yml 中使用。 在 start.yml 中,如果通过脚本步骤传递了 buildStep,则它会被拒绝,管道生成会失败。 从模板扩展时,可以通过添加所需的模板审批来提高安全性。

# File: start.yml
parameters:
- name: buildSteps # the name of the parameter is buildSteps
  type: stepList # data type is StepList
  default: [] # default value of buildSteps
stages:
- stage: secure_buildstage
  pool:
    vmImage: windows-latest
  jobs:
  - job: secure_buildjob
    steps:
    - script: echo This happens before code 
      displayName: 'Base: Pre-build'
    - script: echo Building
      displayName: 'Base: Build'

    - ${{ each step in parameters.buildSteps }}:
      - ${{ each pair in step }}:
          ${{ if ne(pair.value, 'CmdLine@2') }}:
            ${{ pair.key }}: ${{ pair.value }}       
          ${{ if eq(pair.value, 'CmdLine@2') }}: 
            # Step is rejected by raising a YAML syntax error: Unexpected value 'CmdLine@2'
            '${{ pair.value }}': error         

    - script: echo This happens after code
      displayName: 'Base: Signing'
# File: azure-pipelines.yml
trigger:
- main

extends:
  template: start.yml
  parameters:
    buildSteps:  
      - bash: echo Test #Passes
        displayName: succeed
      - bash: echo "Test"
        displayName: succeed
      # Step is rejected by raising a YAML syntax error: Unexpected value 'CmdLine@2'
      - task: CmdLine@2
        inputs:
          script: echo "Script Test"
      # Step is rejected by raising a YAML syntax error: Unexpected value 'CmdLine@2'
      - script: echo "Script Test"

从包含资源的模板扩展

还可以使用 extends 从 Azure 管道中包含资源的模板进行扩展。

# File: azure-pipelines.yml
trigger:
- none

extends:
  template: resource-template.yml
# File: resource-template.yml
resources:
  pipelines:
  - pipeline: my-pipeline 
    source: sourcePipeline

steps:
- script: echo "Testing resource template"

插入模板

可以从一个 YAML 复制内容,并在不同的 YAML 中重复使用它。 将内容从一个 YAML 复制到另一个 YAML 可避免在多个位置手动包含相同的逻辑。 文件 include-npm-steps.yml 模板包含在 azure-pipelines.yml 中重复使用的步骤。

注意

模板文件需要在管道运行开始时就存在于文件系统上。 不能在工件中引用模板。

# File: templates/include-npm-steps.yml

steps:
- script: npm install
- script: yarn install
- script: npm run compile
# File: azure-pipelines.yml

jobs:
- job: Linux
  pool:
    vmImage: 'ubuntu-latest'
  steps:
  - template: templates/include-npm-steps.yml  # Template reference
- job: Windows
  pool:
    vmImage: 'windows-latest'
  steps:
  - template: templates/include-npm-steps.yml  # Template reference

步骤重用

可以插入模板,以便跨多个作业重复使用一个或多个步骤。 除了模板中的步骤外,每个作业还可以定义更多步骤。

# File: templates/npm-steps.yml
steps:
- script: npm install
- script: npm test
# File: azure-pipelines.yml

jobs:
- job: Linux
  pool:
    vmImage: 'ubuntu-latest'
  steps:
  - template: templates/npm-steps.yml  # Template reference

- job: macOS
  pool:
    vmImage: 'macOS-latest'
  steps:
  - template: templates/npm-steps.yml  # Template reference

- job: Windows
  pool:
    vmImage: 'windows-latest'
  steps:
  - script: echo This script runs before the template's steps, only on Windows.
  - template: templates/npm-steps.yml  # Template reference
  - script: echo This step runs after the template's steps.

作业重用

与步骤非常类似,作业可通过模板重复使用。

# File: templates/jobs.yml
jobs:
- job: Ubuntu
  pool:
    vmImage: 'ubuntu-latest'
  steps:
  - bash: echo "Hello Ubuntu"

- job: Windows
  pool:
    vmImage: 'windows-latest'
  steps:
  - bash: echo "Hello Windows"
# File: azure-pipelines.yml

jobs:
- template: templates/jobs.yml  # Template reference

使用多个作业时,请记得在模板文件中删除作业的名称,以避免冲突

# File: templates/jobs.yml
jobs:
- job: 
  pool:
    vmImage: 'ubuntu-latest'
  steps:
  - bash: echo "Hello Ubuntu"

- job:
  pool:
    vmImage: 'windows-latest'
  steps:
  - bash: echo "Hello Windows"
# File: azure-pipelines.yml

jobs:
- template: templates/jobs.yml  # Template reference
- template: templates/jobs.yml  # Template reference
- template: templates/jobs.yml  # Template reference

阶段重用

阶段也可以通过模板重复使用。

# File: templates/stages1.yml
stages:
- stage: Angular
  jobs:
  - job: angularinstall
    steps:
    - script: npm install angular
# File: templates/stages2.yml
stages:
- stage: Build
  jobs:
  - job: build
    steps:
    - script: npm run build
# File: azure-pipelines.yml
trigger:
- main

pool:
  vmImage: 'ubuntu-latest'

stages:
- stage: Install
  jobs: 
  - job: npminstall
    steps:
    - task: Npm@1
      inputs:
        command: 'install'
- template: templates/stages1.yml # Template reference
- template: templates/stages2.yml # Template reference

带参数的作业、阶段和步骤模板

在下面的模板中:

  • templates/npm-with-params.yml 定义以下两个参数:namevmImage,创建一个作业,其中 name 参数为作业名称,vmImage 参数为 VM 映像。
  • 管道(azure-pipelines.yml) 引用模板三次,每次都用分别表示操作系统和 VM 映像名称的不同参数值。
  • 生成的管道在不同的 VM 映像上运行,并根据指定的 OS 进行命名。 每个作业都执行 npm 安装和 npm 测试步骤。
# File: templates/npm-with-params.yml

parameters:
- name: name  # defaults for any parameters that aren't specified
  default: ''
- name: vmImage
  default: ''

jobs:
- job: ${{ parameters.name }}
  pool: 
    vmImage: ${{ parameters.vmImage }}
  steps:
  - script: npm install
  - script: npm test

在管道中使用模板时,请指定模板参数的值。

# File: azure-pipelines.yml

jobs:
- template: templates/npm-with-params.yml  # Template reference
  parameters:
    name: Linux
    vmImage: 'ubuntu-latest'

- template: templates/npm-with-params.yml  # Template reference
  parameters:
    name: macOS
    vmImage: 'macOS-latest'

- template: templates/npm-with-params.yml  # Template reference
  parameters:
    name: Windows
    vmImage: 'windows-latest'

具有多个参数的阶段模板

在下面的模板中:

  • stage-template.yml 模板定义以下四个参数:stageNamejobNamevmImagescriptPath 以及所有类型的字符串。 该模板使用 stageName 参数创建阶段来设置阶段名称、使用 jobName 定义作业,并包含一个运行脚本的步骤。
  • 然后,管道 azure-pipeline.yml 使用参数动态定义阶段和作业,并运行执行脚本 build-script.sh 的作业。
# stage-template.yml

parameters:
  - name: stageName
    type: string
  - name: jobName
    type: string
  - name: vmImage
    type: string
  - name: scriptPath
    type: string

stages:
  - stage: ${{ parameters.stageName }}
    jobs:
      - job: ${{ parameters.jobName }}
        pool:
          vmImage: ${{ parameters.vmImage }}
        steps:
          - script: ./${{ parameters.scriptPath }}
# azure-pipelines.yml
trigger:
- main

stages:
- template: stage-template.yml
  parameters:
    stageName: 'BuildStage'
    jobName: 'BuildJob'
    scriptPath: 'build-script.sh' # replace with script in your repository
    vmImage: 'ubuntu-latest'

包含步骤和参数的模板

还可以将参数与步骤模板或阶段模板一起使用。

在下面的模板中:

  • 模板 (templates/steps-with-params.yml) 定义一个名为 runExtendedTests 的参数,其默认值为 false。
  • 管道 (azure-pipelines.yml) 运行 npm testnpm test --extended,因为 runExtendedTests 参数为 true。
# File: templates/steps-with-params.yml

parameters:
- name: 'runExtendedTests'  # defaults for any parameters that aren't specified
  type: boolean
  default: false

steps:
- script: npm test
- ${{ if eq(parameters.runExtendedTests, true) }}:
  - script: npm test --extended

在管道中使用模板时,请指定模板参数的值。

# File: azure-pipelines.yml

steps:
- script: npm install

- template: templates/steps-with-params.yml  # Template reference
  parameters:
    runExtendedTests: 'true'

注意

没有指定类型的标量参数被视为字符串。 例如,如果 myparam 未显式设置为 booleaneq(true, parameters['myparam']) 将返回 true,即使 myparam 参数是单词 false。 非空字符串在布尔上下文中强制转换为 true。 可以重写该表达式以显式比较字符串:eq(parameters['myparam'], 'true')

参数不限于标量字符串。 请参阅数据类型的列表。 例如,使用 object 类型:

# azure-pipelines.yml
jobs:
- template: process.yml
  parameters:
    pool:   # this parameter is called `pool`
      vmImage: ubuntu-latest  # and it's a mapping rather than a string


# process.yml
parameters:
- name: 'pool'
  type: object
  default: {}

jobs:
- job: build
  pool: ${{ parameters.pool }}

变量重用

变量可以在一个 YAML 中定义,并包含在另一个模板中。 如果要将所有变量存储在一个文件中,这可能很有用。 如果使用模板在管道中包含变量,则包含的模板只能用于定义变量。 从模板扩展时,可以使用步骤和更复杂的逻辑。 如果要限制类型,请使用参数而不是变量。

在此示例中,变量 favoriteVeggie 包含在 azure-pipelines.yml 中。

# File: vars.yml
variables:
  favoriteVeggie: 'brussels sprouts'
# File: azure-pipelines.yml

variables:
- template: vars.yml  # Template reference

steps:
- script: echo My favorite vegetable is ${{ variables.favoriteVeggie }}.

带参数的变量模板

可以使用模板将参数传递给变量。 在此示例中,你要将 DIRECTORY 参数传递给变量 RELEASE_COMMAND

# File: templates/package-release-with-params.yml

parameters:
- name: DIRECTORY 
  type: string
  default: "." # defaults for any parameters that specified with "." (current directory)

variables:
- name: RELEASE_COMMAND
  value: grep version ${{ parameters.DIRECTORY }}/package.json | awk -F \" '{print $4}'  

在管道中使用模板时,请指定模板参数的值。

# File: azure-pipelines.yml

variables: # Global variables
  - template: package-release-with-params.yml # Template reference
    parameters:
      DIRECTORY: "azure/checker"

pool:
  vmImage: 'ubuntu-latest'

stages:
- stage: Release_Stage 
  displayName: Release Version
  variables: # Stage variables
  - template: package-release-with-params.yml  # Template reference
    parameters:
      DIRECTORY: "azure/todo-list"
  jobs: 
  - job: A
    steps: 
    - bash: $(RELEASE_COMMAND) #output release command

从模板扩展并使用“包含”模板和变量

一种常见的方案是拥有一个包含开发、测试和生产阶段的管道,该管道既使用变量模板,又使用阶段或作业的扩展模板。

在下面的示例中,variables-template.yml 定义一组虚拟机变量,然后将这些变量用于 azure-pipeline.yml

# variables-template.yml

variables:
- name: devVmImage
  value: 'ubuntu-latest'
- name: testVmImage
  value: 'ubuntu-latest'
- name: prodVmImage
  value: 'ubuntu-latest'

以下 stage-template.yml 文件使用三个参数(namevmImagesteps)和一个名为 Build 的作业定义了一个可重用的阶段配置。

# stage-template.yml
parameters:
- name: name
  type: string
  default: ''
- name: vmImage
  type: string
  default: ''
- name: steps
  type: stepList
  default: []

stages:
- stage: ${{ parameters.name }}
  jobs:
  - job: Build
    pool:
      vmImage: ${{ parameters.vmImage }}
    steps: ${{ parameters.steps }}

以下 azure-pipelines.yml 管道从 variables-template.yml 中导入变量,然后为每个阶段使用 stage-template.yml 模板。 每个阶段(开发、测试、生产)都使用相同的模板定义,但具有不同的参数,这会导致跨阶段的一致性,同时允许进行自定义。 生产阶段包含环境变量,作为可用于身份验证的内容的示例。

# azure-pipelines.yml
trigger:
- main

variables:
- template: variables-template.yml

stages:
- template: stage-template.yml
  parameters:
    name: Dev
    vmImage: ${{ variables.devVmImage }}
    steps:
      - script: echo "Building in Dev"
- template: stage-template.yml
  parameters:
    name: Test
    vmImage: ${{ variables.testVmImage }}
    steps:
      - script: echo "Testing in Test"
- template: stage-template.yml
  parameters:
    name: Prod
    vmImage: ${{ variables.prodVmImage }}
    steps:
      - script: echo "Deploying to Prod"
        env:
          SYSTEM_ACCESSTOKEN: $(System.AccessToken)

引用模板路径

模板路径可以是存储库中的绝对路径,也可以是相对于包含的文件的绝对路径。

若要使用绝对路径,模板路径必须以 / 开头。 所有其他路径都被视为相对路径。

下面是一个嵌套层次结构示例。

|
+-- fileA.yml
|
+-- dir1/
     |
     +-- fileB.yml
     |
     +-- dir2/
          |
          +-- fileC.yml

然后,在 fileA.yml,可以像这样来引用 fileB.ymlfileC.yml

steps:
- template: dir1/fileB.yml
- template: dir1/dir2/fileC.yml

如果 fileC.yml 是起点,可以像这样来包括 fileA.ymlfileB.yml

steps:
- template: ../../fileA.yml
- template: ../fileB.yml

如果 fileB.yml 是起点,可以像这样来包括 fileA.ymlfileC.yml

steps:
- template: ../fileA.yml
- template: dir2/fileC.yml

或者,fileB.yml 可以引用 fileA.yml 和使用 fileC.yml 这样的绝对路径。

steps:
- template: /fileA.yml
- template: /dir1/dir2/fileC.yml

使用其他存储库

可以将模板保留在其他存储库中。 例如,假设你有一个核心管道,你希望所有应用管道都使用该管道。 可以将模板放在核心存储库中,然后从每个应用存储库中引用它:

# Repo: Contoso/BuildTemplates
# File: common.yml
parameters:
- name: 'vmImage'
  default: 'ubuntu-22.04'
  type: string

jobs:
- job: Build
  pool:
    vmImage: ${{ parameters.vmImage }}
  steps:
  - script: npm install
  - script: npm test

现在可以在多个管道中重复使用此模板。 使用 resources 规范提供核心存储库的位置。 引用核心存储库时,请使用 @ 以及你在 resources 中为它提供的名称。

# Repo: Contoso/LinuxProduct
# File: azure-pipelines.yml
resources:
  repositories:
    - repository: templates
      type: github
      name: Contoso/BuildTemplates

jobs:
- template: common.yml@templates  # Template reference
# Repo: Contoso/WindowsProduct
# File: azure-pipelines.yml
resources:
  repositories:
    - repository: templates
      type: github
      name: Contoso/BuildTemplates
      ref: refs/tags/v1.0 # optional ref to pin to

jobs:
- template: common.yml@templates  # Template reference
  parameters:
    vmImage: 'windows-latest'

对于 type: githubname<identity>/<repo>(如以上示例所示)。 对于type: git (Azure Repos),name<project>/<repo>。 如果该项目位于单独的 Azure DevOps 组织中,则需要配置具有项目访问权限的 Azure Repos/Team Foundation Server 类型的服务连接,并将其包含在 YAML 中:

resources:
  repositories:
  - repository: templates
    name: Contoso/BuildTemplates
    endpoint: myServiceConnection # Azure DevOps service connection
jobs:
- template: common.yml@templates

当管道启动时,存储库仅解析一次。 之后,在管道的持续时间内使用相同的资源。 仅使用模板文件。 完全扩展模板后,最终管道就如同完全在源存储库中定义一样运行。 这意味着不能在管道中使用模板存储库中的脚本。

如果要使用模板的特定固定版本,请确保固定到 refrefs 是分支 (refs/heads/<name>) 或标记 (refs/tags/<name>)。 如果要固定特定提交,请先创建一个指向该提交的标记,然后固定到该标记。

注意

如果未指定 ref,则管道将默认使用 refs/heads/main

还可以使用存储库资源的 SHA 值固定到 Git 中的特定提交。 SHA 值是一个 40 个字符的校验和,用于唯一标识提交。

resources:
  repositories:
    - repository: templates
      type: git
      name: Contoso/BuildTemplates
      ref: 1234567890abcdef1234567890abcdef12345678

还可以使用 @self 引用找到原始管道的存储库。 如果要引用回扩展管道存储库中的内容,这在 extends 模板中使用起来很方便。 例如:

# Repo: Contoso/Central
# File: template.yml
jobs:
- job: PreBuild
  steps: []

  # Template reference to the repo where this template was
  # included from - consumers of the template are expected
  # to provide a "BuildJobs.yml"
- template: BuildJobs.yml@self

- job: PostBuild
  steps: []
# Repo: Contoso/MyProduct
# File: azure-pipelines.yml
resources:
  repositories:
    - repository: templates
      type: git
      name: Contoso/Central

extends:
  template: template.yml@templates
# Repo: Contoso/MyProduct
# File: BuildJobs.yml
jobs:
- job: Build
  steps: []

常见问题解答

如何在模板中使用变量?

有时,根据变量将参数设置为值可能会很有用。 参数在处理管道运行时会提前扩展,因此并非所有变量都可用。 若要查看模板中有哪些预定义变量可用,请参阅使用预定义变量

在此示例中,预定义变量 Build.SourceBranchBuild.Reason 用于 template.yml 中的条件。

# File: azure-pipelines.yml
trigger:
- main

extends:
  template: template.yml
# File: template.yml
steps:
- script: echo Build.SourceBranch = $(Build.SourceBranch) # outputs refs/heads/main
- script: echo Build.Reason = $(Build.Reason) # outputs IndividualCI
- ${{ if eq(variables['Build.SourceBranch'], 'refs/heads/main') }}: 
  - script: echo I run only if Build.SourceBranch = refs/heads/main 
- ${{ if eq(variables['Build.Reason'], 'IndividualCI') }}: 
  - script: echo I run only if Build.Reason = IndividualCI 
- script: echo I run after the conditions