本文目录导读:

Git 钩子脚本可以通过以下方式检查代码的可复现性门禁,确保提交的代码能可靠地重建出相同的构建结果:
预提交钩子 (pre-commit) 检查方案
#!/bin/bash
# .git/hooks/pre-commit
set -e
echo "🔍 检查可复现性门禁..."
# 1. 检查构建文件锁定
check_build_locks() {
local lock_files=("package-lock.json" "yarn.lock" "pnpm-lock.yaml" "Gemfile.lock" "Cargo.lock")
for file in "${lock_files[@]}"; do
if [ -f "$file" ]; then
# 确保锁文件已暂存
if git diff --cached --name-only | grep -q "$file"; then
echo "✅ 锁文件 $file 已包含在提交中"
else
echo "❌ 锁文件 $file 变更未暂存!"
exit 1
fi
fi
done
}
# 2. 检查 Dockerfile 固定版本
check_dockerfile() {
if [ -f "Dockerfile" ]; then
# 检查是否使用固定标签而非 latest
if grep -qE "FROM.*:latest" Dockerfile; then
echo "⚠️ Dockerfile 使用了 :latest 标签,建议使用固定版本"
fi
# 检查是否指定了具体版本号
if ! grep -qE "FROM.*:[0-9]+\.[0-9]+" Dockerfile; then
echo "❌ Dockerfile 未指定固定版本号!"
exit 1
fi
fi
}
# 3. 检查依赖版本固定
check_dependency_versions() {
# 检查 package.json 是否使用范围版本
if [ -f "package.json" ]; then
if grep -qE '"[^"]+":\s*"\^|\~' package.json; then
echo "⚠️ package.json 中存在范围版本(^/~),建议锁定精确版本"
fi
fi
# 检查 requirements.txt 是否固定版本
if [ -f "requirements.txt" ]; then
if grep -qE ">|<|>=" requirements.txt; then
echo "❌ requirements.txt 中存在不固定版本依赖!"
exit 1
fi
fi
}
# 4. 检查构建脚本的确定性
check_build_scripts() {
if [ -f "Makefile" ]; then
# 检查是否使用了随机或时间相关的操作
if grep -qE "date|\$(date)|mktemp|random" Makefile; then
echo "⚠️ Makefile 包含可能影响可复现性的命令"
fi
fi
}
# 5. 验证构建缓存
check_build_cache() {
if [ -d "node_modules" ] || [ -d ".next" ] || [ -d "dist" ]; then
echo "⚠️ 构建缓存目录存在,建议清理后提交"
# 检查 .gitignore 中是否排除
if [ -f ".gitignore" ]; then
grep -q "node_modules" .gitignore || echo "⚠️ node_modules 未在 .gitignore 中"
fi
fi
}
# 执行检查
check_build_locks
check_dockerfile
check_dependency_versions
check_build_scripts
check_build_cache
echo "✅ 可复现性门禁检查通过"
提交信息钩子 (commit-msg) 检查
#!/bin/bash
# .git/hooks/commit-msg
commit_msg_file=$1
commit_msg=$(cat "$commit_msg_file")
# 检查提交信息是否包含可复现性标记
if [[ ! "$commit_msg" =~ (reproducible|reproducibility|可复现) ]]; then
echo "⚠️ 建议在提交信息中添加可复现性说明"
read -p "是否继续提交? (y/n) " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
exit 1
fi
fi
推送钩子 (pre-push) 更严格的检查
#!/bin/bash
# .git/hooks/pre-push
# 1. 执行完整的构建验证
echo "🏗️ 执行可复现性构建验证..."
# 清理构建环境
clean_build() {
rm -rf node_modules dist .next
npm ci || yarn install --frozen-lockfile
}
# 计算构建哈希
compute_build_hash() {
if [ -f "package.json" ]; then
npm run build 2>/dev/null || yarn build 2>/dev/null
find dist -type f -exec md5sum {} \; | sort | md5sum
fi
}
# 两次构建验证
first_hash=$(clean_build && compute_build_hash)
second_hash=$(clean_build && compute_build_hash)
if [ "$first_hash" != "$second_hash" ]; then
echo "❌ 构建结果不一致!请检查构建过程的可复现性"
exit 1
fi
echo "✅ 构建可复现性验证通过"
配置管理钩子
创建 .reproducibility-check.yml 配置文件:
# 可复现性检查配置 checks: build_locks: true docker_versions: true dependency_versions: true build_scripts: true # 排除检查的文件 exclude: - "*.md" - "docs/*" # 强制检查的路径 include: - "src/**/*" - "Dockerfile" - "package.json" # 允许的构建工具 allowed_build_tools: - npm - yarn - make - docker # 禁止的命令 forbidden_commands: - curl - wget - date - timestamp
安装脚本
#!/bin/bash
# setup-hooks.sh
HOOKS_DIR=".git/hooks"
install_hook() {
local hook_name=$1
local hook_content=$2
cat > "$HOOKS_DIR/$hook_name" << 'EOF'
#!/bin/bash
# 可复现性检查钩子
EOF
echo "$hook_content" >> "$HOOKS_DIR/$hook_name"
chmod +x "$HOOKS_DIR/$hook_name"
echo "✅ 安装 $hook_name 钩子完成"
}
# 安装预提交钩子
install_hook "pre-commit" "$(cat << 'SCRIPT'
#!/bin/bash
source "$(dirname "$0")/reproducibility-check.sh"
check_reproducibility_gate
SCRIPT
)"
# 创建共享检查脚本
cat > ".git/hooks/reproducibility-check.sh" << 'SCRIPT'
#!/bin/bash
check_reproducibility_gate() {
# 从配置文件读取设置
CONFIG_FILE=".reproducibility-check.yml"
if [ -f "$CONFIG_FILE" ]; then
echo "📋 加载可复现性检查配置..."
# 解析 YAML 配置并执行对应检查
fi
# 执行标准检查
# ... 检查逻辑
}
# 导出函数
export -f check_reproducibility_gate
SCRIPT
chmod +x ".git/hooks/reproducibility-check.sh"
echo "✅ 所有可复现性门禁钩子安装完成"
使用建议
- 渐进式实施:先设置为警告,再逐步升级为强制检查
- 环境感知:根据开发/生产环境设置不同的严格程度
- 结果记录:将检查结果记录到日志中便于审计
- CI/CD集成:在持续集成流程中并行执行相同检查
- 白名单机制:允许特殊情况跳过检查(需审批)
这些钩子脚本可以有效确保代码的可复现性,防止因环境差异导致的构建不一致问题。