#!/usr/bin/env bash # ============================================================ # pre-commit-guard.sh # Claude Code PreToolUse hook — 拦截 git commit,执行全量检查 # # 触发时机:Claude 尝试执行 git commit 之前 # 作用:阻塞提交直到所有质量门禁通过 # 退出码:0 = 放行,2 = 阻塞(Claude 必须先修复问题) # # 配置方法:在 .claude/settings.local.json 中注册: # "hooks": { # "PreToolUse": [ # { "matcher": "Bash", "command": "bash scripts/hooks/pre-commit-guard.sh" } # ] # } # ============================================================ set -euo pipefail # <填写: 项目 Conda 环境路径> # PROJ_ENV="/home//miniconda3/envs//bin" # [[ -d "$PROJ_ENV" ]] && export PATH="$PROJ_ENV:$PATH" INPUT=$(cat) COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // ""') # 只拦截 git commit 命令 if ! echo "$COMMAND" | grep -qE '^\s*git\s+commit'; then exit 0 fi ERRORS="" WARNINGS="" # <填写: 核心代码目录,如 core/> CODE_DIR="core" # ── 1. 项目结构检查(按需取消注释) ── # 检查根目录是否有不该存在的 .py # for pyfile in *.py; do # [[ "$pyfile" == "*.py" ]] && break # if [[ "$pyfile" != "main.py" && "$pyfile" != "conftest.py" ]]; then # ERRORS+="[结构] 根目录不该有: $pyfile(应移至 $CODE_DIR/)\n" # fi # done # ── 2. 全量代码质量检查 ── if [[ -d "$CODE_DIR" ]] && command -v ruff &> /dev/null; then RUFF_OUTPUT=$(ruff check "$CODE_DIR" 2>&1 || true) if [[ -n "$RUFF_OUTPUT" ]]; then ERROR_COUNT=$(echo "$RUFF_OUTPUT" | wc -l) ERRORS+="[ruff] $CODE_DIR/ 中有 ${ERROR_COUNT} 个问题。运行 ruff check $CODE_DIR/ 查看详情。\n" fi fi if [[ -d "$CODE_DIR" ]] && command -v radon &> /dev/null; then RADON_OUTPUT=$(radon cc "$CODE_DIR" -n C -s 2>&1 || true) if echo "$RADON_OUTPUT" | grep -qE '^\s+[FMC]\s'; then ERRORS+="[radon] 存在圈复杂度 ≥ C 的函数:\n$RADON_OUTPUT\n" fi fi # ── 3. 文件行数检查 ── if [[ -d "$CODE_DIR" ]]; then while IFS= read -r pyfile; do lines=$(wc -l < "$pyfile") if [[ "$lines" -gt 200 ]]; then WARNINGS+="[行数] $pyfile 有 ${lines} 行,超过 200 行建议上限。\n" fi done < <(find "$CODE_DIR" -name "*.py" 2>/dev/null || true) fi # ── 4. 测试检查 ── if command -v pytest &> /dev/null && [[ -d "tests" ]]; then TEST_OUTPUT=$(pytest tests/ --tb=line -q 2>&1 || true) if echo "$TEST_OUTPUT" | grep -qE 'failed|error'; then FAILED=$(echo "$TEST_OUTPUT" | tail -1) ERRORS+="[测试] 有测试未通过:$FAILED\n" fi fi # ── 判定结果 ── if [[ -n "$WARNINGS" ]]; then echo -e "⚠️ Warnings(不阻塞):\n" >&2 echo -e "$WARNINGS" >&2 fi if [[ -n "$ERRORS" ]]; then echo -e "❌ 提交被阻塞 — 请先修复以下问题:\n" >&2 echo -e "$ERRORS" >&2 echo -e "修复完成后重新执行 git commit。" >&2 exit 2 fi echo "✅ 所有检查通过,允许提交。" exit 0