> For the complete documentation index, see [llms.txt](https://lizh.gitbook.io/knowledge/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://lizh.gitbook.io/knowledge/tech/06-tong-guo-.gitlabci.yml-pei-zhi-ren-wu.md).

# 通过.Gitlab Ci.Yml配置任务

GitLab提供持续集成服务。如果添加一个.gitlab-ci.yml文件到项目根目录，并配置GitLab项目使用某个Runner，然后每一次提交或者是推送都会触发CI pipeline。

从7.12版本开始，GitLab CI使用YAML文件 （.gitlab-ci.yml）来管理项目配置。

### YAML语言

YAML 是专门用来写配置文件的语言，非常简洁和强大，远比 JSON 格式方便。

YAML 语言（发音 /ˈjæməl/ ）的设计目标，就是方便人类读写。它实质上是一种通用的数据串行化格式。

#### 基本语法规则

* 大小写敏感
* 使用缩进表示层级关系
* 缩进时不允许使用Tab键，只允许使用空格。
* 缩进的空格数目不重要，只要相同层级的元素左侧对齐即可
* `#` 表示注释，从这个字符一直到行尾，都会被解析器忽略。

#### YAML 支持的数据结构有三种

* 对象：键值对的集合，又称为映射（mapping）/ 哈希（hashes） / 字典（dictionary）

  ```yaml
  # yaml
  animal: pets
  hash: { name: Steve, foo: bar } 

  # js
  # { animal: 'pets' }
  # { hash: { name: 'Steve', foo: 'bar' } }
  ```
* 数组：一组按次序排列的值，又称为序列（sequence） / 列表（list）

  ```yaml
  - Cat
  - Dog
  - Goldfish
  # JS
  # [ 'Cat', 'Dog', 'Goldfish' ]

  -
   - Cat
   - Dog
   - Goldfish
  # js
  # [ [ 'Cat', 'Dog', 'Goldfish' ] ]

  animal: [Cat, Dog]
  # js
  # { animal: [ 'Cat', 'Dog' ] }
  ```
* 纯量（scalars）：单个的、不可再分的值。有：字符串、布尔值、整数、浮点数、Null、时间、日期

  ```yaml
  number: 12.30
  isSet: true
  parent: ~ 
  iso8601: 2001-12-14t21:59:43.10-05:00 
  date: 1976-07-31
  e: !!str 123
  f: !!str true
  str: 这是一行字符串
  ```

  ```js
  {
    number: 12.30,
    isSet: true,
  	parent: null, // null用~表示
  	iso8601: new Date('2001-12-14t21:59:43.10-05:00'), // 时间采用 ISO8601 格式
  	date: new Date('1976-07-31'), // 日期采用复合 iso8601 格式的年、月、日表示,
  	e: '123', // 使用两个感叹号，强制转换数据类型。
  	f: 'true',  // 使用两个感叹号，强制转换数据类型。
    str: "这是一行字符串" // 默认不使用引号, 如果字符串之中包含空格或特殊字符，需要放在引号(单引号和双引号都可以)之中。
  }
  ```

更多数据类型，可查看[YAML 语言教程](https://www.ruanyifeng.com/blog/2016/07/yaml.html)

### `.gitlab-ci.yml`是什么

`.gitlab-ci.yml`是用来配置CI在我们的项目中做些什么工作。它位于项目的根目录。

在任何的push操作，GitLab都会寻找`.gitlab-ci.yml`文件，并对此次commit开始jobs，jobs的内容来源于`.gitlab-ci.yml`文件。

### .gitlab-ci.yml

YAML文件定义了一系列带有约束说明的任务。这些任务都是以任务名开始并且至少要包含`script`部分：

```yaml
job1:
  script: "execute-script-for-job1"
  
job2:
  script: "execute-script-for-job2"
```

上面这个例子就是一个最简单且带有两个独立任务的CI配置，每个任务分别执行不同的命令。

script可以直接执行系统命令(例如：./configure;make;make install)或者是直接执行脚本(test.sh)。

任务是由 Runners 接管并且由服务器中runner执行。更重要的是，每一个任务的执行过程都是独立运行的。

用下面这个例子来说明YAML语法还有更多复杂的任务：

```js
image: ruby:2.1
services:
  - postgres

before_script:
  - bundle install

after_script:
  - rm secrets

stages:
  - build
  - test
  - deploy

job1:
  stage: build
  script:
    - execute-script-for-job1
  only:
    - master
  tags:
    - docker
```

下面列出保留字段，这些保留字段不能被定义为`job`名称：

| 关键字            | 是否必须 | 描述                                                                        |
| -------------- | ---- | ------------------------------------------------------------------------- |
| image          | 否    | 用于docker镜像，查看[docker](https://docs.gitlab.com/ce/ci/docker/README.html)文档 |
| services       | 否    | 用于docker服务，查看[docker](https://docs.gitlab.com/ce/ci/docker/README.html)文档 |
| stages         | 否    | 定义构建阶段                                                                    |
| before\_script | 否    | 定义在每个job之前运行的命令                                                           |
| after\_script  | 否    | 定义在每个job之后运行的命令                                                           |
| variable       | 否    | 定义构建变量                                                                    |
| cache          | 否    | 定义一组文件列表，可在后续运行中使用                                                        |

#### image和services

这两个关键字允许使用一个自定义的Docker镜像和一系列的服务，并且可以用于整个job周期。

#### before\_script

用来定义所有 job 之前运行的命令，包括 deploy(部署) jobs，但是在修复 artifacts 之后。它可以是一个数组或者是多行字符串。

#### after\_script

用来定义所有 job 之后运行的命令。它必须是一个数组或者是多行字符串。

#### stages

用来定义可以被 job 调用的 stages。stages 的规范允许有灵活的多级 pipelines。

stages 中的元素顺序决定了对应 job 的执行顺序：

```
1. 相同stage的job可以平行执行。
2. 下一个stage的job会在前一个stage的job成功后开始执行。
```

接下仔细看看这个例子，它包含了3个 stage：

```js
stages:
 - build
 - test
 - deploy
```

1. 首先，所有 `build` 的 jobs 都是并行执行的。
2. 所有 `build` 的 jobs 执行成功后，`test` 的 jobs 才会开始并行执行。
3. 所有 `test` 的 jobs 执行成功，`deploy` 的 jobs 才会开始并行执行。
4. 所有的 `deploy` 的 jobs 执行成功，commit 才会标记为 `success`
5. 任何一个前置的 jobs 失败了，commit 会标记为 `failed` 并且下一个 stages 的 jobs 都不会执行。

> 注：
>
> 如果 `.gitlab-ci.yml` 中没有定义 `stages`，那么 job's stages 会默认定义为 `build`，`test` 和 `deploy`。
>
> 如果一个 job 没有指定 `stage`，那么这个任务会分配到 `test` stage。

#### variables

GItLab CI 允许在 `.gitlab-ci.yml` 文件中添加变量，并在 job 环境中起作用。因为这些配置是存储在git仓库中，所以最好是存储项目的非敏感配置，例如：

```yaml
variables:
  DATABASE_URL:"postgres://postgres@postgres/my_database"
```

这些变量可以被后续的命令和脚本使用。服务容器也可以使用 YAML 中定义的变量，因此我们可以很好的调控服务容器。变量也可以定义成 job level。

除了用户自定义的变量外，Runner 也可以定义它自己的变量。CI\_COMMIT\_REG\_NAME 就是一个很好的例子，它的值表示用于构建项目的分支或 tag 名称。除了在 .gitlab-ci.yml 中设置变量外，还有可以通过 GitLab 的界面上设置私有变量。

#### cache

用来指定需要在 job 之间缓存的文件或目录。只能使用该项目工作空间内的路径。

从GitLab 9.0开始，pipelines和job就默认开启了缓存。

如果 `cache` 定义在 jobs 的作用域之外，那么它就是全局缓存，所有 jobs 都可以使用该缓存。

```yaml
cache:
	paths:
		- binaries/
		- .config
```

> 注意，缓存是在jobs之前进行共享的。如果你不同的jobs缓存不同的文件路径，必须设置不同的**cache:key**，否则缓存内容将被重写。

#### 缓存key

key 指令允许我们定义缓存的作用域(亲和性)，可以是所有 jobs 的单个缓存，也可以是每个 job，也可以是每个分支或者是任何你认为合适的地方。

它也可以让你很好的调整缓存，允许你设置不同 jobs 的缓存，甚至是不同分支的缓存。

cache:key 可以使用任何的预定义变量。

默认 key 是默认设置的这个项目缓存，因此默认情况下，每个 pipelines 和 jobs 中可以共享一切，从 GitLab 9.0 开始。

### Jobs

`.gitlab-ci.yml` 允许指定无限量 jobs。每个 jobs 必须有一个唯一的名字，而且不能是上面提到的关键字。job 由一列参数来定义 jobs 的行为。

```
job_name:
  script:
    - rake spec
    - coverage
  stage: test
  only:
    - master
  except:
    - develop
  tags:
    - ruby
    - postgres
  allow_failure: true
```

| Keyword        | Required | Description                                                                                                                            |
| -------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| script         | yes      | Runner执行的命令或脚本                                                                                                                         |
| image          | no       | 所使用的docker镜像，查阅[使用docker镜像](https://docs.gitlab.com/ce/ci/docker/using_docker_images.html#define-image-and-services-from-gitlab-ciyml) |
| services       | no       | 所使用的docker服务，查阅[使用docker镜像](https://docs.gitlab.com/ce/ci/docker/using_docker_images.html#define-image-and-services-from-gitlab-ciyml) |
| stage          | no       | 定义job stage（默认：`test`）                                                                                                                 |
| variables      | no       | 定义job级别的变量                                                                                                                             |
| only           | no       | 定义一列git分支，并为其创建job                                                                                                                     |
| except         | no       | 定义一列git分支，不创建job                                                                                                                       |
| tags           | no       | 定义一列tags，用来指定选择哪个Runner（同时Runner也要设置tags）                                                                                              |
| allow\_failure | no       | 允许job失败。失败的job不影响commit状态                                                                                                              |
| when           | no       | 定义何时开始job。可以是`on_success`，`on_failure`，`always`或者`manual`                                                                              |
| dependencies   | no       | 定义job依赖关系，这样他们就可以互相传递artifacts                                                                                                         |
| cache          | no       | 定义应在后续运行之间缓存的文件列表                                                                                                                      |
| before\_script | no       | 重写一组在作业前执行的命令                                                                                                                          |
| after\_script  | no       | 重写一组在作业后执行的命令                                                                                                                          |
| environment    | no       | 定义此作业完成部署的环境名称                                                                                                                         |
| coverage       | no       | 定义给定作业的代码覆盖率设置                                                                                                                         |

#### script

`script` 是 Runner 执行的 yaml 脚本。举个例子：

```
job:
  script: "bundle exec rspec"
```

该参数也可以用数组包含多个命令：

```
job:
  script:
    - uname -a
    - bundle exec rspec
```

有时候，`script`命令需要被单引号或者是双引号包裹起来。举个例子，当命令中包含冒号(`:`)时，script需要被包在双引号中，这样YAML解析器才可以正确解析为一个字符串而不是一个键值对(key:value)。使用这些特殊字符的时候一定要注意：`:`,`{`,`}`,`[`,`]`,`,`,`&`,`*`,`#`,`?`,`|`,`-`,`<`,`>`,`=`,`!`。

#### stage

`stage` 允许一组 jobs 进入不同的 stages。jobs 在相同的 `stage` 时会 `parallel` 同时进行。

#### only and except

`only` 和 `except` 是两个参数用分支策略来限制 jobs 构建：

* `only` 定义哪些分支和标签的 git 项目将会被 job 执行。
* `except` 定义哪些分支和标签的 git 项目将不会被 job 执行。

#### variables

在 job 中是可以使用关键字 variables 来定义 job 变量。它的运行原理跟 global-level 是一样的，但是它允许设置特殊的 job 变量。

当设置了 job 级别的关键字 variables，它会覆盖全局 YAML 和预定义中的 job 变量。想要关闭全局变量可以在 job 中设置一个空数组：

```yaml
job_name:
  variables: []
```

#### tags

`tags` 可以从允许运行此项目的所有 Runners 中选择特定的 Runners 来执行 jobs。

在注册 Runner 的过程中，我们可以设置 Runner 的标签，比如`ruby`，`postgres`，`development`。

`tags` 可通过 tags 来指定特殊的 Runners 来运行 jobs：

```
job:
  tags:
    - ruby
    - postgres
```

上面这个示例中，需要确保构建此 `job` 的 Runner 必须定义了 `ruby` 和 `postgres` 这两个 tags。

#### allow\_failure

`allow_failure` 可以用于当你想设置一个job失败的之后并不影响后续的CI组件的时候。失败的jobs不会影响到commit状态。

#### when

用于实现在失败时或即使失败也要运行的作业。

when可以设置以下值：

* on\_success - 只有前面 stages 的所有工作成功时才执行。 这是默认值。
* on\_failure - 当前面 stages 中任意一个jobs失败后执行。
* always - 无论前面 stages 中 jobs 状态如何都执行。
* `manual` - 手动执行(GitLab8.10增加)。更多请查看手动操作。

#### enviroment

environment 用于定义 job 部署到特殊的环境中。如果指定了environment，并且没有该名称下的环境，则会自动创建新环境。

#### artifacts

`artifacts` 用于指定成功后应附加到 job 的文件和目录的列表。只能使用项目工作间内的文件或目录路径。

```yaml
job:
   artifacts:
     paths:
		   - binaries/
     name: "$CI_COMMIT_REF_NAME"
     untracked: true
```

#### dependencies

这个功能应该与`artifacts`一起使用，并允许定义在不同jobs之间传递artifacts。

注意：所有之前的 stages 都是默认设置通过。

如果要使用此功能，应该在上下文的job中定义dependencies，并且列出之前都已经通过的jobs和可下载的artifacts。你只能在当前执行的stages前定义jobs。你如果在当前stages或者后续的stages中定义了jobs，它将会报错。可以通过定义一个空数组是当前job跳过下载artifacts。

### pages

pages是一个特殊的job，用于将静态的内容上传到GitLab，可用于为您的网站提供服务。它有特殊的语法，因此必须满足以下两个要求：

1. 任何静态内容必须放在`public/`目录下
2. `artifacts`必须定义在`public/`目录下

下面的这个例子是将所有文件从项目根目录移动到`public/`目录。`.public`工作流是`cp`，并且它不会循环复制`public/`本身。

```yaml
pages:
  stage: deploy
  script:
  - mkdir .public
  - cp -r * .public
  - mv .public public
  artifacts:
    paths:
    - public
  only:
  - master
```

### 参考资料

[阮一峰 YAML语言教程](https://www.ruanyifeng.com/blog/2016/07/yaml.html)

[官方GitLab文档翻译](https://github.com/Fennay/gitlab-ci-cn)

[Gitlab CI yaml官方配置文件翻译](https://segmentfault.com/a/1190000010442764)
