Git钩子脚本如何检查透明性门禁

wen 实用脚本 2

本文目录导读:

Git钩子脚本如何检查透明性门禁

  1. 场景一:阻止直接提交到 main / master 分支
  2. 场景二:检查 Commit Message 中包含 Issue/PR 编号
  3. 场景三:在 pre-push 中检查 CI 构建状态
  4. 场景四:检查文件是否经过安全扫描或格式校验
  5. 如何让团队强制使用这些钩子?

Git钩子脚本可以通过多种方式检查“透明性门禁”,这里“透明性门禁”通常指代码审查的透明性(如:必须经过Code Review、不得直接提交到主分支)或CI/CD流程的透明性(如:必须在通过所有测试、安全扫描后,才能合并或发布)。

下面是几个常见场景的Git钩子脚本实现方案,主要使用 pre-commitcommit-msgpre-push 钩子。


阻止直接提交到 main / master 分支

这是最基础的“门禁”,确保所有代码必须通过PR(Pull Request)并经过审查后才能合入主分支。

脚本位置: .git/hooks/pre-commit.git/hooks/pre-push Bash):**

#!/bin/bash
# 获取当前分支名
BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD)
# 定义受保护分支(可以扩展到多个)
PROTECTED_BRANCHES=("main" "master" "develop" "release/")
for PROTECTED in "${PROTECTED_BRANCHES[@]}"; do
    if [[ "$BRANCH_NAME" == "$PROTECTED" || "$BRANCH_NAME" == $PROTECTED* ]]; then
        echo "❌ 错误:不允许在 '$BRANCH_NAME' 分支上直接提交。"
        echo "请创建一个功能分支(feature/xxx),提交并推送,然后通过 Pull Request 合并。"
        exit 1
    fi
done
exit 0

设置:

chmod +x .git/hooks/pre-commit
# 或
chmod +x .git/hooks/pre-push

检查 Commit Message 中包含 Issue/PR 编号

要求每次提交都关联到一个任务或评审单,增加可追溯性(透明性)。

脚本位置: .git/hooks/commit-msg Bash):**

#!/bin/bash
# 读取提交信息文件
COMMIT_MSG_FILE=$1
COMMIT_MSG=$(cat "$COMMIT_MSG_FILE")
# 检查是否包含 JIRA/Redmine Issue 编号(如 PROJ-123)
if ! echo "$COMMIT_MSG" | grep -qiE "\[(PROJ|FEAT|BUG)-[0-9]+\]|#[0-9]+"; then
    echo "❌ 错误:提交信息必须包含 Issue/PR 编号([PROJ-123] 或 #42)。"
    echo "示例:git commit -m \"[PROJ-456] 修复登录超时问题\""
    exit 1
fi
exit 0

注意: 这只能检查格式,无法验证编号是否真实存在,如需更严格的“透明性”,可结合API(如JIRA、GitHub Issues)在 pre-push 中验证编号有效性。


pre-push 中检查 CI 构建状态

这是最强大的“透明性门禁”,在推送代码到远程仓库前,自动查询CI服务(如Jenkins、GitLab CI、GitHub Actions)的最近一次构建状态。

脚本位置: .git/hooks/pre-push 以GitHub API为例):**

#!/bin/bash
# 需要设置环境变量或从git配置中读取
# GITHUB_TOKEN 需要提前设置
# GIT_REPO 可以从git remote获取
REMOTE_URL=$(git remote get-url origin)
if [[ "$REMOTE_URL" == *"github.com"* ]]; then
    # 解析 owner/repo
    REPO=$(echo "$REMOTE_URL" | sed -n 's/.*github.com[:\/]\(.*\)\.git/\1/p')
    BRANCH=$(git rev-parse --abbrev-ref HEAD)
    # 查询最新commit的check suite状态(简化示例,可能需要更多处理)
    LAST_COMMIT=$(git rev-parse HEAD)
    STATUS=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \
        "https://api.github.com/repos/$REPO/commits/$LAST_COMMIT/check-suites" \
        | jq -r '.check_suites[0].conclusion // "none"')
    if [[ "$STATUS" != "success" ]]; then
        echo "❌ 错误:当前 commit 的 CI 构建状态为 '$STATUS',未通过。"
        echo "必须确保代码在所有门禁(测试、lint、安全扫描)通过后才能推送。"
        exit 1
    fi
else
    echo "⚠️ 非GitHub仓库,跳过CI状态检查(或实现其他CI的检查)。"
fi
exit 0

注意:

  • 需要安装 curljq
  • 需要配置 API Token(建议通过环境变量,不要硬编码)。
  • 对性能有影响(网络请求),但能有效确保“透明性”。

检查文件是否经过安全扫描或格式校验

确保所有的 .env 文件不被提交,或所有 .java 文件都通过了格式化。

脚本位置: .git/hooks/pre-commit 检查正在暂存的文件):**

#!/bin/bash
# 获取暂存区中的文件列表(只检查新增/修改的文件)
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM)
# 检查是否包含敏感文件
for FILE in $STAGED_FILES; do
    if [[ "$FILE" == *.env || "$FILE" == *.p12 || "$FILE" == *credential* ]]; then
        echo "❌ 错误:检测到敏感文件 '$FILE',请移除后重新提交。"
        exit 1
    fi
done
# 检查Java文件是否符合格式化(如果没有格式化工具,可跳过)
if command -v google-java-format &> /dev/null; then
    for FILE in $STAGED_FILES; do
        if [[ "$FILE" == *.java ]]; then
            # 检查格式(这里只是示例,实际可能需要更复杂的逻辑)
            google-java-format --dry-run "$FILE"
            if [ $? -ne 0 ]; then
                echo "❌ 错误:文件 '$FILE' 未通过 google-java-format 检查,请执行格式化后再提交。"
                exit 1
            fi
        fi
    done
fi
exit 0

如何让团队强制使用这些钩子?

手动复制 .git/hooks/ 很麻烦且容易绕过,推荐以下方法:

  1. 使用工具(推荐):

    • Husky(Node.js项目):通过 package.json 管理钩子。
    • pre-commit(Python项目):通过 .pre-commit-config.yaml 配置。
    • lefthook:跨语言、快、支持 pre-commitcommit-msgpre-push

    示例(使用 lefthook 的配置):

    # lefthook.yml
    pre-commit:
      commands:
        check-branch:
          run: bash scripts/check-branch.sh
        lint:
          run: npx eslint --fix {staged_files}
    pre-push:
      commands:
        check-ci:
          run: bash scripts/check-ci.sh
  2. 在CI中强制执行: Git钩子是本地的,用户可以 --no-verify 跳过,真正的“门禁”必须放在远端(CI/CD流程中)强制执行。

    • GitHub: 在仓库设置中启用分支保护规则。
    • GitLab: 设置“成员合并请求批准”规则。
    • Jenkins/Bamboo: 在构建Job中检查提交信息、分支名称。
钩子类型 作用
pre-commit 、格式、敏感信息 本地快速拦截
commit-msg 提交信息格式(关联Issue) 保证可追溯性
pre-push 分支名称、CI状态、API验证 推送前的最后一道“硬”门禁

最佳实践: 本地钩子做前置提醒和快速检查(格式、分支名、提交信息),真正的“透明性门禁”应在CI/CD的Pull Request/Merge Check中强制执行,两者结合,才能实现不可绕过的透明性保障。

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