1、背景简介
项目越来越大,代码越来越多,然后就是各种五花八门的代码格式、代码规范。可用自查或者交叉检查,但是都是人工需要费事费力,还不一定能面面俱到,所以必须有一种强制的手段,不折不扣的执行代码规范,不符合规范的无法提交到代码仓库,从而杜绝了不符合规范的代码产生。同时,团队内的代码提交信息也需要遵循一定的格式,一并做校验。
2、gitlab 服务端hooks机制
利用gitlab的 (钩子)hooks机制,关于钩子有客户端、服务端的,本文仅描述服务端的钩子,更多关于钩子的资料参考:
gitlab服务端会有三个主要钩子:
- pre-receive(处理客户端push动作时最先被调用的脚本,以非0值退出拒绝Push,可以用来做注释标准化、代码标准化等);
- update(与pre-receive功能类似,为每个准备更新的分支各运行一次);
- post-receive(是在push之后执行的脚本,可以用来调用后续的持续集成、发邮件通知等)。
而本文的用到的做代码规则校验的就是pre-receive的钩子,通过此钩子执行java –cp命令调用aliyun p3c-pmd的规则校验,然后检查如不符合规范返回错误信息到客户端(idea,git bash等)。
3、P3C-PMD
P3C阿里云的插件,github地址:https://github.com/alibaba/p3c
此处我们用到p3c-pmd插件,需要打包成jar文件,以便gitlab服务端使用,下载源码之后,直接打包(gradle或maven均可),得到 p3c-pmd-2.1.1-jar-with-dependencies.jar ,后面备用;已经打包好的可以直接下载使用:p3c-pmd-2.1.1.jarP3c阿里云的插件-Java文档类资源-CSDN文库
4、代码提交信息格式
提交信息即 git commit message,在常规开发中,可能很多人会忽略提交信息的重要性,对于提交信息的内容会相当随意地进行输入,其实这样在很大的程度上会影响后续的维护和必要情况下的故障的追踪。所以,在此我们需要对提交信息做一个初步规范。
首先,提交信息必须格式化,即提交信息需要按照一定的格式来撰写,当团队中的所有成员都遵循一定的格式的时候,团队的其他成员可以非常方便的阅读内容。且不允许多个task一个提交,可以一个task完整提交,也可以局部提交,但是不允许跨task提交,每次提交不能让项目跑不起来,养成每日多次提交的习惯,我们约定提交信息的格式为:(任务类型后的为英文冒号!!!)
{{Task #}} // 项目管理软件中的任务 ID
{{任务类型 (feat/fix/docs/style/refactor/perf/test/build/chore/revert/merge)}}:{{任务描述}}
再者,我们可以通过项目管理软件中自动从提交信息中完成和任务的关联,提高大家的工作效率。
针对日常开发中可能存在一些并没有实际task的零碎的工作,引入了NOTASK标识,即在提交时备注信息的地方原本需要填入task编号的地方填入NOTASK,用以和普通开发任务进行区分,如:
NOTASK // 没有实际任务情况下的细小改动
{{任务类型 (feat/fix/docs/style/refactor/perf/test/build/chore/revert/merge)}}: {{任务描述}}
任务分类:
- feat:新增功能
- fix:bug修复
- docs:仅仅修改了文档,比如README,CHANGELOG等等
- style:代码格式改变(修改空白字符,格式缩进,补全缺失的分号等,没有改代码的逻辑)
- refactor:重构代码(没有新增功能或bug修复)
- perf:优化,比如:提升性能、体验、算法等
- test:增加/修改测试用例,包括单元测试、集成测试等
- build:改变了build工具
- revert:还原先前提交的更改
- chore:改变构建流程或者增加依赖库、工具等
- merge:代码合并
- ci:与CI(持续集成)有关的改动
5、配置钩子(所有项目生效,如只需针对单个项目,请自行看网上教程)
1、gitlab所在服务器创建目录:/opt/gitlab/embedded/service/gitlab-shell/hooks/pre-receive.d ;
2、将 p3c-pmd-2.1.1-jar-with-dependencies.jar上传到该目录;
## 校验commit message
validate_commit_message()
{
oldrev=$(git rev-parse $1)
newrev=$(git rev-parse $2)
refname="$3"
#echo 'Old version: '$oldrev
#echo 'New version: '$newrev
#echo 'Branch: '$refname
## git 命令
#GITCMD="git"
## 按时间倒序列出 commit 找出两个版本之间差异的版本号集合 oldrev~newrev
commitList=`git rev-list $oldrev..$newrev`
#echo 'commitList: '$commitList
split=($commitList)
#echo 'split: '$split
# 遍历数组
for s in ${split[@]}
do
#echo “$s”
#通过版本号获取仓库中对象实体的类型、大小和内容的信息
#比如提交人、作者、邮件、提交时间、提交内容等
currentContent=`git cat-file commit $s`
#echo 'Commit obj: '$currentContent
#echo 's==: '$s
#获取提交内容
msg=`git cat-file commit $s | sed '1,/^$/d'`
echo 'commitid:'$s',msg: '$msg
## merge合并分之直接放行
if [[ $msg == *"Merge branch"* ]] || [[ $msg == *"Merge remote-tracking branch"* ]]; then
echo "Merge branch...skip the checking"
else
## 做内容校验
match=`echo $msg | grep -nE "(${regex})"`
#echo 'Match result: '$match
## 找到匹配说明是符合规范的
if [ "${match}" != "" ]; then
## 校验注释长度
msg_length=${#msg}
#echo "Msg length: ${msg_length}"
if [[ ${msg_length} -lt ${COMMIT_MESSAGE_MIN_LENGTH} ]]; then
#echo -e "Error: Commit message should be bigger than ${COMMIT_MESSAGE_MIN_LENGTH} and current commit message length: ${msg_length}"
echo -e "错误: 提交备注信息的长度应该大于 ${COMMIT_MESSAGE_MIN_LENGTH} ,本次提交的备注长度为: ${msg_length} ,请修改后再提交!"
exit 1
fi
### 找到匹配内容做相应处理,如fix ,校验pom文件等
#if [[ "${match}" =~ "fix:" ]]; then
## 如果是修补bug,规范有点获取到fix中的ID,然后调用禅道对外的API关闭,其他场景类似
#fi
# 是否开启校验和main分支
isMaster=$(echo $refname | grep "main$")
if [ $CHECK_MASTER_POM_SNAPSHOT_ON == 0 ] && [ -n "$isMaster" ]; then
# 如果是main分之,并且pom文件发生了变更,判断pom文件是否含有sonapshot的引用
pomfile=`git diff --name-only ${oldrev} ${newrev} | grep -e "pom\.xml"`
if [[ "${pomfile}" != "" ]]; then
#echo $pomfile
## 获取pom文件更新的内容
pomcontent=`git show $newrev:$pomfile`
#echo $pomcontent
## 校验pom文件是否包含snapshot版本
if [[ $pomcontent =~ 'SNAPSHOT' ]]; then
#echo -e "Error: Snapshot version cannot exist in main branch!"
echo -e "错误: main 分支中不能存在快照版本!"
exit 1
fi
fi
fi
## 其他操作
#echo "Commit comments validate Success!"
echo "提交备注格式检查通过!"
else
#echo -e "Error: Commit comments message should be started with [${tips_msg}]..."
echo -e "错误: 提交备注格式错误,请修改后再提交,具体规范请参考团队规范文档"
exit 1
fi
fi
done
}
## 代码校验
validate_code_rules()
{
echo 'Start code analysis!'
oldrev=$(git rev-parse $1)
newrev=$(git rev-parse $2)
refname="$3"
#echo 'Old version: '$oldrev
#echo 'New version: '$newrev
#echo 'Branch: '$refname
TEMPDIR=$BASE_PATH/"tmp"
FILES=`git diff --name-only ${oldrev} ${newrev} | grep -e "\.java$"`
committer=`git log -1 $newrev --pretty=%ce`
if [ -n "$FILES" ]; then
for FILE in ${FILES}; do
mkdir -p "${TEMPDIR}/`dirname ${FILE}`" >/dev/null
git show $newrev:$FILE > ${TEMPDIR}/${FILE}
done;
MAIN_JAVA_PATH=$TEMPDIR'/src/main'
#echo 'Temp update files path: '$MAIN_JAVA_PATH
#FILES_TO_CHECK=`find $MAIN_JAVA_PATH -name '*.java'`
#echo 'Check files:'${FILES_TO_CHECK}
echo 'Aliyun p3c-pmd check starting.....'
#echo 'Current shell Path:' $BASE_PATH
#echo 'JAVA_HOME:' $JAVA_HOME
#echo 'Root directory for java sources: '$MAIN_JAVA_PATH
#管理员提交暂时过滤掉检查
if [ $committer != 'tangkai2009fei@163.com' ]; then
if [[ $CODE_RULE_TYPE == 0 ]]; then
## 需要阿里云P3C的插件包p3c-pmd-2.0.0.jar与该脚本在同级目录下
echo 'Code analysis for Aliyun-p3c..'
#$JAVA_HOME/bin/java -Dpmd.language=en -cp $BASE_PATH/p3c-pmd-2.0.0.jar net.sourceforge.pmd.PMD -d $MAIN_JAVA_PATH -R rulesets/java/ali-comment.xml,rulesets/java/ali-concurrent.xml,rulesets/java/ali-constant.xml,rulesets/java/ali-exception.xml,rulesets/java/ali-flowcontrol.xml,rulesets/java/ali-naming.xml,rulesets/java/ali-oop.xml,rulesets/java/ali-orm.xml,rulesets/java/ali-other.xml,rulesets/java/ali-set.xml -f text
$JAVA_HOME/bin/java -Dfile.encoding=$FILE_ENCODING -Dpmd.language=$PMD_LANGUAGE -cp $BASE_PATH/p3c-pmd-2.1.1-jar-with-dependencies.jar net.sourceforge.pmd.PMD -d $TEMPDIR -R rulesets/java/ali-comment.xml,rulesets/java/ali-concurrent.xml,rulesets/java/ali-constant.xml,rulesets/java/ali-exception.xml,rulesets/java/ali-flowcontrol.xml,rulesets/java/ali-naming.xml,rulesets/java/ali-oop.xml,rulesets/java/ali-other.xml,rulesets/java/ali-set.xml -f text
RESULT=$?
#echo $RESULT
if [ $RESULT -gt 0 ]; then
echo "代码质量检测不通过,请修改后重试,请参考规范文档:https://github.com/alibaba/p3c"
rm -rf $TEMPDIR
exit 1
fi
echo 'Code analysis success!'
else
echo "在白名单中,临时允许通过"
fi
else
echo 'No java code, analysis end!'
fi
rm -rf $TEMPDIR
}
4、给 pre-receive 文件增加执行权限
chmod +777 pre-receive
5、客户端提交代码进行测试