Git钩子是实现开发流程自动化的核心工具,本文通过预提交代码检查、提交信息规范验证、自动部署触发等实战场景,构建完整的企业级自动化流水线。包含ESLint集成、Docker自动构建、消息通知等完整钩子脚本,实现从代码提交到生产部署的无缝自动化。
![图片[1]-Git钩子深度实战:从代码检查到自动部署的完整流水线](https://blogimg.vcvcc.cc/2025/11/20251109030156233-1024x576.png?imageView2/0/format/webp/q/75)
一、Git钩子架构深度解析
1. 钩子生命周期与执行时机
<strong>#!/bin/bash</strong>
# 钩子执行顺序演示脚本
echo "=== Git钩子执行顺序分析 ==="
# 客户端钩子执行流程演示
cat > .git/hooks/client-flow << 'EOF'
#!/bin/bash
echo "客户端钩子执行顺序:"
echo "1. pre-applypatch → 应用补丁前"
echo "2. applypatch-msg → 补丁信息检查"
echo "3. pre-commit → 提交前检查"
echo "4. prepare-commit-msg → 准备提交信息"
echo "5. commit-msg → 提交信息验证"
echo "6. post-commit → 提交后操作"
echo "7. pre-rebase → 变基前检查"
echo "8. post-checkout → 检出后操作"
echo "9. post-merge → 合并后操作"
echo "10. pre-push → 推送前检查"
EOF
chmod +x .git/hooks/client-flow
2. 服务端钩子安全防护
<strong>#!/bin/bash</strong>
# 服务端钩子配置模板
cat > hooks/server-hooks-setup << 'EOF'
#!/bin/bash
echo "服务端钩子配置:"
echo "1. pre-receive → 接收前全局检查"
echo "2. update → 分支更新检查"
echo "3. post-receive → 接收后触发部署"
echo ""
echo "安全配置要点:"
echo "- 所有钩子必须可执行"
echo "- 设置严格的权限控制"
echo "- 记录完整的操作日志"
EOF
二、预提交检查深度实战
1. 代码质量自动化检查
<strong>#!/bin/bash</strong>
# .git/hooks/pre-commit - 综合代码检查钩子
echo "🚀 开始代码提交前检查..."
# 颜色定义
RED='3[0;31m'
GREEN='3[0;32m'
YELLOW='3[1;33m'
NC='3[0m'
# 检查是否在Git仓库中
if ! git rev-parse --git-dir > /dev/null <strong>2</strong>><strong>&1</strong>; then
echo -e "${RED}错误: 不在Git仓库中${NC}"
exit 1
fi
# 1. 检查是否有未暂存的更改
UNSTAGED_FILES=$(git diff --name-only)
if [ -n "$UNSTAGED_FILES" ]; then
echo -e "${YELLOW}警告: 发现未暂存的更改${NC}"
echo "$UNSTAGED_FILES"
read -p "是否继续提交? (y/N): " -n 1 -r
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
exit 1
fi
echo
fi
# 2. 代码语法检查(如果项目有ESLint配置)
if [ -f package.json ] && grep -q '"eslint"' package.json; then
echo "📋 运行ESLint检查..."
if npx eslint --fix --ext .js,.jsx,.ts,.tsx .; then
echo -e "${GREEN}✅ ESLint检查通过${NC}"
# 添加自动修复的文件
git add .
else
echo -e "${RED}❌ ESLint检查失败,请修复错误后重新提交${NC}"
exit 1
fi
fi
# 3. TypeScript类型检查
if [ -f tsconfig.json ]; then
echo "📘 运行TypeScript编译检查..."
if npx tsc --noEmit; then
echo -e "${GREEN}✅ TypeScript检查通过${NC}"
else
echo -e "${RED}❌ TypeScript编译错误${NC}"
exit 1
fi
fi
# 4. 检查调试代码
echo "🔍 检查调试代码..."
DEBUG_PATTERNS=(
"console.log"
"debugger"
"TODO:"
"FIXME:"
)
for pattern in "${DEBUG_PATTERNS[@]}"; do
FILES_WITH_PATTERN=$(git diff --cached --name-only -G "$pattern")
if [ -n "$FILES_WITH_PATTERN" ]; then
echo -e "${YELLOW}⚠️ 发现调试代码: $pattern${NC}"
echo "$FILES_WITH_PATTERN"
read -p "是否继续提交? (y/N): " -n 1 -r
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
exit 1
fi
echo
fi
done
# 5. 文件大小检查
echo "📏 检查文件大小..."
MAX_FILE_SIZE=5242880 # 5MB
git diff --cached --name-only | while read file; do
if [ -f "$file" ]; then
size=$(stat -f%z "$file" <strong>2</strong>>/dev/null || stat -c%s "$file" <strong>2</strong>>/dev/null)
if [ "$size" -gt "$MAX_FILE_SIZE" ]; then
echo -e "${RED}❌ 文件过大: $file ($((size/1024/1024))MB)${NC}"
echo "请使用Git LFS管理大文件"
exit 1
fi
fi
done
echo -e "${GREEN}🎉 所有检查通过,可以提交!${NC}"
exit 0
2. 提交信息规范验证
<strong>#!/bin/bash</strong>
# .git/hooks/commit-msg - 提交信息规范检查
COMMIT_MSG_FILE=$1
COMMIT_MSG=$(cat "$COMMIT_MSG_FILE")
# 提交信息格式规范
# 格式: <type>(<scope>): <subject>
# 示例: feat(auth): 添加用户登录功能
# 类型定义
VALID_TYPES=("feat" "fix" "docs" "style" "refactor" "test" "chore")
# 正则表达式模式
PATTERN="^([a-z]+)(\([a-z0-9-]+\))?: .{1,72}"
# 检查提交信息长度
if [ ${#COMMIT_MSG} -lt 10 ]; then
echo "❌ 提交信息太短,请详细描述更改内容"
exit 1
fi
# 检查格式
if ! echo "$COMMIT_MSG" | grep -qE "$PATTERN"; then
echo "❌ 提交信息格式错误"
echo "正确格式: <type>(<scope>): <subject>"
echo "示例: feat(auth): 添加用户登录功能"
echo ""
echo "可用类型: ${VALID_TYPES[*]}"
exit 1
fi
# 提取类型
TYPE=$(echo "$COMMIT_MSG" | sed -E 's/^([a-z]+).*/\1/')
# 验证类型
valid_type=false
for valid_type in "${VALID_TYPES[@]}"; do
if [ "$TYPE" = "$valid_type" ]; then
valid_type=true
break
fi
done
if [ "$valid_type" = false ]; then
echo "❌ 无效的提交类型: $TYPE"
echo "可用类型: ${VALID_TYPES[*]}"
exit 1
fi
echo "✅ 提交信息格式正确"
exit 0
三、服务端钩子与企业级防护
1. 分支保护与推送策略
<strong>#!/bin/bash</strong>
# .git/hooks/pre-receive - 服务端推送检查
echo "=== 服务端推送验证 ==="
# 颜色定义
RED='3[0;31m'
GREEN='3[0;32m'
NC='3[0m'
# 保护分支配置
PROTECTED_BRANCHES=("main" "master" "develop")
ADMIN_USERS=("admin" "ci-bot")
while read oldrev newrev refname; do
branch=$(git rev-parse --symbolic --abbrev-ref "$refname")
user=$(whoami)
echo "推送信息: 分支=$branch, 用户=$user"
# 检查是否推送到保护分支
for protected_branch in "${PROTECTED_BRANCHES[@]}"; do
if [ "$branch" = "$protected_branch" ]; then
echo -e "${YELLOW}⚠️ 检测到推送到保护分支: $branch${NC}"
# 检查用户权限
is_admin=false
for admin_user in "${ADMIN_USERS[@]}"; do
if [ "$user" = "$admin_user" ]; then
is_admin=true
break
fi
done
if [ "$is_admin" = false ]; then
echo -e "${RED}❌ 无权直接推送到保护分支 $branch${NC}"
echo "请创建Pull Request进行代码审查"
exit 1
fi
# 强制推送检查
if git merge-base --is-ancestor "$oldrev" "$newrev" <strong>2</strong>>/dev/null; then
echo "推送类型: 快进"
else
echo -e "${RED}❌ 拒绝强制推送到保护分支${NC}"
exit 1
fi
fi
done
# 提交数量限制
commit_count=$(git rev-list --count "$oldrev".."$newrev")
if [ "$commit_count" -gt 50 ]; then
echo -e "${RED}❌ 单次推送包含过多提交 ($commit_count),请分批推送${NC}"
exit 1
fi
# 大文件检查
echo "🔍 检查大文件..."
git log --format=format: --name-only "$oldrev"..""$newrev"" | \
sort | uniq | while read file; do
if [ -f "$file" ]; then
size=$(git cat-file -s "$(git rev-parse "$newrev":"$file")" <strong>2</strong>>/dev/null || echo 0)
if [ "$size" -gt 10485760 ]; then # 10MB
echo -e "${RED}❌ 发现大文件: $file ($((size/1024/1024))MB)${NC}"
echo "请使用Git LFS管理大文件"
exit 1
fi
fi
done
done
echo -e "${GREEN}✅ 服务端检查通过,接受推送${NC}"
exit 0
2. 自动代码审查集成
<strong>#!/bin/bash</strong>
# .git/hooks/update - 分支级别代码审查
refname="$1"
oldrev="$2"
newrev="$3"
echo "🔍 执行自动代码审查..."
# 只对特定分支进行深度检查
CODE_REVIEW_BRANCHES=("develop" "main" "master")
current_branch=$(echo "$refname" | sed 's/refs\/heads\///')
should_review=false
for branch in "${CODE_REVIEW_BRANCHES[@]}"; do
if [ "$current_branch" = "$branch" ]; then
should_review=true
break
fi
done
if [ "$should_review" = false ]; then
echo "跳过代码审查分支: $current_branch"
exit 0
fi
# 代码复杂度检查
echo "📊 分析代码复杂度..."
git diff --name-only "$oldrev" "$newrev" | grep -E '\.(js|ts|jsx|tsx|py|java)$' | while read file; do
if [ -f "$file" ]; then
# 简单的行数检查
lines=$(git show "$newrev":"$file" <strong>2</strong>>/dev/null | wc -l || echo 0)
if [ "$lines" -gt 1000 ]; then
echo "⚠️ 文件过大: $file ($lines 行)"
fi
# 检查函数长度(示例)
if echo "$file" | grep -qE '\.(js|ts|jsx|tsx)$'; then
long_functions=$(git show "$newrev":"$file" <strong>2</strong>>/dev/null | \
grep -c "function.*{" || echo 0)
if [ "$long_functions" -gt 20 ]; then
echo "⚠️ 函数过多: $file"
fi
fi
fi
done
# 安全漏洞模式检查
echo "🛡️ 安全模式检查..."
git diff "$oldrev" "$newrev" | grep -n -E "(eval\(|exec\(|password|secret|token)" | while read -r line; do
echo "⚠️ 发现潜在安全问题: $line"
done
echo "✅ 自动代码审查完成"
exit 0
四、自动化部署流水线
1. 推送后自动部署
<strong>#!/bin/bash</strong>
# .git/hooks/post-receive - 自动部署钩子
echo "🚀 开始自动部署流程..."
# 部署配置
DEPLOY_DIR="/var/www/app"
BACKUP_DIR="/var/backups/app"
LOG_FILE="/var/log/git-deploy.log"
# 日志函数
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$LOG_FILE"
echo "$1"
}
# 错误处理
handle_error() {
log "❌ 部署失败: $1"
exit 1
}
# 获取部署分支
while read oldrev newrev refname; do
branch=$(git rev-parse --symbolic --abbrev-ref "$refname")
# 只有特定分支触发部署
if [ "$branch" = "main" ] || [ "$branch" = "master" ]; then
log "检测到部署分支: $branch"
# 创建备份
log "创建备份..."
if [ -d "$DEPLOY_DIR" ]; then
backup_name="backup-$(date +%Y%m%d-%H%M%S)"
tar -czf "$BACKUP_DIR/$backup_name.tar.gz" -C "$DEPLOY_DIR" . || \
handle_error "备份失败"
fi
# 创建工作目录
WORK_TREE="/tmp/git-deploy-$$"
mkdir -p "$WORK_TREE" || handle_error "创建工作目录失败"
# 检出代码
log "检出代码..."
GIT_WORK_TREE="$WORK_TREE" git checkout -f "$branch" || \
handle_error "代码检出失败"
# 执行部署前脚本
if [ -f "$WORK_TREE/deploy/before-deploy.sh" ]; then
log "执行部署前脚本..."
cd "$WORK_TREE" && ./deploy/before-deploy.sh || \
handle_error "部署前脚本执行失败"
fi
# 同步文件到部署目录
log "同步文件..."
rsync -av --delete \
--exclude='.git' \
--exclude='node_modules' \
--exclude='.env' \
"$WORK_TREE/" "$DEPLOY_DIR/" || handle_error "文件同步失败"
# 执行部署后脚本
if [ -f "$DEPLOY_DIR/deploy/after-deploy.sh" ]; then
log "执行部署后脚本..."
cd "$DEPLOY_DIR" && ./deploy/after-deploy.sh || \
handle_error "部署后脚本执行失败"
fi
# 清理工作目录
rm -rf "$WORK_TREE"
# 重启服务
log "重启应用服务..."
systemctl restart app-service || \
echo "⚠️ 服务重启失败,请手动检查"
log "✅ 自动部署完成"
# 发送通知
if command -v curl >/dev/null <strong>2</strong>><strong>&1</strong>; then
curl -s -X POST -H "Content-Type: application/json" \
-d "{\"text\":\"🚀 应用已成功部署: $branch -> $(git log -1 --pretty=format:%h)\"}" \
"$WEBHOOK_URL" >/dev/null <strong>2</strong>><strong>&1</strong> &
fi
fi
done
exit 0
2. Docker自动化构建
<strong>#!/bin/bash</strong>
# deploy/after-deploy.sh - Docker自动化构建
echo "🐳 开始Docker构建..."
# 配置
DOCKER_IMAGE="myapp:latest"
DOCKER_REGISTRY="registry.example.com"
DEPLOY_ENV="production"
# 检查Docker是否可用
if ! command -v docker &> /dev/null; then
echo "❌ Docker不可用,跳过构建"
exit 0
fi
# 构建Docker镜像
echo "构建Docker镜像..."
docker build -t "$DOCKER_IMAGE" . || {
echo "❌ Docker构建失败"
exit 1
}
# 运行测试
echo "运行容器测试..."
docker run --rm "$DOCKER_IMAGE" npm test || {
echo "❌ 容器测试失败"
exit 1
}
# 推送到镜像仓库
if [ -n "$DOCKER_REGISTRY" ]; then
echo "推送镜像到仓库..."
docker tag "$DOCKER_IMAGE" "$DOCKER_REGISTRY/$DOCKER_IMAGE"
docker push "$DOCKER_REGISTRY/$DOCKER_IMAGE" || {
echo "❌ 镜像推送失败"
exit 1
}
fi
# 更新生产环境(如果使用Docker部署)
if [ "$DEPLOY_ENV" = "production" ]; then
echo "更新生产环境..."
docker-compose down
docker-compose pull
docker-compose up -d || {
echo "❌ 容器启动失败"
exit 1
}
fi
echo "✅ Docker构建和部署完成"
五、团队协作增强钩子
1. 提交信息模板生成
<strong>#!/bin/bash</strong>
# .git/hooks/prepare-commit-msg
COMMIT_MSG_FILE=$1
COMMIT_SOURCE=$2
SHA1=$3
# 只在空提交信息时应用模板
if [ -z "$COMMIT_SOURCE" ] || [ "$COMMIT_SOURCE" = "message" ]; then
exit 0
fi
# 提交信息模板
TEMPLATE=$(cat << 'EOF'
# <类型>(<范围>): <主题>
#
# <正文>
#
# <脚注>
#
# 类型: feat|fix|docs|style|refactor|test|chore
# 示例: feat(auth): 添加用户登录功能
#
# 主题行不超过50字符
# 正文每行不超过72字符
EOF
)
# 检查是否已经有提交信息
if [ ! -s "$COMMIT_MSG_FILE" ]; then
echo "$TEMPLATE" > "$COMMIT_MSG_FILE"
fi
2. 分支命名规范检查
<strong>#!/bin/bash</strong>
# .git/hooks/pre-push - 分支命名规范
echo "🔍 检查分支命名规范..."
CURRENT_BRANCH=$(git symbolic-ref --short HEAD)
# 分支命名模式
VALID_PATTERNS=(
"^(feature|fix|hotfix|release)/[a-z0-9-]+$"
"^(main|master|develop)$"
"^[a-z]+/[A-Z]+-[0-9]+" # 例如: feature/PROJ-123
)
valid_branch=false
for pattern in "${VALID_PATTERNS[@]}"; do
if echo "$CURRENT_BRANCH" | grep -qE "$pattern"; then
valid_branch=true
break
fi
done
if [ "$valid_branch" = false ]; then
echo "❌ 分支名称不符合规范: $CURRENT_BRANCH"
echo "允许的格式:"
echo " - feature/功能名称"
echo " - fix/修复描述"
echo " - hotfix/紧急修复"
echo " - 项目编号/描述 (例如: feature/PROJ-123)"
exit 1
fi
echo "✅ 分支名称符合规范"
exit 0
六、钩子管理与维护
1. 钩子安装与同步脚本
<strong>#!/bin/bash</strong>
# install-hooks.sh - 团队钩子安装脚本
echo "安装Git钩子..."
HOOKS_DIR=".git/hooks"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# 钩子文件列表
HOOK_FILES=(
"pre-commit"
"commit-msg"
"pre-push"
"post-receive"
)
for hook in "${HOOK_FILES[@]}"; do
source_file="$SCRIPT_DIR/git-hooks/$hook"
target_file="$HOOKS_DIR/$hook"
if [ -f "$source_file" ]; then
cp "$source_file" "$target_file"
chmod +x "$target_file"
echo "✅ 安装钩子: $hook"
else
echo "⚠️ 钩子文件不存在: $hook"
fi
done
# 安装客户端钩子配置
if [ ! -f "$HOOKS_DIR/client-hooks-config" ]; then
cat > "$HOOKS_DIR/client-hooks-config" << 'EOF'
# Git客户端钩子配置
# 此文件由 install-hooks.sh 自动生成
# 启用哪些钩子
ENABLE_PRE_COMMIT=true
ENABLE_COMMIT_MSG=true
ENABLE_PRE_PUSH=true
# 检查级别
CHECK_LEVEL=strict # strict|normal|minimal
EOF
echo "✅ 创建钩子配置文件"
fi
echo "🎉 Git钩子安装完成"
总结
Git钩子是实现开发流程自动化的强大工具,通过系统化的钩子配置可以:
代码质量保障:
- 自动化代码检查和测试
- 提交信息规范验证
- 安全漏洞模式检测
流程自动化:
- 自动部署流水线
- Docker镜像构建和推送
- 服务重启和状态通知
团队协作规范:
- 分支命名规范检查
- 提交信息模板生成
- 代码审查自动化
最佳实践建议:
- 钩子脚本应该轻量且快速执行
- 重要的检查应该在服务端重复进行
- 提供清晰的错误信息和修复指导
- 定期审查和更新钩子逻辑
通过合理的Git钩子配置,可以构建从代码提交到生产部署的完整自动化流水线,显著提升开发效率和代码质量。
© 版权声明
THE END










暂无评论内容