0df4d365a5
ruff check prints 'All checks passed!' on success, which the non-empty output test counted as one problem and blocked every commit. Use --quiet --output-format concise so success is silent and each diagnostic is exactly one line.
85 lines
2.8 KiB
Bash
Executable File
85 lines
2.8 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# ============================================================
|
||
# post-edit-quality.sh
|
||
# Claude Code PostToolUse hook — 每次编辑 .py 文件后自动检查
|
||
#
|
||
# 触发时机: Claude 每次执行 Write/Edit 后
|
||
# 作用: 对被修改的文件执行复杂度和风格检查(确定性质量守卫)
|
||
# 退出码: 0 = 通过, 非0 = 报告问题(Claude 会看到 stderr 反馈)
|
||
#
|
||
# 注册: 见 .claude/settings.json → hooks.PostToolUse
|
||
# ============================================================
|
||
set -euo pipefail
|
||
|
||
# PolyGateway conda 环境(存在则优先其工具链)
|
||
PROJ_ENV="$HOME/miniconda3/envs/PolyGateway/bin"
|
||
[[ -d "$PROJ_ENV" ]] && export PATH="$PROJ_ENV:$PATH"
|
||
|
||
INPUT=$(cat)
|
||
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.path // ""')
|
||
|
||
# 只检查 .py 文件
|
||
if [[ ! "$FILE_PATH" == *.py ]]; then
|
||
exit 0
|
||
fi
|
||
|
||
# reference/ 只读区、外部路径不检查(写入本身由 pre-tool-guard 拦截)
|
||
case "$FILE_PATH" in
|
||
*/reference/*|reference/*) exit 0 ;;
|
||
esac
|
||
|
||
# 只检查存在的文件
|
||
if [[ ! -f "$FILE_PATH" ]]; then
|
||
exit 0
|
||
fi
|
||
|
||
ERRORS=""
|
||
WARNINGS=""
|
||
|
||
# ── 1. Ruff 格式 + lint 检查 ──
|
||
if command -v ruff &> /dev/null; then
|
||
# --quiet: 成功时零输出(避免把 "All checks passed!" 误判为问题),失败时只打印诊断行
|
||
RUFF_OUTPUT=$(ruff check --quiet --output-format concise "$FILE_PATH" 2>&1 || true)
|
||
if [[ -n "$RUFF_OUTPUT" ]]; then
|
||
ERRORS+="[ruff] 风格/lint 问题:\n$RUFF_OUTPUT\n\n"
|
||
fi
|
||
fi
|
||
|
||
# ── 2. Radon 圈复杂度(只报告 C 级及以下) ──
|
||
if command -v radon &> /dev/null; then
|
||
RADON_OUTPUT=$(radon cc "$FILE_PATH" -n C -s 2>&1 || true)
|
||
if echo "$RADON_OUTPUT" | grep -qE '^\s+[FMC]\s'; then
|
||
ERRORS+="[radon] 圈复杂度过高(≥C):\n$RADON_OUTPUT\n\n"
|
||
fi
|
||
fi
|
||
|
||
# ── 3. 文件行数检查(warning,不阻塞)──
|
||
LINE_COUNT=$(wc -l < "$FILE_PATH")
|
||
if [[ "$LINE_COUNT" -gt 200 ]]; then
|
||
WARNINGS+="[行数] $FILE_PATH 有 ${LINE_COUNT} 行,超过 200 行建议上限。\n\n"
|
||
fi
|
||
|
||
# ── 4. 禁止裸 except ──
|
||
if grep -nE '^\s*except\s*:\s*$|^\s*except\s+Exception\s*:\s*pass' "$FILE_PATH" 2>/dev/null; then
|
||
ERRORS+="[安全] 检测到裸 except 或 except Exception: pass,请捕获具体异常类型(CLAUDE.md P5)。\n\n"
|
||
fi
|
||
|
||
# ── 5. 禁止硬编码敏感信息 ──
|
||
if grep -nEi "(api_key|secret|password|token)\s*=\s*[\"'][^\"']+[\"']" "$FILE_PATH" 2>/dev/null; then
|
||
ERRORS+="[安全] 疑似硬编码敏感信息,请使用环境变量或 .env 文件。\n\n"
|
||
fi
|
||
|
||
# ── 输出结果 ──
|
||
if [[ -n "$WARNINGS" ]]; then
|
||
echo -e "⚠️ Warnings($FILE_PATH,不阻塞):\n" >&2
|
||
echo -e "$WARNINGS" >&2
|
||
fi
|
||
|
||
if [[ -n "$ERRORS" ]]; then
|
||
echo -e "❌ 代码质量检查发现问题($FILE_PATH):\n" >&2
|
||
echo -e "$ERRORS" >&2
|
||
exit 1
|
||
fi
|
||
|
||
exit 0
|