醉卧草庐听风雨

君子藏器于身,待时而动

0%

基于git提交日志的增量持续集成

之前介绍过一个叫drone的持续集成工具,效果虽不错但每次都要对项目进行全量的编译部署,不知是用法不正确还是其他原因目前的持续集成工具基本不支持部分项目的编译。于是决定动手写一个基于git提交记录的持续集成脚本。

实现目标

这里的持续集成脚本需要实现如下目标:

  • 仅被改动的项目进行编译部署,其他项目不进行构建
  • 项目存在依赖关系,实现项目有序的构建部署

编写脚本

总体思路

一句话概括:基于提交记录找出被修改的项目然后按序编译部署

查看被修改的文件

如何基于修改记录找到被修改的项目?查阅git的官方文档,发现在git log章节中,参数–name-only可以显示此次提交的文件名称,例如输入如下命令

1
2
3
4
5
6
7
8
9
10
11
12
13
# 查看最近三次的提交记录
git log -3 --oneline
# 输出如下内容
23b29c9e4 (HEAD -> Boss3.22, origin/Boss3.22) xxxxxxxxxx
0c7f97340 xxxxxxxxx
6d3d5e3b1 xxxxxxxxx
# 查看6d3d5e3b1至最后一次提交的文件
git log -1 --name-only --pretty=format:'' 6d3d5e3b1..
# 输出如下内容
boss-master/boss-business/src/main/webapp/buyCloudPro.html
boss-master/boss-business/src/main/webapp/js/base.js

boss-master/boss-dal/src/main/java/com/fastonz/bossCore/dao/IUserAccountDao.java

例子中可以看到,自6d3d5e3b1之后的两次提交0c7f97340、23b29c9e4分别改动了boss-dal和boss-business,那么这两个即为需要被编译部署的项目

查看被修改的项目

知道了哪些文件被修改,还需要定位至具体项目,这时就需要对被修改的项目进行正则提取,需要用到如下命令:

  • grep 通常是用来查找信息,但是它还支持正则提取,grep -P '(?<=boss-master/)[a-z-]+' -o将会得到boss-master后的子目录名称,即:对应的项目名
  • sort 用于排序的命令,因为同一个项目可能存在于多个提交记录里,通过排序处理将相同项目排列在一起
  • uniq 将排序后的文本去重,达到只处理单个命令
1
2
3
4
5
# 查看6d3d5e3b1至最后提交影响的项目
git log --name-only --pretty=format:'' 6d3d5e3b1.. | grep -P '(?<=boss-master/)[a-z-]+' -o | sort | uniq
# 输出如下内容
boss-business
boss-dal

有序编译部署

现在知道哪些项目需要编译部署,如何实现项目的有序编译部署?其实比较简单用shell中的数组即可

1
2
3
4
5
6
7
8
9
10
11
12
13
# 提取出相关项目
projects=`git log --name-only --pretty=format:'' ${last}.. | grep -P '(?<=boss-master/)[a-z-]+' -o | sort | uniq`
# 组织项目顺序
order=('boss-dal' 'boss-admin' 'boss-business')
for p in ${order[*]}
do
# 按顺序循环查找出现的项目
if echo $projects | grep -qw $p ; then
echo "----------------- start build ${p} ------------------------"
echo '----------------------- start upload file -----------------------'
echo "----------------------- start deploy ${p} -----------------------"
fi
done

完整例子

组合起来就是如下的完整脚本,为了方便使用加入了一个参数用于指定上次提交的版本号

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#!/bin/bash
start_time=`date +%s`
echo 'start at '`date`
# 指定git路径
source_dir='/fsmeeting/data/build/cloud_boss'
cd $source_dir
# 获取上次构建版本号
export last;
# 获取参数,若存在参数则用第一个参数当最后一次构建的记录
if [ $# -ge 1 ]; then
last=$1
else
last=`git log --oneline -1 --pretty=format:'%h'`
fi
# 拉取最新代码
git pull
# 获取待处理的项目
projects=`git log --name-only --pretty=format:'' ${last}.. | grep -P '(?<=boss-master/)[a-z-]+' -o | sort | uniq`
cd boss-master
# 指定顺序
order=('boss-dal' 'boss-admin' 'boss-business')
for p in ${order[*]}
do
if echo $projects | grep -qw $p ; then
echo '----------------- start build '$p' ------------------------'
echo '----------------------- start upload file -----------------------'
echo "----------------------- start deploy ${p#*-} -----------------------"
fi
done
# 计算运行时间
end_time=`date +%s`
duration=`expr $end_time - $start_time`
echo "duration time $duration s"

写在最后

脚本目前用linux的cron定时任务来处理,可以实现定期的编译部署,目前这样已经符合日常的使用,但其实也可以使用git的钩子或者常见的webhook来触发,达到推送后的实时编译部署。当然这只是对开发过程中的快速迭代较为友好,后期为了保证项目全局统一还是全量编译较为稳妥可靠。