TT Lab
开始
学习 学习路径 课程

GitLab CI/CD

打了个 nightly 标签,结果发布了版本

在 TT Lab 中继续学习

目标

通过改变各种情况,确认根据分支、tag、文件是否存在、与远程的改动,作业会进入或退出列表,并把规则的顺序和规则所决定的变量,作为部署策略写进配置。

为什么重要

rules 不是作业开始时询问的条件,而是流水线创建时决定列表的规则。因为只应用第一个匹配项,所以条目的顺序就是策略,顺序放错,就会在没有错误的情况下让部署消失,或者让发布发往错误的 tag。changes 会因为与什么比较而结果不同,如果把条件和值(部署目标)分开放置,总有一天两者会对不上。这类差别光读配置很难看出来,必须改变情况去运行才行。

步骤

  1. 把 /root/glci-rules 创建为 Git 仓库,并在 .gitignore 中放入 .gitlab-ci-local/。在 .gitlab-ci.yml 中放入 stages [build, deploy]、没有条件的作业 unit(build,echo "unit log=$LOG_LEVEL"),以及只在 $CI_COMMIT_BRANCH == "main" 时才创建的 deploy-staging(deploy,echo staging),并提交到 main 分支。评分器会在副本中切换到 feature/login 分支,查看 deploy-staging 是否从列表中消失。
  2. 添加作业 release-notes(deploy,echo notes),使其只在 $CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/ 时才创建,并提交。gitlab-ci-local 不读取 Git tag,所以用 --variable CI_COMMIT_TAG=v1.2.0 来模拟 tag 流水线。如果是 v1.2.0,它必须在列表中,如果是 nightly 或没有 tag,则必须不在列表中。
  3. 给作业 publish(deploy,echo publish)的 rules 设置三个条目:有 tag 时 when: on_success,是 main 分支时 when: manual 和 allow_failure: false,其余情况 when: never。提交。在列表中,如果是 main 必须是 manual(allowFailure false),如果有 tag 变量必须是 on_success,如果是 feature 分支则必须没有。
  4. 用 rules: - exists: [Dockerfile] 添加作业 docker-build(build,echo docker)并提交。这个仓库里暂时不创建 Dockerfile。评分器会检查,只有在副本中放入 Dockerfile 时才会生成该作业。
  5. 在 /root/glci-rules-origin.git 中创建 bare 仓库并注册为 origin,push main 之后,运行 git remote set-head origin main。然后用 rules: - changes: ["docs/**/*"] 添加作业 docs-build(build,echo docs),提交并再次 push。评分器会在副本中分别创建修改了 docs 之下内容的分支和只修改了其他文件的分支,检查是否只有前一种情况会生成 docs-build。
  6. 添加作业 deploy(deploy,echo "target=$DEPLOY_TARGET")。rules 是:tag 时 variables: {DEPLOY_TARGET: production},main 时 variables: {DEPLOY_TARGET: staging}。提交并 push。在 main 上运行必须打印 target=staging,给出 tag 变量时必须打印 target=production。
  7. 在 .gitlab-ci.yml 最上面放入 workflow: rules:。main 时是 variables: {LOG_LEVEL: warn},其余情况(when: always)是 variables: {LOG_LEVEL: debug}。提交并 push。在 main 上,unit 日志必须是 unit log=warn,在 feature 分支上必须是 unit log=debug。

参考

只在 main 上创建预发布部署

把 /root/glci-rules 创建为 Git 仓库,并在 .gitignore 中放入 .gitlab-ci-local/。在 .gitlab-ci.yml 中放入 stages [build, deploy]、没有条件的作业 unit(build,echo "unit log=$LOG_LEVEL"),以及只在 $CI_COMMIT_BRANCH == "main" 时才创建的 deploy-staging(deploy,echo staging),并提交到 main 分支。评分器会在副本中切换到 feature/login 分支,查看 deploy-staging 是否从列表中消失。

rules 是在创建流水线时评估的。如果没有任何条件匹配,该作业会作为 'never' 从列表中排除。切换分支后,用 gitlab-ci-local --list-csv-all 比较 when 列。

连 tag 名称的形状都要看,才创建发布说明

添加作业 release-notes(deploy,echo notes),使其只在 $CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/ 时才创建,并提交。gitlab-ci-local 不读取 Git tag,所以用 --variable CI_COMMIT_TAG=v1.2.0 来模拟 tag 流水线。如果是 v1.2.0,它必须在列表中,如果是 nightly 或没有 tag,则必须不在列表中。

匹配运算符(等号后接波浪号)是正则表达式比较。tag 变量只在 tag 流水线中才会被填充,所以如果只看它是否存在,连 nightly 这样的临时 tag 也会发出发布。正则表达式要用斜杠括起来。

只应用第一个匹配项——顺序就是策略

给作业 publish(deploy,echo publish)的 rules 设置三个条目:有 tag 时 when: on_success,是 main 分支时 when: manual 和 allow_failure: false,其余情况 when: never。提交。在列表中,如果是 main 必须是 manual(allowFailure false),如果有 tag 变量必须是 on_success,如果是 feature 分支则必须没有。

从上往下读,只使用第一个匹配的条目。如果把没有条件的 when: never 放在中间,它下面的条目就永远不会被读到,而且不会出现任何错误。要把 manual 作业真正用作审批门禁,请同时设置 allow_failure: false。

只在有 Dockerfile 的仓库中才构建镜像

用 rules: - exists: [Dockerfile] 添加作业 docker-build(build,echo docker)并提交。这个仓库里暂时不创建 Dockerfile。评分器会检查,只有在副本中放入 Dockerfile 时才会生成该作业。

exists 查看的是仓库中是否有该路径的文件。当多个仓库 include 同一个模板时,用它可以在不修改每个仓库配置的情况下,只开启相应的作业。

只在文档有改动的分支上构建文档

在 /root/glci-rules-origin.git 中创建 bare 仓库并注册为 origin,push main 之后,运行 git remote set-head origin main。然后用 rules: - changes: ["docs/**/*"] 添加作业 docs-build(build,echo docs),提交并再次 push。评分器会在副本中分别创建修改了 docs 之下内容的分支和只修改了其他文件的分支,检查是否只有前一种情况会生成 docs-build。

changes 的关键是“与什么相比的改动”。gitlab-ci-local 与远程默认分支(origin/main)比较,所以必须有远程。GitLab 的分支流水线与上一次 push 比较,合并请求流水线则与目标分支比较。

命中了哪条规则,决定部署目标

添加作业 deploy(deploy,echo "target=$DEPLOY_TARGET")。rules 是:tag 时 variables: {DEPLOY_TARGET: production},main 时 variables: {DEPLOY_TARGET: staging}。提交并 push。在 main 上运行必须打印 target=staging,给出 tag 变量时必须打印 target=production。

rules 条目中的 variables,只有在命中该条目时才会进入作业。把条件和值放在同一个地方,“明明是 tag 却发往了 staging”这类不一致,在结构上就消失了。

整条流水线的值在 workflow 中决定

在 .gitlab-ci.yml 最上面放入 workflow: rules:。main 时是 variables: {LOG_LEVEL: warn},其余情况(when: always)是 variables: {LOG_LEVEL: debug}。提交并 push。在 main 上,unit 日志必须是 unit log=warn,在 feature 分支上必须是 unit log=debug。

workflow:rules 决定是否创建流水线,以及要放入整条流水线的变量。不必在每个作业中重复同样的条件。防止合并请求流水线与分支流水线重复的地方也在这里,但这个行为只能在 GitLab 服务器上确认。