GitLab merge request 脚本

介绍我的编写的 GitLab merge request 脚本,包括脚本每次版本迭代的内容、脚本的功能等。

GitLab merge request 脚本
Photo by Pankaj Patel / Unsplash

背景

2022 年 5 月,我们把公司的 iOS 项目从 phab 迁移到了 GitLab 上。

迁移之前,我们提代码合并可以用 arc 指令,它会自动 lint 并创建一个 land 请求,抛出链接。点击链接就可以找同事领导合并代码了。但迁移之后,在不借助三方工具(比如 Tower 高级版)的情况下,创建一个 GitLab 的 merge request (以下简称 mr)需要提出人先把分支 push 上去,然后在 GitLab 的工程页面点击创建 mr,填写 title 等信息。然后再把创建的 mr 地址复制给同事领导。这一套流程无疑是很繁琐的。

初版 2022.07

在 GitLab 的官方文档里,我发现了执行 git push 指令时携带一些参数可以直接创建 mr,并且会在终端输出 mr 的链接。

借着 GitLab 的这个特性,我用 shell 语言写了初版的脚本,文件名是 mergeRequest.sh

脚本大致流程:

  1. 检查是否有未提交的改动。如果有,终止脚本
  2. 要求输入目标分支。默认是 master,可以直接回车
  3. 要求输入 mr 标题。默认是最近一次提交的信息,可以直接回车
  4. 获取当前分支(源分支)
  5. 从当前分支上创建并切换到一个缓存分支
  6. 将缓存分支 push 到 remote。push 的时候携带了创建 MR 需要的一些信息
  7. 将分支切回本地的源分支,并删掉缓存分支
  8. 输出 mr 链接

初版 shell 脚本为了能截获 git push 后的输出,以便在输出中拿到 mr 链接,是把输出重定向到了一个 txt 文件里,然后读取文件、正则匹配出 mr 链接。

初版 shell 脚本流程

mergeRequest.sh 脚本在速度上是非常快的,推广给了几位同学,他们也非常乐意在开发过程中使用。

二版 2023.04

公司在 2023 年上半年推出了几项新的开发「政策」,要求在 review 比较大的 mr 时必须添加几个评论,另外 iOS 主工程上的 mr 必须补上组件库的 mr。

我入职时,我们的 iOS 开发人员就在搞组件化了。到 2023 年,主工程几乎就是个壳子了。因此我们主工程的 mr 几乎都是对 Podfile 的改动,改一下组件库的 commit。但 review 时,光看 Podfile 的改动肯定是不行的,需要提出人把组件库的 mr 或者 commit 也贴出来,方便 review 具体改动代码。

这样就对我的脚本提出了新的需求——创建 mr 时根据 Podfile 改动获取相关组件库的改动,并且把组件库的 mr 放到主工程的 mr 里。

这时候再用 shell 实现肯定是不行了。因此我转向了使用 Python,并且借助了 python 的 python-gitLab package。这个 package 能够读取和操作 GitLab 上的工程。

使用 Python 写的脚本文件是 createMR.py。但是每次用类似 python createMR.py 的指令来执行这个脚本有点麻烦。我就用 shell 重新包装了一下,写了 createMR.sh 脚本。这个脚本内部默认执行 createMR.py,但也可以通过 --fast 参数来指定使用初版的 mergeRequest.sh。毕竟初版的脚本速度快,不需要获取各种工程配置,不需要处理 Podfile,在组件库中创建 mr 还是挺方便的。

在写二版脚本时,我发现我自己角色的权限有限,没法直接用 python-gitLab 提供的 mr 创建 API。不得不用 os.system 模拟 shell 指令,像初版脚本那样在输出重定向文件里找到 mr 链接。

二版脚本的整体流程是和初版差不多的,只不过多了对 Podfile 的处理,mr 的 description 里面带了相关组件库的 mr 或者 commit 链接。

GitLab 截图

三版 2023.06 ~ 2023.07

机器人消息

这一版在二版的基础上添加了发送飞书机器人消息的支持,添加了懒人模式,以及支持了本地自动更新脚本仓库。

机器人消息是利用了飞书机器人提供的 webhook 能力。我没有申请高级版机器人,用的是普通版。高级版机器人需要企业审核,能随意给单个用户发送消息,还能获取用户信息,普通版只能在指定的群里发消息,但也够用了。

创建飞书机器人

机器人消息是在 mr 创建完成后发送的,发送前会弹出多选选择器,选择自己想要 @ 的人。

懒人模式

懒人模式是一个非常 feature 的功能,大大提高了创建 mr 的效率。

懒人模式利用 python-gitlab 提供的 API,在 Podfile 列出的所有组件库(除三方外)里,检索各自的 commit 是不是组件库主分支最新的 commit。比如下面主工程 Podfile 列出的 KEPPrimeKitModule,对应的 commit 是 42fba9031a8f7de1b0b4bf97f7daa1f8b3bf1879,如果 KEPPrimeKitModule 仓库主分支的 commit 不是 42fba9031a8f7de1b0b4bf97f7daa1f8b3bf1879,那就说明这个仓库还有提交没有合并到主工程。

