Git钩子脚本如何检查问责性门禁

wen 实用脚本 2

本文目录导读:

Git钩子脚本如何检查问责性门禁

  1. 提交者身份验证
  2. GPG签名验证
  3. 提交信息合规性检查
  4. 代码审查检查
  5. 综合检查脚本
  6. 配置强制实施
  7. 注意事项

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

注意事项

  1. 性能考虑:钩子脚本应尽量轻量,避免过长的执行时间
  2. 绕过风险:用户可以使用git commit --no-verify跳过钩子,服务端钩子更可靠
  3. 跨平台兼容:如果团队使用不同OS,考虑使用Python或Node.js编写钩子
  4. 错误友好:提供清晰的错误信息,指导用户如何修复问题
  5. 日志记录:记录检查结果便于追溯

通过合理配置这些钩子,可以有效实施代码提交的问责性门禁,确保每次变更都可追溯、可审计。

抱歉,评论功能暂时关闭!