{{< details >}}

  • Tier: 基础版, 专业版, 旗舰版
  • Offering: JihuLab.com, 私有化部署

{{< /details >}}

你可以通过使用以下功能减少极狐GitLab CI/CD 配置文件中的复杂性和重复配置:

  1. YAML 特定功能如 anchors (&), aliases (*), 和 map merging (<<)。
  2. 使用更灵活和可读的 extends 关键字。你应该尽可能使用 extends

锚点

YAML 有一个称为“锚点”的功能,可以用来在文档中复制内容。

使用锚点来复制或继承属性。将锚点与 隐藏的任务 一起使用,为你的任务提供模板。当有重复的键时,最新包含的键将覆盖其他键。

在某些情况下(见 YAML 锚点的脚本),你可以使用 YAML 锚点来构建具有多组件的数组,这些组件在其他地方定义。例如:

.default_scripts: &default_scripts
  - ./default-script1.sh
  - ./default-script2.sh

job1:
  script:
    - *default_scripts
    - ./job-script.sh

当使用 include 关键字时,你不能跨多个文件使用 YAML 锚点。锚点仅在它们定义的文件中有效。要重用来自不同 YAML 文件的配置,请使用 !reference 标签extends 关键字

以下示例使用锚点和 map 合并。它创建了两个任务,test1test2,继承了 .job_template 配置,每个都有其自定义的 script 定义:

.job_template: &job_configuration  # 隐藏的 yaml 配置,定义了一个名为 'job_configuration' 的锚点
  image: ruby:2.6
  services:
    - postgres
    - redis

test1:
  <<: *job_configuration           # 添加 'job_configuration' 别名的内容
  script:
    - test1 project

test2:
  <<: *job_configuration           # 添加 'job_configuration' 别名的内容
  script:
    - test2 project

& 设置锚点的名称(job_configuration),<< 意味着“将给定的哈希合并到当前的哈希中”,而 * 包含命名的锚点(再次是 job_configuration)。此示例的 扩展版 是:

.job_template:
  image: ruby:2.6
  services:
    - postgres
    - redis

test1:
  image: ruby:2.6
  services:
    - postgres
    - redis
  script:
    - test1 project

test2:
  image: ruby:2.6
  services:
    - postgres
    - redis
  script:
    - test2 project

你可以使用锚点定义两组服务。例如,test:postgrestest:mysql 共享在 .job_template 中定义的 script,但使用不同的 services,分别在 .postgres_services.mysql_services 中定义:

.job_template: &job_configuration
  script:
    - test project
  tags:
    - dev

.postgres_services:
  services: &postgres_configuration
    - postgres
    - ruby

.mysql_services:
  services: &mysql_configuration
    - mysql
    - ruby

test:postgres:
  <<: *job_configuration
  services: *postgres_configuration
  tags:
    - postgres

test:mysql:
  <<: *job_configuration
  services: *mysql_configuration

扩展版 是:

.job_template:
  script:
    - test project
  tags:
    - dev

.postgres_services:
  services:
    - postgres
    - ruby

.mysql_services:
  services:
    - mysql
    - ruby

test:postgres:
  script:
    - test project
  services:
    - postgres
    - ruby
  tags:
    - postgres

test:mysql:
  script:
    - test project
  services:
    - mysql
    - ruby
  tags:
    - dev

你可以看到隐藏的任务被方便地用作模板,并且 tags: [postgres] 覆盖了 tags: [dev]

YAML 锚点的脚本

{{< history >}}

  • 在极狐GitLab 16.9 中,引入了对 stages 关键字的锚点支持。

{{< /history >}}

你可以在 scriptbefore_scriptafter_script 中使用 YAML 锚点,以在多个任务中使用预定义命令:

.some-script-before: &some-script-before
  - echo "Execute this script first"

.some-script: &some-script
  - echo "Execute this script second"
  - echo "Execute this script too"

.some-script-after: &some-script-after
  - echo "Execute this script last"

job1:
  before_script:
    - *some-script-before
  script:
    - *some-script
    - echo "Execute something, for this job only"
  after_script:
    - *some-script-after

job2:
  script:
    - *some-script-before
    - *some-script
    - echo "Execute something else, for this job only"
    - *some-script-after

使用 extends 复用配置部分

你可以使用 extends 关键字 在多个任务中重用配置。它类似于 YAML 锚点,但更简单,并且你可以 extendsincludes 一起使用

extends 支持多级继承。由于额外的复杂性,你应该避免使用超过三级的继承,但最多可以使用十一级。以下示例有两级继承:

.tests:
  rules:
    - if: $CI_PIPELINE_SOURCE == "push"

.rspec:
  extends: .tests
  script: rake rspec

rspec 1:
  variables:
    RSPEC_SUITE: '1'
  extends: .rspec

rspec 2:
  variables:
    RSPEC_SUITE: '2'
  extends: .rspec

spinach:
  extends: .tests
  script: rake spinach

extends 中排除一个键

要从扩展内容中排除一个键,你必须将其分配为 null,例如:

.base:
  script: test
  variables:
    VAR1: base var 1

