Files
vibe_coding/scripts/ai-guard.sh
2026-03-05 21:27:11 +08:00

219 lines
6.1 KiB
Bash

#!/usr/bin/env bash
# =============================================================================
# AI Guard — 质量门禁脚本 (PHP Hyperf + Vue 3 双栈)
# =============================================================================
# 用法:
# bash scripts/ai-guard.sh # 运行所有检查
# bash scripts/ai-guard.sh --pre # 预提交检查
# bash scripts/ai-guard.sh --post # 后执行检查
# bash scripts/ai-guard.sh --quick # 快速检查 (lint + types)
# =============================================================================
set -euo pipefail
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
PASS=0
FAIL=0
WARN=0
LOG_DIR=".ai-logs/guard"
mkdir -p "$LOG_DIR"
LOG_FILE="$LOG_DIR/$(date +%Y-%m-%d).log"
HAS_NODE=false
HAS_PHP=false
# Detect project stacks
[ -f "package.json" ] && HAS_NODE=true
[ -f "composer.json" ] && HAS_PHP=true
# Also check subdirectories
[ -f "Case-Database-Frontend-user/package.json" ] && HAS_NODE=true
[ -f "Case-Database-Frontend-admin/package.json" ] && HAS_NODE=true
[ -f "Case-Database-Backend/composer.json" ] && HAS_PHP=true
log() {
echo "[$(date +%H:%M:%S)] $*" >> "$LOG_FILE"
}
check() {
local name="$1"
local cmd="$2"
local required="${3:-true}"
printf " %-30s" "$name"
if eval "$cmd" > /dev/null 2>&1; then
echo -e "${GREEN}✅ PASS${NC}"
PASS=$((PASS + 1))
log "PASS: $name"
else
if [ "$required" = "true" ]; then
echo -e "${RED}❌ FAIL${NC}"
FAIL=$((FAIL + 1))
log "FAIL: $name"
else
echo -e "${YELLOW}⚠️ WARN${NC}"
WARN=$((WARN + 1))
log "WARN: $name"
fi
fi
}
# ----- 敏感信息扫描 -----
check_secrets() {
local name="Secret Scan"
printf " %-30s" "$name"
local patterns=(
'AKIA[0-9A-Z]{16}'
'password\s*=\s*["'\''"][^"'\''"]+'
'api[_-]?key\s*=\s*["'\''"][^"'\''"]+'
'BEGIN\s+(RSA\s+)?PRIVATE\s+KEY'
'ghp_[a-zA-Z0-9]{36}'
'sk-[a-zA-Z0-9]{48}'
)
local found=false
for pattern in "${patterns[@]}"; do
if git diff --cached --diff-filter=d -U0 2>/dev/null | grep -qiE "$pattern"; then
found=true
break
fi
done
if [ "$found" = "true" ]; then
echo -e "${RED}❌ FAIL — 检测到疑似密钥/凭证!${NC}"
FAIL=$((FAIL + 1))
log "FAIL: $name — secrets detected"
else
echo -e "${GREEN}✅ PASS${NC}"
PASS=$((PASS + 1))
log "PASS: $name"
fi
}
# ----- Context Doctor (lite) -----
check_context_lite() {
local name="Context Doctor Lite"
printf " %-30s" "$name"
if bash scripts/context-doctor.sh --lite --strict > /dev/null 2>&1; then
echo -e "${GREEN}✅ PASS${NC}"
PASS=$((PASS + 1))
log "PASS: $name"
else
echo -e "${RED}❌ FAIL${NC}"
FAIL=$((FAIL + 1))
log "FAIL: $name"
echo -e " ${YELLOW}↳ 运行 'bash scripts/context-doctor.sh --lite --strict' 查看详情${NC}"
fi
}
# ----- Frontend checks -----
run_frontend_checks() {
if [ "$HAS_NODE" = "true" ]; then
for dir in Case-Database-Frontend-user Case-Database-Frontend-admin; do
if [ -f "$dir/package.json" ]; then
echo -e "\n ${BLUE}── Frontend ($dir) ──${NC}"
check "ESLint ($dir)" "cd $dir && npm run lint --silent" "${1:-true}"
if [ "${2:-false}" = "true" ]; then
check "Frontend Tests ($dir)" "cd $dir && npm run test --silent" "false"
check "Frontend Build ($dir)" "cd $dir && npm run build --silent" "false"
fi
fi
done
fi
}
# ----- Backend checks -----
run_backend_checks() {
if [ "$HAS_PHP" = "true" ]; then
echo -e "\n ${BLUE}── Backend (PHP Hyperf / Swoole) ──${NC}"
local BACKEND_DIR="."
[ -d "Case-Database-Backend" ] && BACKEND_DIR="Case-Database-Backend"
check "PHPStan" "cd $BACKEND_DIR && composer analyse --no-progress 2>/dev/null || vendor/bin/phpstan analyse --no-progress" "${1:-true}"
if [ "${2:-false}" = "true" ]; then
check "PHPUnit" "cd $BACKEND_DIR && composer test --no-interaction 2>/dev/null || vendor/bin/phpunit" "false"
fi
fi
}
# ----- 主逻辑 -----
MODE="${1:---post}"
echo ""
echo -e "${BLUE}🛡️ AI Guard — Quality Gateway (Dual Stack)${NC}"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
if [ "$HAS_NODE" = "true" ]; then
echo -e " ${GREEN}${NC} Frontend detected (package.json)"
fi
if [ "$HAS_PHP" = "true" ]; then
echo -e " ${GREEN}${NC} Backend detected (composer.json)"
fi
log "=== AI Guard Run: mode=$MODE node=$HAS_NODE php=$HAS_PHP ==="
case "$MODE" in
--pre)
echo -e "\n${BLUE}Mode: Pre-Commit${NC}"
check_secrets
check "File count (≤10)" "[ $(git diff --cached --name-only | wc -l) -le 10 ]" "false"
check_context_lite
;;
--post)
echo -e "\n${BLUE}Mode: Post-Execute${NC}"
run_frontend_checks "true"
run_backend_checks "true"
check_secrets
;;
--quick)
echo -e "\n${BLUE}Mode: Quick Check${NC}"
run_frontend_checks "true"
run_backend_checks "true"
;;
*)
echo -e "\n${BLUE}Mode: Full Check${NC}"
run_frontend_checks "true" "true"
run_backend_checks "true" "true"
if [ "$HAS_NODE" = "true" ]; then
for dir in Case-Database-Frontend-user Case-Database-Frontend-admin; do
if [ -f "$dir/package.json" ]; then
check "npm audit ($dir)" "cd $dir && npm audit --audit-level=high" "false"
fi
done
fi
if [ "$HAS_PHP" = "true" ]; then
BD="."
[ -d "Case-Database-Backend" ] && BD="Case-Database-Backend"
check "composer audit" "cd $BD && composer audit" "false"
fi
check_secrets
;;
esac
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo -e " ${GREEN}$PASS passed${NC} ${RED}$FAIL failed${NC} ${YELLOW}⚠️ $WARN warnings${NC}"
echo ""
log "Summary: pass=$PASS fail=$FAIL warn=$WARN"
if [ "$FAIL" -gt 0 ]; then
echo -e "${RED}💥 有必须修复的问题!${NC}"
exit 1
else
echo -e "${GREEN}🎉 所有必要检查已通过!${NC}"
exit 0
fi