gitlab-ci.yml 文件是 GitLab CI/CD 的配置文件,它描述了各项任务如何和何时在版本控制过程中执行。文件中定义了一系列的任务或"jobs",这些jobs可以被组织到各个阶段,并在触发某些事件时自动运行,如代码提交或代码合并。每次提交或推送都会触发一个新的 CI/CD “pipeline”,gitlab-ci.yml 文件便是负责协调和控制这个 pipeline 执行过程的大脑。
文件结构示例
stages:
- build
- test
- deploy
build_job:
stage: build
script:
- echo "This is the build stage"
test_job:
stage: test
script:
- echo "This is the test stage"
deploy_job:
stage: deploy
script:
- echo "This is the deploy stage"
stages 定义一组阶段,然后在每个工作描述中使用 stage 关键字指定其属于哪个阶段。GitLab CI/CD将会按照 stages 定义的阶段顺序以及每个阶段内的作业并行度来执行作业。
通常before_script在gitlab-ci.yml 文件的顶层定义,作为全局设置。这样定义的 before_script 会在每个作业(job)执行前都会运行。before_script 虽然有全局配置的偏好,但也可以根据需要在单独的作业中定义。
# 在这个配置中,全局 before_script 会在 job1 和 job2 两个作业之前都执行一次。
stages:
- build
before_script:
- echo "This is a global before_script."
job1:
stage: build
script: echo "This is job1."
job2:
stage: build
script: echo "This is job2."
也可以在具体的作业中定义 before_script,这样设置的 before_script 会只影响这个作业。
# job1 的 before_script 被重写为 "echo "This is a job-specific before_script."",并且只在 job1 执行前运行。
# 全局的 before_script 不会在 job1 之前执行,但会在 job2 之前执行。
# 作业特定的 before_script 会覆盖全局的定义。
stages:
- build
before_script:
- echo "This is a global before_script."
job1:
stage: build
before_script:
- echo "This is a job-specific before_script."
script: echo "This is job1."
job2:
stage: build
script: echo "This is job2."
after_script:它类似于 before_script,定义在这里的命令会在每个作业(job)的 script 部分执行完毕后运行。这常常用来在作业完成之后清理环境或存储日志等。
可以定义一组环境变量,这些变量会在所有作业中可用。例如,你可能需要在多个作业中使用同一个数据库的链接地址。
stages:
- build
variables:
DATABASE_URL: "postgres://user:[email protected]:5432/dbname"
job1:
stage: build
script: echo "The database url is $DATABASE_URL."
定义作业的执行阶段。GitLab CI/CD 会按照 stages
的顺序执行作业。
stages:
- build
- test
job1:
stage: build
script: echo "Build stage"
job2:
stage: test
script: echo "Test stage"
这两个指令都用于定义作业执行的条件。例如,你可以设置一个作业只在特定分支的提交时运行。
stages:
- build
- test
- deploy
build_job:
stage: build
script: echo "This is building stage."
only:
- master
test_job:
stage: test
script: echo "This is testing stage."
except:
- master
deploy_job:
stage: deploy
script: echo "This is deploying stage."
rules:
- if: '$CI_COMMIT_BRANCH == "master"'
when: always
- if: '$CI_COMMIT_BRANCH != "master"'
when: never
build_job 仅在主分支(master)上运行。相反,test_job会在所有分支上运行,除非是主分支。deploy_job 也仅在主分支上运行,使用了 rules 指令来实现这个条件,这是一个更复杂的条件判断,不同的规则 (if 语句)可以应对更为复杂的情况。
允许你定义一个缓存,这个缓存可以在作业之间共享。这常常用于缓存依赖,以加快构建的速度。
stages:
- install
- build
variables:
BUNDLE_PATH: vendor/bundle
cache:
paths:
- vendor/bundle
install:
stage: install
script:
- bundle install --path vendor/bundle
build:
stage: build
script:
- bundle exec rake build
在这个示例中,我们将 Ruby gems 安装到 vendor/bundle 目录下,然后在 cache 指令中声明这个路径来缓存这些 gems。
这样做的好处是,当在后续的作业或者后续的 pipeline 运行时,不需要重新下载和安装这些 gems,在此 .gitlab-ci.yml 文件的配置中,如果 cache 设置正确并且有效,然后在 vendor/bundle 路径下的gems没有发生变化(即 Gemfile 和 Gemfile.lock 文件没有发生改变),那么 bundle install --path vendor/bundle 命令就不会再次下载已经缓存的 gems。这是因为 Bundler(Ruby的依赖管理工具)会检查 gems 是否已经存在,如果已经存在并且版本匹配,那么就不会再次下载。
综上,对于重复的 install 作业,如果 Gemfile 和 Gemfile.lock 中的内容没有发生变化,那么即使在 pipeline 再次运行此作业,也不会再次下载已经缓存的 gems,从而可以加快构建的速度。可以大大减少构建的时间。
install 和 build 两个作业分别在 install 和 build 阶段运行。install 阶段会使用 bundle install 命令来下载和安装 gems,build 阶段则使用 bundle exec 来执行构建命令。
通过这个配置,我们就可以在多个作业之间,以及多次 pipeline 运行之间共享这些 gem 的缓存,以此来加快构建的速度。
对于重复的 install 作业,在下次pipeline中会再次运行吗?
如果 cache 设置正确并且有效,然后在 vendor/bundle 路径下的gems没有发生变化(即 Gemfile 和 Gemfile.lock 文件没有发生改变),那么 bundle install --path vendor/bundle 命令就不会再次下载已经缓存的 gems。这是因为 Bundler(Ruby的依赖管理工具)会检查 gems 是否已经存在,如果已经存在并且版本匹配,那么就不会再次下载。
综上,对于重复的 install 作业,如果 Gemfile 和 Gemfile.lock 中的内容没有发生变化,那么即使在 pipeline 再次运行此作业,也不会再次下载已经缓存的 gems,从而可以加快构建的速度。
声明一些构建产物,这些产物将会在 gitlab-ci.yml
文件中其他作业或后续阶段的作业中可用,或者可供直接下载查看。
stages:
- build
- test
build:
stage: build
script:
- mkdir build
- echo "Build artifacts" > build/artifacts.txt
artifacts:
paths:
- build/
test:
stage: test
script:
- cat build/artifacts.txt
在这个示例中,我们的 pipeline 分为两个阶段:build 和 test。在 build 阶段运行的 build 作业中,我们创建了一个 build 目录,并在其中生成了一个名为 artifacts.txt 的文件。
然后,我们使用 artifacts 指令声明了 build/ 路径下的所有文件都是构建产物,GitLab CI/CD 将会在构建完成后收集这些产物。
在后续的 test 阶段的 test 作业中,我们可以直接访问到这个 artifacts.txt 文件。也就是说,通过 artifacts 允许我们在后续阶段的作业中使用到前一阶段的构建产物。
用于在当前 .gitlab-ci.yml 文件中引入其他的 CI/CD 配置文件,可以实现配置的模块化和共享。被引入的文件可以位于同一项目的其它位置,也可以位于其他项目,甚至可以从远程的 HTTP(S) URL 中引入。引入的文件将像是在主配置文件中一样被解析和执行,这对于大型项目或需要共享 CI/CD 配置的情况很有用。
比如说,你有一个库 my-great-library。该库有自己的 .gitlab-ci.yml 文件,如下:
variables:
BUILD_DIR: build
stages:
- build
- test
build:
stage: build
script:
- mkdir ${BUILD_DIR}
- echo "Building the library" > ${BUILD_DIR}/result.txt
test:
stage: test
script:
- echo "Testing the library"
而你在其他许多项目里都依赖这个库,并且需要对这个库进行构建和测试。这时,你可以在其他项目的 CI 标记 include,将这个库的 CI/CD 配置包含进来,例如:
项目 project-1 的 .gitlab-ci.yml:
include:
- project: 'yourusername/my-great-library'
file: '.gitlab-ci.yml'
这样就可以实现在多个项目中共享一个统一的 CI/CD 配置,避免了每个项目都需要重复编写相同的配置,从而大大提高了效率。同时也有利于集中管理和修改这个共享的配置。
用于复用已经定义的特定配置。在 GitLab CI/CD 配置中,我们可以定义一些通用的配置作为模板,例如设定重用的 script 块或者 before_script 块等,然后在需要的地方使用 extends 进行引用。不同于 include 关键词,extends 并不会引入新的文件,而是仅仅在当前文件内部进行配置的重用。
# 我们首先声明一个 '.base_job',这个定义包含了所有作业共有的部分
.base_job:
before_script:
- echo "Setting up the job environment"
after_script:
- echo "Cleaning up after job"
# '.build_template' 扩展了 '.base_job',并添加了构建阶段特有的设定
.build_template:
extends: .base_job
variables:
BUILD_DIR: build
script:
- mkdir ${BUILD_DIR}
- echo "Building the project" > ${BUILD_DIR}/output.txt
stage: build
# '.test_template' 也扩展了 '.base_job',然后添加了测试阶段特有的设定
.test_template:
extends: .base_job
script:
- echo "Testing the project"
stage: test
# 现在我们定义具体的构建和测试作业,它们分别扩展了 '.build_template' 和 '.test_template'
job1:
extends: .build_template
variables:
JOB_NAME: JOB1
job2:
extends: .test_template
variables:
JOB_NAME: JOB2
stages:
- build
- test
通过 .base_job,我们为所有作业指定了公共的 before_script 和 after_script。然后,对于构建和测试阶段,我们又分别创建了扩展了 .base_job 的 .build_template 和 .test_template。最后,我们创建了两个具体的作业 job1 和 job2,它们扩展了各自的模板并添加了一些特定的配置。.base_job作为模板,.build_template和.test_template去继承它,如果二者都有相同的key,则使用子类的value覆盖父类,同理job1和job2。