使用hook规范GitLab提交的用户名和邮箱
背景
使用GitLab的时候,开发者是可以随意设置其用户名和邮箱的。
git config --global user.name "abc"
git config --global user.email "123"
像我这样设置,代码依然可以正常提交,在gitlab上查看的时候显示的用户名是“abc”。
Git本身精神就是协作,是自由、平等,而非集权式的代码库。这样设计也没毛病,但是在公司的代码管理中,这样就很不好,尤其是在开发 Leader review 代码的时候,如果没有正确配置用户名可能都不知道这代码是谁写的。
人的问题不一定都要用技术来解决,用行政手段也可以达到目的。但是咱们是搞技术的,当然首先想到的是技术能不能解决咯。
使用hook机制校验提交
git 中 hook 有很多种,有系统钩子
,服务器钩子
,客户端钩子
等等。要实现这个需求,使用服务器钩子最为合适。
服务器钩子又分为可应用于全局的和单个仓库的,更多的介绍官方文档:Git server hooks | GitLab
这里我要为所有仓库建立统一的规范,因此使用服务器全局钩子,在 custom_hooks/pre-receive.d
目录下编写脚本实现校验。
#!/usr/bin/env bash
if [[ "$GL_PROTOCOL" == "web" ]]; then
exit 0
fi
ACCESS_TOKEN=XXXXXXXX # 用高权限的用户建一个访问令牌(access token),拿到token放这里
GL_URL=http://gitlab-webservice-default:8181 # GitLab访问URL
res=`curl -s -H "PRIVATE-TOKEN:${ACCESS_TOKEN}" ${GL_URL}/api/v4/users/${GL_ID#user-}`
GL_USER_EMAIL=`echo ${res} | jq -r '.email'` # 依赖jq,如果不想用jq,可以参考我另一篇文章 纯shell解析json
GL_NICKNAME=`echo ${res} | jq -r '.name'`
if [[ ${GL_USER_EMAIL} == "" ]] || [[ ${GL_USER_EMAIL} == null ]]; then
echo "ERROR: User not set email."
exit 1
fi
if [[ ${GL_NICKNAME} == "" ]] || [[ ${GL_NICKNAME} == null ]]; then
echo "ERROR: User not set username."
exit 1
fi
zero_commit="0000000000000000000000000000000000000000"
excludeExisting="--not --all"
while read oldrev newrev refname; do
if [ "$newrev" = "$zero_commit" ]; then
continue
fi
if [ "$oldrev" = "$zero_commit" ]; then
span=`git rev-list $newrev $excludeExisting`
else
span=`git rev-list $oldrev..$newrev $excludeExisting`
fi
for COMMIT in $span; do
AUTHOR_USER=`git log --format=%an -n 1 ${COMMIT}`
AUTHOR_EMAIL=`git log --format=%ae -n 1 ${COMMIT}`
COMMIT_USER=`git log --format=%cn -n 1 ${COMMIT}`
COMMIT_EMAIL=`git log --format=%ce -n 1 ${COMMIT}`
if [[ ${AUTHOR_USER} != ${GL_NICKNAME} ]]; then
echo -e "ERROR: Commit author (${AUTHOR_USER}) does not match the current gitlab user (${GL_NICKNAME})"
exit 20
fi
if [[ ${COMMIT_USER} != ${GL_NICKNAME} ]]; then
echo -e "ERROR: Commit user (${COMMIT_USER}) does not match the current gitlab user (${GL_NICKNAME})"
exit 30
fi
if [[ ${AUTHOR_EMAIL,,} != ${GL_USER_EMAIL,,} ]]; then
echo -e "ERROR: Commit author's email (${AUTHOR_EMAIL,,}) does not match the current gitlab user's email (${GL_USER_EMAIL})"
exit 40
fi
if [[ ${COMMIT_EMAIL,,} != ${GL_USER_EMAIL,,} ]]; then
echo -e "ERROR: Commit user's email (${COMMIT_EMAIL,,}) does not match the current gitlab user's email (${GL_USER_EMAIL,,})"
exit 50
fi
done
done
验证
[root@host test]# git config --global user.name "abc"
[root@host test]# git config --global user.email "123"
[root@host test]# echo test >> README.md
[root@host test]# git add README.md
[root@host test]# git commit -m "test"
[main a6ff85c] test
1 file changed, 1 deletion(-)
[root@host test]# git log
commit a6ff85c1588b3c84b15859651874938fdca834fd
Author: abc <123>
Date: Tue Dec 13 11:09:55 2022 +0800
test
commit b14089d2fe55763d589da7702b06e7ffd57d63ba
Author: 陈日志 <[email protected]>
Date: Mon Dec 12 14:49:37 2022 +0800
first commit
[root@host test]# git push
Counting objects: 5, done.
Writing objects: 100% (3/3), 220 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
remote: ERROR: Commit author (abc) does not match the current gitlab user (陈日志)
To https://git.test.com/chenrizhi/test.git
! [remote rejected] main -> main (pre-receive hook declined)
error: failed to push some refs to 'https://git.test.com/chenrizhi/test.git'
可以看到,修改了用户名和邮箱,提交代码会被拒绝。
参考:platform-samples/commit-current-user-check.sh at master · github/platform-samples · GitHub
大佬牛逼!
请教您一下。
在上文脚本中的用户校验逻辑,是建立在 “Push提交人的user信息 == commit中的 author 信息 == commit中的committer信息”之上。
可是如果在 git merge 或者 cherry pick 的场景下,是否可能存在 committer 与 author 不相同,导致校验失败,错误拦截了的情况?
不会,只会校验新的commit。你可以试下各种场景,有问题再沟通。