pod "KEPPrimeKitModule", :git => "git@gitlab.gotokeep.com:ios/ug/KEPPrimeKitModule.git", :commit => "42fba9031a8f7de1b0b4bf97f7daa1f8b3bf1879", :inhibit_warnings => false

检索时会过滤掉超过七天没有合并的提交,因为这样的提交大概率是作者本人也不想合并的。另一方面,既然我想用懒人模式创建 mr 了,那我肯定是刚刚在组件库提交了改动。

检索完成后,会显示一个多选列表,脚本使用者可以选出来自己想要更新的组件库。选择完后,会再次弹出来一个单选列表,这个列表列出来的是刚才选择的那些组件库里最新提交的 commit message,以便生成 mr 的 title,当然也可以自己输入。脚本会在 Podfile 里修改已选择组件对应的 commit,并且自动 git commit,message 当然是刚才选择的 message 了。

上面操作做完后,剩下的流程就跟二版类似了,最后会发送机器人提醒消息。

以下是懒人模式的流程(画面下方的 sublime merge 界面是为了展示脚本对仓库的改动):

0:00
/0:50

懒人模式

仓库自动更新和依赖自动安装

脚本起初只有我和我旁边的几位同事在用,后来别的组同事也在用了。这样的话,每次脚本仓库有 feat 或者 fix 时就不能口头说 pull 脚本仓库最新代码了。我就写了个 repo_update_check.sh,放在了 createMR.sh 的最后面执行。每次 mr 创建完成后,就会触发脚本仓库的自动更新,就像软件更新版本一样。

这一版还引入了几个三方库,比如 pick。有些同事第一次用脚本时,电脑里很大可能没有安装这些 package,就会报错。所以我在脚本的入口加了依赖的检测和自动安装。

# 安装依赖
declare -a arr=("python-gitlab" "GitPython" "pick" "dacite" "numpy")
for package in "${arr[@]}"
do
	# echo "$package"
	env_check=$(pip3 --disable-pip-version-check list | grep -F "$package")
	if [[ -z "$env_check" ]]; then
		echo "$package 没有安装. 安装中..."
		pip3 install "$package"
	fi
done

第四版 2023.09 ~ 2023.10

这一版主要就是引入了服务端,代码 Sirius 部署在我的 DO 服务器上。

新功能包括:

  1. mr 合并后,发送机器人消息通知提出人。这样提出人就不需要时不时刷新 mr 网页看看代码有没有被合并了。
  2. mr 创建后,超过半个小时没有被合并的话,会定时向群里发送提醒消息,@ 相关人员。这个定时任务只会在工作日的 10 点到 19 点执行。
待处理通知
定时提醒
已合并通知

这一版的更新灵感来源于 GitLab 的 webhook 能力。GitLab 有很多事件可以触发 webhook,比如 mr 的创建/合并/关闭,pipeline 事件,push 事件,issue 事件等等。想要使用这个,就需要咱们在 GitLab 仓库的设置里添加一个 webhook 地址,以及选择监听的事件。事件触发 webhook 时,GitLab 就会向配置的那个 webhook 地址发送一个 post 请求,请求体里携带了事件的数据。

我用 Spring Boot 在我的服务器上部署了一个 post 接口,并把接口地址配置在 webhook 里。这样当有 mr 被合并时,我就能通过这个接口拿到事件的数据,接着再调用飞书机器人的 webhook,发送提醒消息,告知提出人 mr 已合并。

做定时提醒的话,需要我的接口在收到 post 请求后,把数据存到数据库里,主键是 mr 的 iid (没打错,是两个 i)。这样定时任务触发时,遍历一下数据库数据,发送提醒消息就好了。然后收到 mr 合并事件时,再把数据里对应 iid 的数据删掉,防止无用数据累积。

数据库用的是开源的 MariaDB,不得不说占用内存是真高。我的服务器一共才 2GB 内存,这个服务启动后内存占用就涨到了 500MB。优化空间还是挺大的。

开发的时候我是在本地的 Docker 环境里运行一个 MariaDB 服务,要不然执行时会出现连接数据库失败的错误。部署后可以在本地用 DataGrip 这样的软件连上服务器上的数据库,进行数据查看或删除。

这一版是我写这篇文章时的最新版。功能基本上闭环,感觉也没有什么新功能新灵感可以往上加了。如果非要加的话,那就是和 CI/CD 的深入集成吧,又或者加入评论通知啥的,不过那样需要向公司审批高级版的飞书机器人。最近可能要离职,没有心思和精力搞了。也许到了下家公司,我做的这脚本就派不上用场了,不过那是后话了。

最后,简单总结一下。mr 脚本库从去年开始迭代到现在,功能越来越多,越来越方便,使用的同事也越来越多。它给同事们带来了工作效率上的提升,我是感到非常开心和有成就感的。当然我也从这一过程中学到了很多东西,比如我全栈梦想里面的服务端开发,以及软件(我是把它当做了一个没有 GUI 的软件)的设计。一开始脚本库用的是 ini 作为配置文件格式,但后来迁移到了 json,如果一开始就想到用 json 的话,就不需要写一堆迁移适配的代码了。

「磨刀不误砍柴工」,「工欲善其事,必先利其器」。把研发的效能工具做好,是真的能提高咱们的生产力。