制定你的GitLab流水线任务
2021-10-13
最近要将开发的 npm 包合入 CICD 流水线里,摸索着完成了这个搭建。
本文默认你已了解 CICD 是什么,并且 GitLab 具备必要条件:如可用的 Runner。
一个流水线服务需要以下几个基本要素:
.gitlab-ci.yml
文件- 可用的
Runner
- 执行某个流水线任务的脚本文件
编写配置文件和脚本
.gitlab-ci.yml
文件是必须的,它定义了流水线任务如何执行。而我们的任务肯定要在某个环境中执行,Runner 就是一个具备运行条件的环境,可能是某个服务器,也可能是某个 Docker 容器,这里不涉及 Runner 的搭建,因此默认已经有可用的 Runner。
下面是一个示例文件:
stages:
- code_scan
- eslint_check
代码扫描:
stage: code_scan
tags:
- code-check
script:
- cd ${CI_PROJECT_DIR}
- chmod +x ./.gitlab/ci/codeCheck.sh
- ./.gitlab/ci/codeCheck.sh encore
allow_failure: true
when: always
only:
variables:
- $UEDC_MERGE_REQUEST_APPROVED == "true"
eslint检查:
stage: eslint_check
tags:
- eslint-check
script:
- ./.gitlab/ci/eslintCheck.sh encore
only:
- pushes
stages
它定义了整个流水线的阶段,你的流水线的任务会按照这个阶段的编写顺序一步步来执行。
接下来就需要指定 Job,即流水线要做的事情。比如我这里就制定了 代码扫描
和 eslint检查
两个任务。 以 代码扫描
为例来讲述 Job 的内容。
stage
它表示 代码扫描
这个任务属于哪一个阶段,前面说了 stages 会按照定义的顺序来执行,也就是说如果多个 Job 都属于同一个 stage,那么这些 Job 是同时执行的。
tags
它用来指定这个 Job 在哪个特定的 Runner 上执行,因为不同的任务可能需要不同的运行环境,一个公用的 Runner 无法满足。
script
它定义了这个 Job 具体要做的事情。如果比较简单,那么就可以直接在这里写上脚本命令;如果比较复杂,则建议将其单独写在某个文件里。这里是写在了 codeCheck.sh 这个 shell 文件里。
allow_failure
它表示这个 Job 是否允许失败,即它是否会阻塞剩余的 CI 执行
when
它指定这个任务的执行时机。有以下字段可配置:
- on_success: 前面任务成功
- on_failure: 前面任务失败
- always: 不管怎么样都执行
- manual: 自己手动在 GitLab页面上去点击触发任务
only
这个字段定义 Job 在什么时候被创建,注意这个【创建】,说明从过程上来说它是在 when 之前。
它的内容还蛮多的,详细可以见这里。简单来说你可以指定:
分支名
:比如配置为/^test-.*$/
,则该 Job 只会在 test- 开头的分支下运行关键字
:比如 eslint 检查就是使用了 pushes 关键字,表示在执行 git push 时创建变量条件
:如代码扫描就是通过 UEDC_MERGE_REQUEST_APPROVED 这个环境变量来决定是否创建 Job
顺带一提,示例文件中代码扫描任务 script 中 CI_PROJECT_DIR
是 GitLab的环境变量,这里指的是仓库存放代码的路径,比如 CI_PROJECT_NAME
是当前仓库名。
接下来便是编写各任务的脚本代码,这个当然就是具体问题具体编写了。
#!/bin/sh
echo "这里是代码扫描任务"
npm i codeScan -D
codeScan check ./src
if [ $? != 0 ]; then
echo "代码扫描失败";
exit 1;
fi
echo "代码检查结束"
至此一个可用的流水线服务就写完了,还是很简单的。
结合 GitLab webhook
在实际的搭建中,可能由于 GitLab版本原因导致某些配置/字段不支持,因此需要结合 webhook 来解决。这里以我遇到的问题为例。
我期望代码检查是在提交合并请求的时候执行,要等扫描没有问题或者扫描出来的问题被解决的情况下,该分支才允许被合入。Gitlab 可以通过配置 only 字段来解决这个实现这个操作:
only:
- merge_request
但还是由于版本原因,该字段不支持,因此只能另辟蹊径。
在 GitLab中,我们可以对项目配置 webhook,配置路径在项目左侧菜单中 设置->集成
。绑定项目触发的事件和一个自己的 web 服务后,GitLab 会在这个事件发生时向 web 服务发送 post 请求。比如我勾选触发器事件为合并请求事件
,配置服务链接地址为 https://test.com/merge_reqest
,那么就可以实现在提交合并请求时告知我的 web 服务,同时 GitLab 会携带很多有用的数据过来供我操作,那么通过配置 webhook 以及结合 GitLab API,便可以在自己的 web 服务中实现代码扫描流水线任务的触发。
由于 webhook 需要安全令牌来验证接收信息的有效性,它将通过 HTTP 头的 X-Gitlab-Token 发送。这个 token 是在我们配置触发器时要配置的,它的生成方法是:
- 先在 GitLab 该项目到左树菜单中
设置->CI/CD->流水线触发器->增加触发器
,此操作会生成一个令牌 - 在
设置->集成
页面中,将刚刚生成的令牌配置为 webhook 的安全令牌
接下来实现这个 web 服务。 web 服务的路由为 /merge_request
,该路由处理逻辑的伪代码如下:
// headers 和 body 数据是由 GitLab 的请求带过来的数据,根据具体实现该服务的框架来获取
const token = headers['x-gitlab-token'];
const {
projectId, // 该项目的id
source_branch, // 当前提交合并请求的分支
target_branch, // 要合入的目标分支
} = body;
// 要添加到 CI 中的变量,比如代码扫描中要用到的 UEDC_MERGE_REQUEST_APPROVED 字段
const variables = 'variables[UEDC_MERGE_REQUEST_APPROVED]=true&variables[UEDC_MERGE_REQUEST_IID]=123';
// 触发流水线的 api
const triggerPipeline = `[你的gitlab地址]/api/v4/projects/${projectId}/trigger/pipeline?ref=${source_branch}&token=${token}&${variables}`;
// 发送请求
http.post(triggerPipeline);
这样便可以在提交合并请求时通过 web 服务来触发流水线执行。