test1:
  extends: .base
  variables:
    VAR1: test1 var 1
    VAR2: test2 var 2

test2:
  extends: .base
  variables:
    VAR2: test2 var 2

test3:
  extends: .base
  variables: {}

test4:
  extends: .base
  variables: null

合并后的配置:

test1:
  script: test
  variables:
    VAR1: test1 var 1
    VAR2: test2 var 2

test2:
  script: test
  variables:
    VAR1: base var 1
    VAR2: test2 var 2

test3:
  script: test
  variables:
    VAR1: base var 1

test4:
  script: test
  variables: null

一起使用 extendsinclude

要重用不同配置文件中的配置,结合使用 extendsinclude

在以下示例中,一个 scriptincluded.yml 文件中定义。然后,在 .gitlab-ci.yml 文件中,extends 引用 script 的内容:

  • included.yml:

    .template:
      script:
        - echo Hello!
    
  • .gitlab-ci.yml:

    include: included.yml
    
    useTemplate:
      image: alpine
      extends: .template
    

合并细节

你可以使用 extends 合并哈希,但不能合并数组。用于合并的算法是“最接近的范围获胜”。当有重复的键时,极狐GitLab 基于键进行反向深度合并。最后一个成员的键总是覆盖在其他层次上定义的任何东西。例如:

.only-important:
  variables:
    URL: "http://my-url.internal"
    IMPORTANT_VAR: "the details"
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
    - if: $CI_COMMIT_BRANCH == "stable"
  tags:
    - production
  script:
    - echo "Hello world!"

.in-docker:
  variables:
    URL: "http://docker-url.internal"
  tags:
    - docker
  image: alpine

rspec:
  variables:
    GITLAB: "is-awesome"
  extends:
    - .only-important
    - .in-docker
  script:
    - rake rspec

结果是这个 rspec 任务:

rspec:
  variables:
    URL: "http://docker-url.internal"
    IMPORTANT_VAR: "the details"
    GITLAB: "is-awesome"
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
    - if: $CI_COMMIT_BRANCH == "stable"
  tags:
    - docker
  image: alpine
  script:
    - rake rspec

在这个例子中:

  • variables 部分合并,但 URL: "http://docker-url.internal" 覆盖了 URL: "http://my-url.internal"
  • tags: ['docker'] 覆盖了 tags: ['production']
  • script 不合并,但 script: ['rake rspec'] 覆盖了 script: ['echo "Hello world!"']。你可以使用 YAML 锚点 合并数组。

!reference 标签

使用 !reference 自定义 YAML 标签从其他任务部分选择关键字配置,并在当前部分中重用它。与 YAML 锚点 不同,你还可以使用 !reference 标签重用来自 包含的 配置文件的配置。

在以下示例中,test 任务中重用了来自两个不同位置的 scriptafter_script

  • configs.yml:

    .setup:
      script:
        - echo creating environment
    
  • .gitlab-ci.yml:

    include:
      - local: configs.yml
    
    .teardown:
      after_script:
        - echo deleting environment
    
    test:
      script:
        - !reference [.setup, script]
        - echo running my own command
      after_script:
        - !reference [.teardown, after_script]
    

在以下示例中,test-vars-1 重用了 .vars 中的所有变量,而 test-vars-2 选择了一个特定变量,并将其重用为新的 MY_VAR 变量。

.vars:
  variables:
    URL: "http://my-url.internal"
    IMPORTANT_VAR: "the details"

test-vars-1:
  variables: !reference [.vars, variables]
  script:
    - printenv

test-vars-2:
  variables:
    MY_VAR: !reference [.vars, variables, IMPORTANT_VAR]
  script:
    - printenv

在使用 !reference 标签与 parallel:matrix 关键字 时,存在一个 已知问题

scriptbefore_scriptafter_script 中嵌套 !reference 标签

{{< history >}}

  • 在极狐GitLab 16.9 中,引入了对 stages 关键字的 !reference 支持。

{{< /history >}}

你可以在 scriptbefore_scriptafter_script 部分中嵌套 !reference 标签多达 10 级。使用嵌套标签在构建更复杂的脚本时定义可重用的部分。例如:

.snippets:
  one:
    - echo "ONE!"
  two:
    - !reference [.snippets, one]
    - echo "TWO!"
  three:
    - !reference [.snippets, two]
    - echo "THREE!"

nested-references:
  script:
    - !reference [.snippets, three]

在这个例子中,nested-references 任务运行所有三个 echo 命令。

配置你的 IDE 支持 !reference 标签

流水线编辑器 支持 !reference 标签。然而,像 !reference 这样的自定义 YAML 标签的模式规则可能会被你的编辑器默认视为无效。你可以配置一些编辑器以接受 !reference 标签。例如:

  • 在 VS Code 中,你可以在 settings.json 文件中设置 vscode-yaml 以解析 customTags

    "yaml.customTags": [
       "!reference sequence"
    ]
    
  • 在 Sublime Text 中,如果你正在使用 LSP-yaml 包,你可以在你的 LSP-yaml 用户设置中设置 customTags

    {
      "settings": {
        "yaml.customTags": ["!reference sequence"]
      }
    }