CI/CD必知(落后master分支检测)
背景
来公司一年多,业务之余一直在参与做BU自己的前端发布平台;以前我们的构建底层(CI/CD)大多依赖于集团的能力,所以经常一个应用某个迭代发布之后,其他迭代再构建部署,就会报错,这时就需要打开集团系统的构建日志,会看到类似下面的报错提示:
master 分支有新的提交,请合并分支后再继续部署今年由于有新的业务系统要对接,我们需要有自己的CI/CD底层,而集成这个能力,在最初我也是走了很多弯路,故记录一下。
为什么落后检测很重要 目前多数简单的前端构建部署,都是以分支来打理;比如我们完成一个需求时:我们会从master主干拉一个迭代分支,然后我们会用这个分支在
开发-测试
等环境做构建部署,线上发布前,我们再将更改合并回主干。上面的流程,是一个很通用的流程,但有可能也有其他的做法,比如做的特别前沿的:部署分支每次都是从master主干拉取,然后合并迭代分支,然后再部署。这种情况不在今天的讨论范围之内,因为这种策略不会有这个落后的烦劳。
回到前面,为什么我们需要做落后检测?因为很多时候,一个应用会存在多个人维护,存在多个迭代(A,B),这里我们假设10.10号这两个分支都是从主干同一节点拉取。A,B自己正常开发部署,然后A的迭代在10.22号上线了,发布完时,代码合并到了master;然而B 迭代10.24号上线,但并不知道A有发布,如果这时部署系统没有master分支落后检测,B就顺顺利利的用分支B上线了,这会造成什么后果?
- B上线后,分支合并回主干报错(大概率),后面的迭代没有这个功能,导致后面会有故障发生;
- A迭代上的功能没了,线上事故(重一点,3.25)。这锅算谁的?A的?B的?还是平台的?在我看来,这个锅就是平台的
- 上线前有灰度阶段,如果先合了master,如果灰度发现有bug,或者其他迭代要先上,退出灰度就很麻烦,这时主干分支也会被污染;
- 从master重新拉分支,合并代码构建部署,如果是手动操作,这样对开发者而言,会显得很麻烦;如果平台自己来做这个操作,会有代价,而且都集成这个能力了,就可以直接再往前建设一点,走前面提到的前沿方案
首先要明白,什么情况,我们称之为落后master,上个图:
文章图片
文章图片
由于feat/1.0.0 发布后merge到了master,导致feat/1.0.1 落后master分支, 这种落后与1.0.1提交多少commit无关,只与是否和master分支commit信息同步相关;
其实去网上查了关于分支比对这方面的资料,发现基本都是shell脚本处理, 在stackoverflow有一个帖子和我述求基本一致:链接地址
Is there a way to do a diff between my branch and master that excludes changes in master that have not been merged into my branch yet?
里面的高赞答案,就提到了:
git diff branch...master
,并很好心了给了官方链接解释;
里面提到了一个概念叫
merge-base
, 等同于两个分支共同的起点,比如上面两个分支,这个点就是X;
git diff master...feat/1.0.1 is equivalent to git diff $(git merge-base master feat/1.0.1) feat/1.0.1 is equivalent to git diff commitX commitF
所以当运行:
git diff feat/1.0.1...master
, 可以看到:文章图片
从上图的结果可以看到,列出的差异点只有A/B两次提交;
所以两次差异比较就等同于:
git diff X B
;That's it, wo got it!!!
但我们的平台是基于gitlab API的,没法直接运行这种命令行,幸运的是互联网是无所不能的,这个API就是:
gitlab.Repositories.compare
// Repositories.compare(projectId: string | number, from: string, to: string, options?: Sudo)
const info: any = await gitlab.Repositories.compare(projectId, commitId, 'master');
注意一下from和to的位置,from是feat/1.0.1, to是master,这个很重要;
得到的info结果长下面这样:
{
"commit": {},
"commits": [],
"diffs": [],
"compare_timeout": false,
"compare_same_ref": false,
"web_url": "https://gitlab.example.com/thedude/gitlab-foss/-/compare/ae73cb07c9eeaf35924a10f713b364d32b2dd34f...0b4bc9a49b562e85de7cc9e834518ea6828729b9"
}
所以如果分支没有落后master,即master分支没有新的提交,那么commit就是一个null值,commit 和 diffs就是一个空数组;另外要注意compare_timeout的值,如果分支比对工作量过于庞大,则有可能造成超时,compare_timeout为true,那么这时检测也是无效的;
文章图片
还有一个需要注意的,就是这个api第四个参数是个option,option.straight 为真时,这时的diff结果,就不是我们预想的结果,所以调用时也要注意。
野路子分享 其实事情,并不像上面描述的那样顺利,最初我们因为时间紧迫没有发现compare这个API,而是采用了递归的方式,就是不断去回溯分支节点,试图找到和当前master的commitId相同的节点,最大查找范围为向下8层,如果超过8层还没找到,那就判断为落后master分支,否则就是安全的,我个人觉得这个算法实现不低于leetCode的中等题;
在一些简单的迭代分支管理上,上面的算法还能奏效,但对于过于复杂的分支,要不就是超时,要不就是超过了8层;超时是因为如果一个节点不匹配,就需要调API拿到下一堆子节点,API调用过程是非常耗时的:
// 部分代码实现
if (level > MaxLevel || this.globalFinish) {
return false;
}const reocords = []
for (let i = 0;
i < parentIds.length;
i++) {
const currentId = parentIds[i];
if (this.lookedIds.has(currentId)) {
continue;
}
this.lookedIds.add(currentId);
if (currentId === masterId) {
has = true;
break;
}
const commitInfo = await gitlab.Commits.show(projectId, currentId);
reocords.push({
gitlab,
projectId,
parentIds: commitInfo.parent_ids,
masterId,
level: level + 1,
recursion: true
});
}
上面这个算法,在平台刚上线时,还运行了一两天,还没遇到什么问题;但在知道compare这个算法时,我们就果断换了,连夜测试上线,因为官方的API更可靠。
这个分享到此为止,如果你看到了这里,希望对你有用。
【CI/CD必知(落后master分支检测)】
文章图片
推荐阅读
- (Android面试必知必会系列)Android事件分发
- 我的生意经(13)(在变化中发展之落后追赶版)
- Python开发者必知的13个Python|Python开发者必知的13个Python GUI库(转)
- 我一直在努力,忽然被当头一喝,发现自己却还是迷失和落后在生活中
- [30天蜕变之路]社群创业必知的降龙十八掌
- Git上传项目提示Push|Git上传项目提示Push rejected: Push to origin/master was rejected解决办法
- 怎么在EarMaster中使用麦克风
- 算法之使用Master Theorem估算时间复杂度
- json虽然简单,但这些细节你未必知道
- .NET+C#|ASP.NET 2.0中Theme、MasterPage和代码国际化