Ctrl K
ComunidadGuías…

scripts/ai_guard/pre_commit_hook.sh

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
#!/usr/bin/env bash
# git pre-commit hook: IDE 无关的后备保护。
# 在每次 git commit 前运行关键门禁检查。
# 安装: bash scripts/ai_guard/install-hooks.sh

set -euo pipefail

ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
cd "$ROOT"
export PYTHONPATH=.

# Shared protected-files list (also used by commit-msg hook).
. "$ROOT/scripts/ai_guard/protected_files.sh"

echo "== AI Guard: pre-commit 检查 =="

# 1. 检查是否修改了受保护文件 (warn-only here; commit-msg hook enforces)
PROTECTED_HITS=()
for pf in "${PROTECTED_FILES[@]}"; do
    if git diff --cached --name-only | grep -q "^${pf}$"; then
        PROTECTED_HITS+=("$pf")
    fi
done

if [ "${#PROTECTED_HITS[@]}" -gt 0 ]; then
    echo "⚠️  受保护文件被修改(commit-msg hook 将检查 [MANUAL-OVERRIDE]):"
    for pf in "${PROTECTED_HITS[@]}"; do
        echo "   - $pf"
    done
    echo ""
fi

# 2. 检查层依赖(快速版)
if git diff --cached --name-only | grep -qE "^butler/.*\.py$"; then
    echo "→ 运行层依赖检查..."
    if ! python -m pytest tests/test_eng15_layer_dependency_matrix.py -q --tb=line --no-header 2>/dev/null; then
        echo "❌ 层依赖检查失败!"
        echo "   运行完整检查: bash scripts/butler-layer-import-gate.sh"
        exit 1
    fi
    echo "✅ 层依赖检查通过"

    # G1: lazy import 预算检查(仅改 butler/*.py 时)
    echo "→ 运行 lazy import 预算检查..."
    if ! bash scripts/p3i-lazy-import-report.sh 2>/dev/null; then
        echo "❌ Lazy import 预算超限(>1910)!"
        echo "   AI 可能添加了模块级 import 导致循环依赖。"
        echo "   请将新增的 from butler.* 改为函数内 lazy import。"
        echo "   完整报告: bash scripts/p3i-lazy-import-report.sh"
        exit 1
    fi
    echo "✅ Lazy import 预算检查通过"

    # G6: 文件大小守卫
    echo "→ 运行文件大小守卫..."
    if ! python3 scripts/ai_guard/file_size_check.py --staged; then
        echo "❌ 文件大小守卫失败!"
        echo "   项目约定 .py 文件 >800 行应拆分。"
        echo "   如确需大文件,请在 commit message 中包含 [MANUAL-OVERRIDE] 标记。"
        # 文件大小是警告,不阻止(exit 0)— 由 file_size_check.py 控制
    fi
fi

# G3: Secret scanning(所有文件改动时都检查;no override — secrets must not be committed)
echo "→ 运行 secret 扫描..."
# 注意:while 循环的退出码 = 最后一条 body 命令的退出码。set -euo pipefail 下,
# git check-ignore 对"未被忽略的文件"返回 1,会使该 pipeline 返回非零。
# 用 if/continue 归一化 + 末尾 || true 兜底,杜绝其把非零退出码带出命令替换。
SECRET_HITS=$(git diff --cached --name-only | while read f; do
    [ -f "$f" ] || continue
    # 跳过 .gitignore 中的文件
    if git check-ignore "$f" >/dev/null 2>&1; then
        continue
    fi
    # 扫描常见密钥模式
    if grep -nE '(sk-[a-zA-Z0-9]{32,}|AKIA[A-Z0-9]{16}|ghp_[a-zA-Z0-9]{36}|eyJ[a-zA-Z0-9_-]+\.eyJ[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+|-----BEGIN (RSA |EC )?PRIVATE KEY-----)' "$f" 2>/dev/null; then
        echo "$f"
    fi
done || true)

if [ -n "$SECRET_HITS" ]; then
    echo "❌ 检测到可能的密钥/凭证:"
    echo "$SECRET_HITS"
    echo ""
    echo "提交被阻止。如确需提交(如测试 fixture),请使用 --no-verify 跳过所有 hooks。"
    exit 1
fi
echo "✅ Secret 扫描通过"

# 3. Butler v5 迁移 schema 警告
if git diff --cached --name-only | grep -qE "^butler-v5/packages/persistence/src/migrations/"; then
    echo "⚠️  WARNING: v5 persistence migration 被修改"
    echo "   请运行: cd butler-v5 && pnpm exec vitest run packages/persistence/src/runtime-schema.test.ts"
fi

# 4. 检查契约文件是否被修改(警告)
if git diff --cached --name-only | grep -qE "^butler/contracts/"; then
    echo "⚠️  WARNING: 契约层文件被修改"
    echo "   请确保已运行契约测试: python -m pytest tests/contracts/ -v"
    echo "   并确认所有 Port 实现已同步更新。"
fi

# 5. 检查 shim 文件是否被修改(警告)
SHIM_FILES=$(git diff --cached --name-only | grep -E "\.py$" || true | while read f; do
    if [ -f "$f" ] && head -5 "$f" 2>/dev/null | grep -q "Deprecated:"; then
        echo "$f"
    fi
done)

if [ -n "$SHIM_FILES" ]; then
    echo "⚠️  WARNING: 以下 shim 文件被修改:"
    echo "$SHIM_FILES" | while read f; do echo "   - $f"; done
    echo "   shim 文件是向后兼容层,修改不会影响实际实现。"
    echo "   如需修改功能,请修改对应的包目录下的实际实现文件。"
fi

echo "== AI Guard: pre-commit 检查通过 =="
exit 0