本文目录导读:

Git钩子脚本可以通过在代码提交或推送前执行检查来实现问责性门禁(Accountability Gate),确保提交者身份可信、提交信息完整且符合规范,以下是几种常见实现方式:
提交者身份验证
pre-commit 钩子 - 检查作者信息
#!/bin/bash
# 文件位置: .git/hooks/pre-commit
# 获取当前提交者信息
AUTHOR_NAME=$(git config user.name)
AUTHOR_EMAIL=$(git config user.email)
GLOBAL_NAME=$(git config --global user.name)
GLOBAL_EMAIL=$(git config --global user.email)
# 检查是否与全局配置一致
if [ "$AUTHOR_NAME" != "$GLOBAL_NAME" ] || [ "$AUTHOR_EMAIL" != "$GLOBAL_EMAIL" ]; then
echo "错误: 提交者信息与全局配置不匹配!"
echo "当前: $AUTHOR_NAME <$AUTHOR_EMAIL>"
echo "全局: $GLOBAL_NAME <$GLOBAL_EMAIL>"
echo "请使用: git config user.name '你的名字' && git config user.email '你的邮箱'"
exit 1
fi
# 验证邮箱域名(可选)
if [[ ! "$AUTHOR_EMAIL" =~ @company\.com$ ]]; then
echo "错误: 必须使用公司邮箱提交 (xxx@company.com)"
exit 1
fi
GPG签名验证
pre-receive 钩子 (服务端) - 强制签名提交
#!/bin/bash
# 文件位置: 远程仓库的 .git/hooks/pre-receive
# 读取标准输入: <旧值> <新值> <分支名>
while read oldrev newrev refname; do
# 只检查非删除的提交
if [ "$newrev" != "0000000000000000000000000000000000000000" ]; then
# 遍历所有新提交
for commit in $(git rev-list "$oldrev..$newrev"); do
# 检查GPG签名
if ! git verify-commit "$commit" 2>/dev/null; then
echo "错误: 提交 $commit 没有有效的GPG签名"
exit 1
fi
# 验证签名者身份
SIGNER=$(git log --format="%aN <%aE>" -1 "$commit")
GPG_USER=$(git verify-commit "$commit" 2>&1 | grep "Good signature from" | sed 's/.*from //')
if [ -z "$GPG_USER" ]; then
echo "错误: 提交 $commit 来自未授权的开发者: $SIGNER"
exit 1
fi
done
fi
done
提交信息合规性检查
commit-msg 钩子 - 检查提交信息格式
#!/bin/bash
# 文件位置: .git/hooks/commit-msg
COMMIT_MSG_FILE=$1
COMMIT_MSG=$(cat "$COMMIT_MSG_FILE")
# 检查提交信息长度
if [ ${#COMMIT_MSG} -lt 10 ]; then
echo "错误: 提交信息至少10个字符"
exit 1
fi
# 检查是否包含JIRA编号(示例格式: [PROJECT-1234])
if [[ ! "$COMMIT_MSG" =~ \[[A-Z]+-[0-9]+\] ]]; then
echo "错误: 提交信息必须包含JIRA编号,格式为 [PROJECT-1234]"
exit 1
fi
# 检查第一行长度(不超过72字符)
FIRST_LINE=$(head -n 1 "$COMMIT_MSG_FILE")
if [ ${#FIRST_LINE} -gt 72 ]; then
echo "错误: 提交信息第一行不能超过72个字符"
echo "当前: ${#FIRST_LINE} 字符"
exit 1
fi
代码审查检查
pre-push 钩子 - 检查Pull Request状态
#!/bin/bash
# 文件位置: .git/hooks/pre-push
# GitHub API检查PR状态(需要配置环境变量)
GH_TOKEN=${GITHUB_TOKEN:-}
if [ -z "$GH_TOKEN" ]; then
echo "警告: 未设置GITHUB_TOKEN,跳过PR检查"
exit 1
fi
# 获取当前分支
BRANCH=$(git rev-parse --abbrev-ref HEAD)
# 调用GitHub API检查PR状态
PR_STATUS=$(curl -s -H "Authorization: token $GH_TOKEN" \
"https://api.github.com/repos/组织/仓库/pulls?head=$BRANCH&state=open" | \
jq -r '.[0].mergeable')
if [ "$PR_STATUS" != "true" ]; then
echo "错误: 当前分支的Pull Request未通过审查或存在冲突"
exit 1
fi
综合检查脚本
完整的pre-commit检查脚本
#!/bin/bash
# 文件位置: .git/hooks/pre-commit
echo "=== 开启问责性检查 ==="
# 1. 检查提交者身份
check_author() {
local email=$(git config user.email)
local name=$(git config user.name)
# 检查是否属于允许的邮箱域名
case "$email" in
*@company.com|*@org.com) ;;
*)
echo "错误: 邮箱 $email 不属于认可的组织"
exit 1
;;
esac
# 检查是否有对应LDAP/活跃目录账户
if ! ldapsearch -x "(&(objectClass=user)(mail=$email))" 2>/dev/null; then
echo "警告: 无法验证邮箱 $email 在LDAP中的存在性"
# 可以配置为严格模式强制检查
fi
}
# 2. 检查文件变更
check_changes() {
# 检查是否有未解决的冲突标记
if git diff --cached | grep -E "^\+.*<<<<<<<|^\+.*=======|^\+.*>>>>>>>"; then
echo "错误: 存在未解决的冲突标记"
exit 1
fi
# 检查是否修改了敏感文件(需要审批)
SENSITIVE_FILES="^(config|secret|keys)/"
for file in $(git diff --cached --name-only); do
if [[ "$file" =~ $SENSITIVE_FILES ]]; then
echo "错误: $file 是敏感文件,需要管理者审批才能修改"
exit 1
fi
done
}
# 3. 记录审计日志
log_audit() {
local commit_msg=$(cat "$1" 2>/dev/null || echo "N/A")
echo "[$(date)] Submitter: $(git config user.name) <$(git config user.email)>" >> .git/audit.log
echo "Branch: $(git rev-parse --abbrev-ref HEAD)" >> .git/audit.log
echo "Message: $commit_msg" >> .git/audit.log
}
# 执行检查
check_author
check_changes
log_audit "$1"
echo "=== 所有检查通过 ==="
exit 0
配置强制实施
在仓库中启用全局钩子
# 设置core.hooksPath指向共享钩子目录 git config core.hooksPath /path/to/shared/hooks # 或通过.gitattributes为特定目录启用 echo "*.sh filter=gate-check" >> .gitattributes
注意事项
- 性能考虑:钩子脚本应尽量轻量,避免过长的执行时间
- 绕过风险:用户可以使用
git commit --no-verify跳过钩子,服务端钩子更可靠 - 跨平台兼容:如果团队使用不同OS,考虑使用Python或Node.js编写钩子
- 错误友好:提供清晰的错误信息,指导用户如何修复问题
- 日志记录:记录检查结果便于追溯
通过合理配置这些钩子,可以有效实施代码提交的问责性门禁,确保每次变更都可追溯、可审计。