本文目录导读:

Git 钩子脚本是实现发布门禁检查的常用方式,通常部署在服务端(如远程仓库)的 pre-receive 或 update 钩子中,下面详细说明如何编写和配置这些脚本。
核心原理
服务端钩子的工作流程:
pre-receive:在引用更新前执行,接收所有待更新的引用列表update:每个引用单独执行一次,参数更清晰
钩子脚本通过退出状态码控制门禁:
- 返回 0:允许推送
- 返回 非0:拒绝推送
常用门禁检查项
1 分支命名规范
#!/bin/bash
# pre-receive 钩子示例
# 定义允许的分支模式
BRANCH_PATTERN="^(feature|bugfix|hotfix|release|main|develop)/.*$|^main$|^develop$"
while read oldrev newrev refname; do
# 提取分支名
branch=${refname#refs/heads/}
# 跳过标签
[[ "$refname" == *refs/tags/* ]] && continue
if [[ ! "$branch" =~ $BRANCH_PATTERN ]]; then
echo "错误: 分支名 '$branch' 不符合规范"
echo "允许的分支格式: feature/*, bugfix/*, hotfix/*, release/*"
exit 1
fi
done
exit 0
2 Commit 信息格式检查
# 检查 commit message 格式
check_commit_message() {
local commit_msg=$(git log --format=%B -1 "$1")
# 示例规则:必须以 JIRA 编号开头
if [[ ! "$commit_msg" =~ ^[A-Z]+-[0-9]+ ]]; then
echo "错误: Commit 必须以 JIRA 编号开头 (如: PROJ-1234 ...)"
return 1
fi
# 检查长度
local first_line=$(echo "$commit_msg" | head -1)
if [[ ${#first_line} -gt 72 ]]; then
echo "错误: Commit 首行不能超过72个字符"
return 1
fi
return 0
}
3 代码质量检查
check_code_quality() {
local newrev="$1"
# 获取新增/修改的文件
local files=$(git diff --name-only --diff-filter=AM "$2" "$newrev" | grep '\.py$\|\.js$\|\.java$')
# 运行 linter
for file in $files; do
case "$file" in
*.py)
git show "$newrev:$file" | python -m pyflakes -
if [[ $? -ne 0 ]]; then
echo "错误: Python 文件 $file 存在语法或风格问题"
exit 1
fi
;;
*.js)
git show "$newrev:$file" | npx eslint --stdin --
if [[ $? -ne 0 ]]; then
echo "错误: JS 文件 $file 存在语法或风格问题"
exit 1
fi
;;
esac
done
}
4 安全检查(禁止敏感信息)
check_security() {
local newrev="$1"
# 扫描新增的文件
local files=$(git diff --name-only --diff-filter=AM "$2" "$newrev")
# 敏感信息模式
local patterns=(
'password\s*=\s*['"'"'"]'
'api_key\s*=\s*['"'"'"]'
'AKIA[0-9A-Z]{16}' # AWS key 模式
'-----BEGIN RSA PRIVATE KEY-----'
)
for file in $files; do
local content=$(git show "$newrev:$file" 2>/dev/null)
for pattern in "${patterns[@]}"; do
if echo "$content" | grep -qiE "$pattern"; then
echo "错误: 文件 $file 可能包含敏感信息"
exit 1
fi
done
done
}
完整的 pre-receive 脚本
#!/bin/bash
# .git/hooks/pre-receive
set -e
RED='\033[0;31m'
NC='\033[0m'
echo "开始执行发布门禁检查..."
while read oldrev newrev refname; do
echo "检查: $refname ($oldrev -> $newrev)"
# 跳过删除分支
if [[ "$newrev" == "0000000000000000000000000000000000000000" ]]; then
continue
fi
# 1. 分支命名检查
branch=${refname#refs/heads/}
if [[ "$refname" != *refs/tags/* ]]; then
if [[ ! "$branch" =~ ^(main|develop|release/.+)$ ]]; then
echo -e "${RED}错误: 不允许直接推送到分支 '$branch'${NC}"
exit 1
fi
fi
# 2. 获取所有新 commit
if [[ "$oldrev" == "0000000000000000000000000000000000000000" ]]; then
# 新分支
commits=$(git rev-list "$newrev" --not --all)
else
commits=$(git rev-list "$oldrev..$newrev" 2>/dev/null)
fi
# 3. 对每个 commit 进行检查
for commit in $commits; do
# 检查 commit message
commit_msg=$(git log --format=%B -1 "$commit")
if [[ ! "$commit_msg" =~ ^(feat|fix|docs|style|refactor|test|chore)\(.*\): ]]; then
echo -e "${RED}错误: Commit $commit 格式不符合 Conventional Commits${NC}"
exit 1
fi
# 检查是否包含大文件
files_modified=$(git diff-tree --no-commit-id -r --name-only "$commit")
# 限制单个文件大小 (5MB)
for file in $files_modified; do
file_size=$(git show "$commit:$file" | wc -c)
if [[ $file_size -gt 5242880 ]]; then
echo -e "${RED}错误: 文件 $file 超过 5MB 限制${NC}"
exit 1
fi
done
done
# 4. 合并检查(针对合并到主分支)
if [[ "$branch" == "main" || "$branch" == "master" ]]; then
# 检查是否有未验证的 merge 提交
merge_commits=$(git rev-list --merges "$oldrev..$newrev")
if [[ -z "$merge_commits" ]]; then
echo -e "${RED}错误: 推送到主分支必须通过 MR/PR 合并${NC}"
exit 1
fi
fi
done
echo "所有门禁检查通过!"
exit 0
部署与配置
1 部署钩子
# 在服务端仓库设置 cd /path/to/your/repo.git # 创建 hooks 目录 mkdir -p hooks # 复制脚本(使用规范名称) cp /path/to/your/pre-receive-script hooks/pre-receive # 添加执行权限 chmod +x hooks/pre-receive chown -R git:git hooks
2 GitLab/GitHub 配置(替代方案)
如果使用 GitLab,可以用 CI 替代钩子:
# .gitlab-ci.yml
stages:
- gate
release_gate:
stage: gate
script:
- ./scripts/check_release_gates.sh
only:
- main
- tags
最佳实践
1 性能优化
# 批量检查而不是逐个
check_commits_batch() {
local oldrev="$1"
local newrev="$2"
# 一次性获取所有 commit 信息
git log --format="%H %s" "$oldrev..$newrev" |
while read hash subject; do
# 检查逻辑
done
}
2 错误处理
# 友好的错误信息
print_error() {
local message="$1"
local commit="$2"
echo "========================================"
echo "🚫 门禁检查失败"
echo "提交: ${commit:0:8}"
echo "错误: $message"
echo "========================================"
}
3 测试你的钩子
# 本地测试 cd /tmp/test-repo git push origin feature/test-branch # 观察钩子输出 # 绕过测试(不推荐生产环境使用) git push -o ci.skip origin feature/test-branch
注意事项
- 性能:钩子脚本会在推送时执行,避免进行耗时操作(如编译)
- 环境:确保服务端有脚本所需的工具(linters, formatters 等)
- 兼容性:考虑脚本的兼容性和幂等性
- 备份:修改前备份原钩子脚本
- 监控:记录钩子执行日志以便排查问题
通过合理配置 Git 钩子,可以在代码准入环节自动执行质量检查,确保只有符合规范的代码才能进入受保护分支。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播