背景
当我们接收一份新的代码,代码拿到手要做的第一件事就是 git log,看看这份代码的提交记录,最近提交的情况,做了些什么。但往往看到的 git log 杂乱无章,不知道每次提交到底是做了些什么。由此可见,在团队中,CHANGELOG 的重要性不言而喻,不仅有助于他人帮忙 review 代码,熟悉代码,也能高效的输出 CHANGELOG,对项目管理也至关重要。我们本文介绍使用 git 的服务端 hook 来针对 change log 进行校验,拦截不符合我们规范的提交。
服务端 hook 介绍
服务端 git hook 分为三种,分别是 pre-receive、update、post-receive,这三个步骤就是我们本地 push 完代码服务端要做的事情,如图所示:
我们可以在 pre-receive 阶段来做提交信息的校验,如果不符合我们的要求,直接返回非 0,则该推送便不会推送到 gitlab 仓库中去。
配置服务端 hook
环境配置
gitlab 版本:13.2
hook 配置
- 找到要配置仓库在 gitlab 中存储的路径,但因 gitlab 的仓库自某个版本开始采用 hash 存储,我们想要知道仓库对应的物理路径,需要到 gitlab 的 postgresql 数据库中的表 project_repositories 中,根据 project_id 能拿到对应的物理路径;
- 当拿到仓库对应的物理路径后,我们打开,目录如下:
#!/bin/bash
echo "开始提交信息检查..."
# 从标准输入获取本次提交的commit id及分支的信息
read normalInput
ARR=($normalInput)
parentCommitId=${ARR[0]}
currentCommitId=${ARR[1]}
branch=${ARR[2]}
echo "您提交的分支为:$branch"
# 获取coomit的信息,用户,邮箱,msg等
user=$(git log --pretty=format:"%an" $currentCommitId -1)
echo "提交者为:$user"
commitDate=$(git log --pretty=format:"%cd" $currentCommitId -1)
echo "提交日期为:$commitDate"
msg=$(git log --pretty=format:"%s" $currentCommitId -1)
echo "提交的备注为:$msg"
flag=$(echo $msg | grep "modify.*")
if [ -z "$flag" ]; then
echo "[ERROR]提交信息校验未通过,需以modify开头"
exit 1
fi
Python版本的代码如下:
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
import sys, re, datetime
import fileinput
import shlex, subprocess
import dateutil.parser
import pytz
def datestring2timezone(datestring,timezone='Asia/Shanghai',dateformat='%Y-%m-%d %H:%M:%S'):
"""将带有时区的时间统一化规定的时区时间
:param datestring:svn/git时间,eg:2011-01-19T05:13:13.421543Z,2018-11-09 17:38:37 +0800
:param timezone:统一时区,默认是中国时区
:param dateformat:转换成时间格式
:return:
"""
local_time = dateutil.parser.parse(datestring).astimezone(pytz.timezone(timezone)) # 解析string 并转换为北京时区
# print(local_time , type(local_time)) # datetime 类型
da = datetime.datetime.strftime(local_time, dateformat) # 将datetime转换为string
return da
print("Begin check your commit info")
"""获取用户提交的信息"""
origin_commit, curr_commit, branch = None, None, None
# 读取用户试图更新的所有引用
for line in fileinput.input():
line_list = line.strip().split()
if len(line_list) >= 3:
origin_commit, curr_commit, branch = line_list[:3]
break
# TODO: 目前2.27.0版本的git有点问题,在部署的时候需要额外注意
# git_version = subprocess.check_output(shlex.split('git --version'), shell=False)
# print("git version: {}".format(str(git_version)))
# which_git = subprocess.check_output(shlex.split('which git'), shell=False)
# print("which git: {}".format(str(which_git)))
# 获取commit的信息,用户,邮箱,msg等
commit_user = subprocess.check_output(shlex.split('git log --pretty=format:"%an" {} -1'.format(curr_commit)), shell=False)
commit_date = subprocess.check_output(shlex.split('git log --pretty=format:"%cd" {} -1'.format(curr_commit)), shell=False)
commit_msg = subprocess.check_output(shlex.split('git log --pretty=format:"%s" {} -1'.format(curr_commit)), shell=False)
# 针对merge request的请求,取最新一条非merge request的提交信息进行判断
RULE_MERGE_REQUEST = r'^Merge branch .*(into|.*)'
if re.search(RULE_MERGE_REQUEST, str(commit_msg), flags=0):
# 获取最新一条非merge request的commit的信息,用户,邮箱,msg等
commit_user = subprocess.check_output(shlex.split('git log --no-merges --date-order --pretty=format:"%an" -1'), shell=False)
commit_date = subprocess.check_output(shlex.split('git log --no-merges --date-order --pretty=format:"%cd" -1'), shell=False)
commit_msg = subprocess.check_output(shlex.split('git log --no-merges --date-order --pretty=format:"%s" -1'), shell=False)
start_date = "2021-07-07 19:00:00"
# 提交日期大于给定的开始时间才校验
if start_date >= datestring2timezone(commit_date):
sys.exit(0)
if not re.search(r'^JIRA-[0-9]{4,6}', str(commit_msg), flags=0):
print("ERROR:Comment must start with DPT-<ID>. E.g.: DPT-1234")
sys.exit(1)
sys.exit(0)
- 在本地尝试推送,推送显示如下,如果不符合规范则无法提交成功
如探索更多关于服务端 hook 的功能,可以与第三方系统,例如 jira 等做交互,打造属于自己团队更适用的工具。