Git Hooks 设置
概览
配置 Git 钩子以强制执行代码质量标准,运行自动化检查,并防止有问题的提交被推送到共享仓库。
何时使用
- 提交前代码质量检查
- 提交信息验证
- 防止提交中包含秘密
- 提交前运行测试
- 代码格式化执行
- 代码风格检查配置
- 团队范围内的标准执行
实施示例
1. Husky 安装和配置
#!/bin/bash
# setup-husky.sh
# 安装 Husky
npm install husky --save-dev
# 初始化 Husky
npx husky install
# 创建提交前钩子
npx husky add .husky/pre-commit "npm run lint"
# 创建提交信息钩子
npx husky add .husky/commit-msg 'npx --no -- commitlint --edit "$1"'
# 创建推送前钩子
npx husky add .husky/pre-push "npm run test"
# 创建合并后钩子
npx husky add .husky/post-merge "npm install"
2. 提交前钩子(Node.js)
#!/usr/bin/env node
# .husky/pre-commit
const { execSync } = require('child_process');
const fs = require('fs');
console.log('🔍 运行提交前检查...
');
try {
// 获取暂存文件
const stagedFiles = execSync('git diff --cached --name-only', { encoding: 'utf-8' })
.split('
')
.filter(file => file && (file.endsWith('.js') || file.endsWith('.ts')))
.join(' ');
if (!stagedFiles) {
console.log('✅ 没有 JavaScript/TypeScript 文件需要检查');
process.exit(0);
}
// 在暂存文件上运行 linter
console.log('📝 运行 ESLint...');
execSync(`npx eslint ${stagedFiles} --fix`, { stdio: 'inherit' });
// 运行 Prettier
console.log('✨ 运行 Prettier...');
execSync(`npx prettier --write ${stagedFiles}`, { stdio: 'inherit' });
// 暂存修正后的文件
console.log('📦 暂存修正后的文件...');
execSync(`git add ${stagedFiles}`);
console.log('
✅ 提交前检查通过!');
} catch (error) {
console.error('❌ 提交前检查失败!');
process.exit(1);
}
3. 提交信息验证
#!/bin/bash
# .husky/commit-msg
# 验证提交信息格式
COMMIT_MSG=$(<"$1")
# 模式:type(scope): description
PATTERN="^(feat|fix|docs|style|refactor|test|chore|perf)(\([a-z\-]+\))?: .{1,50}"
if ! [[ $COMMIT_MSG =~ $PATTERN ]]; then
echo "❌ 提交信息格式无效"
echo "格式:type(scope): description"
echo "类型:feat, fix, docs, style, refactor, test, chore, perf"
echo ""
echo "示例:"
echo " feat: 添加新功能"
echo " fix(auth): 解决登录错误"
echo " docs: 更新 README"
exit 1
fi
# 检查信息长度
FIRST_LINE=$(echo "$COMMIT_MSG" | head -n1)
if [ ${#FIRST_LINE} -gt 72 ]; then
echo "❌ 提交信息太长(最多 72 个字符)"
exit 1
fi
echo "✅ 提交信息有效"
4. Commitlint 配置
// commitlint.config.js
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [2, 'always', ['feat', 'fix', 'docs', 'style', 'refactor', 'test', 'chore']],
'subject-case': [2, 'never', ['start-case', 'pascal-case', 'upper-case']],
'type-empty': [2, 'never']
}
};
5. 推送前钩子(综合)
#!/usr/bin/env bash
# .husky/pre-push
BRANCH=$(git rev-parse --abbrev-ref HEAD)
# 防止直接推送到主分支
if [[ "$BRANCH" =~ ^(main|master)$ ]]; then
echo "❌ 直接推送到 $BRANCH 不被允许"
exit 1
fi
npm test && npm run lint && npm run build
6. 提交前框架(Python)
# .pre-commit-config.yaml
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files
args: ['--maxkb=1000']
- id: detect-private-key
- id: check-merge-conflict
- repo: https://github.com/psf/black
rev: 23.3.0
hooks:
- id: black
language_version: python3.11
- repo: https://github.com/PyCQA/flake8
rev: 6.0.0
hooks:
- id: flake8
args: ['--max-line-length=88', '--extend-ignore=E203,W503']
- repo: https://github.com/PyCQA/isort
rev: 5.12.0
hooks:
- id: isort
args: ['--profile', 'black']
- repo: https://github.com/Yelp/detect-secrets
rev: v1.4.0
hooks:
- id: detect-secrets
args: ['--baseline', '.secrets.baseline']
- repo: https://github.com/commitizen-tools/commitizen
rev: 3.5.2
hooks:
- id: commitizen
stages: [commit-msg]
7. 秘密检测钩子
#!/bin/bash
# .husky/pre-commit-secrets
git diff --cached | grep -E 'password|api_key|secret|token' && exit 1
echo "✅ 未检测到秘密"
8. package.json 中的 Husky
{
"scripts": { "prepare": "husky install" },
"devDependencies": {
"husky": "^8.0.0",
"@commitlint/cli": "^17.0.0"
},
"lint-staged": {
"*.{js,ts}": "eslint --fix"
}
}
最佳实践
✅ 执行
- 强制执行提交前代码风格检查和格式化
- 验证提交信息格式
- 提交前扫描秘密
- 提交前运行测试
- 仅在极少数情况下使用
--no-verify跳过钩子 - 在 README 中记录钩子需求
- 使用一致的钩子配置
- 使钩子快速(< 5 秒)
- 提供有用的错误信息
- 允许开发者绕过,并提供清晰的警告
❌ 不要
- 使用
--no-verify跳过检查 - 在提交的文件中存储秘密
- 使用不一致的实现
- 忽略钩子错误
- 在提交前运行完整的测试套件