优化 GitLab CI/CD 配置文件

您可以使用以下方法降低 GitLab CI/CD 配置文件中的复杂性和重复配置:

  • 特定于 YAML 的功能,例如 anchors (&)、别名 (*) 和 map merging (<<)。阅读有关各种 YAML 功能 的更多信息。
  • 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 关键字

以下示例使用锚点和地图合并。它创建了两个作业,test1test2,它们继承了 .job_template 配置,每个作业都定义了自己的自定义 script

.job_template: &job_configuration  # Hidden yaml configuration that defines an anchor named 'job_configuration'
  image: ruby:2.6
  services:
    - postgres
    - redis

test1:
  <<: *job_configuration           # Add the contents of the 'job_configuration' alias
  script:
    - test1 project

test2:
  <<: *job_configuration           # Add the contents of the 'job_configuration' alias
  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 锚点

您可以在多个作业中使用预定义的命令中,将 YAML 锚点脚本before_scriptafter_script 结合使用:

.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 支持多级继承。您应该避免使用三个以上的级别,但您可以使用多达 11 个级别。以下示例具有两个继承级别:

.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

在以下示例中,在 included.yml 文件中定义了一个 script。 然后,在 .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 标签

  • 引入于 13.9 版本
  • rules 关键字的支持引入于 14.3 版本。

使用 !reference 自定义 YAML 标签从其他作业部分选择关键字配置,并在当前部分中重用它。与 YAML 锚点不同,您也可以使用 !reference 标签来重用来自 included 配置文件的配置。

在以下示例中,来自两个不同位置的 script 和一个 after_scripttest 作业中被重用:

  • setup.yml:

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

    include:
      - local: setup.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

scriptbefore_scriptafter_script 中嵌套 !reference 标签

引入于 14.8 版本。

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

.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 标签。但是,默认情况下,您的编辑器可能会将自定义 YAML 标签(如 !reference )的架构规则视为无效。您可以配置一些编辑器来接受 !reference 标签。例如:

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

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

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