mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-01-25 02:08:11 +00:00
170 lines
6.1 KiB
Bash
Executable File
170 lines
6.1 KiB
Bash
Executable File
#!/bin/bash
|
||
|
||
# Prowler UI - Pre-Commit Hook
|
||
# Optionally validates ONLY staged files against AGENTS.md standards using Claude Code
|
||
# Controlled by CODE_REVIEW_ENABLED in .env
|
||
|
||
set -e
|
||
|
||
# Colors
|
||
RED='\033[0;31m'
|
||
GREEN='\033[0;32m'
|
||
YELLOW='\033[1;33m'
|
||
BLUE='\033[0;34m'
|
||
NC='\033[0m' # No Color
|
||
|
||
echo ""
|
||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||
echo "🚀 Prowler UI - Pre-Commit Hook"
|
||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||
echo ""
|
||
|
||
# Load .env file (look in git root directory)
|
||
GIT_ROOT=$(git rev-parse --show-toplevel)
|
||
if [ -f "$GIT_ROOT/ui/.env" ]; then
|
||
CODE_REVIEW_ENABLED=$(grep "^CODE_REVIEW_ENABLED" "$GIT_ROOT/ui/.env" | cut -d'=' -f2 | tr -d ' ')
|
||
elif [ -f "$GIT_ROOT/.env" ]; then
|
||
CODE_REVIEW_ENABLED=$(grep "^CODE_REVIEW_ENABLED" "$GIT_ROOT/.env" | cut -d'=' -f2 | tr -d ' ')
|
||
elif [ -f ".env" ]; then
|
||
CODE_REVIEW_ENABLED=$(grep "^CODE_REVIEW_ENABLED" .env | cut -d'=' -f2 | tr -d ' ')
|
||
else
|
||
CODE_REVIEW_ENABLED="false"
|
||
fi
|
||
|
||
# Normalize the value to lowercase
|
||
CODE_REVIEW_ENABLED=$(echo "$CODE_REVIEW_ENABLED" | tr '[:upper:]' '[:lower:]')
|
||
|
||
echo -e "${BLUE}ℹ️ Code Review Status: ${CODE_REVIEW_ENABLED}${NC}"
|
||
echo ""
|
||
|
||
# Get staged files (what will be committed)
|
||
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(tsx?|jsx?)$' || true)
|
||
|
||
if [ "$CODE_REVIEW_ENABLED" = "true" ]; then
|
||
if [ -z "$STAGED_FILES" ]; then
|
||
echo -e "${YELLOW}⚠️ No TypeScript/JavaScript files staged to validate${NC}"
|
||
echo ""
|
||
else
|
||
echo -e "${YELLOW}🔍 Running Claude Code standards validation...${NC}"
|
||
echo ""
|
||
echo -e "${BLUE}📋 Files to validate:${NC}"
|
||
echo "$STAGED_FILES" | while IFS= read -r file; do echo " - $file"; done
|
||
echo ""
|
||
|
||
echo -e "${BLUE}📤 Sending to Claude Code for validation...${NC}"
|
||
echo ""
|
||
|
||
# Build prompt with full file contents
|
||
VALIDATION_PROMPT=$(
|
||
cat <<'PROMPT_EOF'
|
||
You are a code reviewer for the Prowler UI project. Analyze the full file contents of changed files below and validate they comply with AGENTS.md standards.
|
||
|
||
**RULES TO CHECK:**
|
||
1. React Imports: NO `import * as React` or `import React, {` → Use `import { useState }`
|
||
2. TypeScript: NO union types like `type X = "a" | "b"` → Use const-based: `const X = {...} as const`
|
||
3. Tailwind: NO `var()` or hex colors in className → Use Tailwind utilities and semantic color classes. Exception: `var()` is allowed when passing colors to chart/graph components that require CSS color strings (not Tailwind classes) for their APIs.
|
||
4. cn(): Use for merging multiple classes or for conditionals (handles Tailwind conflicts with twMerge) → `cn(BUTTON_STYLES.base, BUTTON_STYLES.active, isLoading && "opacity-50")`
|
||
5. React 19: NO `useMemo`/`useCallback` without reason
|
||
6. Zod v4: Use `.min(1)` not `.nonempty()`, `z.email()` not `z.string().email()`. All inputs must be validated with Zod.
|
||
7. File Org: 1 feature = local, 2+ features = shared
|
||
8. Directives: Server Actions need "use server", clients need "use client"
|
||
9. Implement DRY, KISS principles. (example: reusable components, avoid repetition)
|
||
10. Layout must work for all the responsive breakpoints (mobile, tablet, desktop)
|
||
11. ANY types cannot be used - CRITICAL: Check for `: any` in all visible lines
|
||
12. Use the components inside components/shadcn if possible
|
||
13. Check Accessibility best practices (like alt tags in images, semantic HTML, Aria labels, etc.)
|
||
|
||
=== FILES TO REVIEW ===
|
||
PROMPT_EOF
|
||
)
|
||
|
||
# Add full file contents for each staged file
|
||
for file in $STAGED_FILES; do
|
||
VALIDATION_PROMPT="$VALIDATION_PROMPT
|
||
|
||
=== FILE: $file ===
|
||
$(cat "$file" 2>/dev/null || echo "Error reading file")"
|
||
done
|
||
|
||
VALIDATION_PROMPT="$VALIDATION_PROMPT
|
||
|
||
=== END FILES ===
|
||
|
||
**IMPORTANT: Your response MUST start with exactly one of these lines:**
|
||
STATUS: PASSED
|
||
STATUS: FAILED
|
||
|
||
**If FAILED:** List each violation with File, Line Number, Rule Number, and Issue.
|
||
**If PASSED:** Confirm all files comply with AGENTS.md standards.
|
||
|
||
**Start your response now with STATUS:**"
|
||
|
||
# Send to Claude Code
|
||
if VALIDATION_OUTPUT=$(echo "$VALIDATION_PROMPT" | claude 2>&1); then
|
||
echo "$VALIDATION_OUTPUT"
|
||
echo ""
|
||
|
||
# Check result - STRICT MODE: fail if status unclear
|
||
if echo "$VALIDATION_OUTPUT" | grep -q "^STATUS: PASSED"; then
|
||
echo ""
|
||
echo -e "${GREEN}✅ VALIDATION PASSED${NC}"
|
||
echo ""
|
||
elif echo "$VALIDATION_OUTPUT" | grep -q "^STATUS: FAILED"; then
|
||
echo ""
|
||
echo -e "${RED}❌ VALIDATION FAILED${NC}"
|
||
echo -e "${RED}Fix violations before committing${NC}"
|
||
echo ""
|
||
exit 1
|
||
else
|
||
echo ""
|
||
echo -e "${RED}❌ VALIDATION ERROR${NC}"
|
||
echo -e "${RED}Could not determine validation status from Claude Code response${NC}"
|
||
echo -e "${YELLOW}Response must start with 'STATUS: PASSED' or 'STATUS: FAILED'${NC}"
|
||
echo ""
|
||
echo -e "${YELLOW}To bypass validation temporarily, set CODE_REVIEW_ENABLED=false in .env${NC}"
|
||
echo ""
|
||
exit 1
|
||
fi
|
||
else
|
||
echo -e "${YELLOW}⚠️ Claude Code not available${NC}"
|
||
fi
|
||
echo ""
|
||
fi
|
||
else
|
||
echo -e "${YELLOW}⏭️ Code review disabled (CODE_REVIEW_ENABLED=false)${NC}"
|
||
echo ""
|
||
fi
|
||
|
||
# Run healthcheck (typecheck and lint check)
|
||
echo -e "${BLUE}🏥 Running healthcheck...${NC}"
|
||
echo ""
|
||
|
||
cd ui || cd .
|
||
if pnpm run healthcheck; then
|
||
echo ""
|
||
echo -e "${GREEN}✅ Healthcheck passed${NC}"
|
||
echo ""
|
||
else
|
||
echo ""
|
||
echo -e "${RED}❌ Healthcheck failed${NC}"
|
||
echo -e "${RED}Fix type errors and linting issues before committing${NC}"
|
||
echo ""
|
||
exit 1
|
||
fi
|
||
|
||
# Run build
|
||
echo -e "${BLUE}🔨 Running build...${NC}"
|
||
echo ""
|
||
|
||
if pnpm run build; then
|
||
echo ""
|
||
echo -e "${GREEN}✅ Build passed${NC}"
|
||
echo ""
|
||
else
|
||
echo ""
|
||
echo -e "${RED}❌ Build failed${NC}"
|
||
echo -e "${RED}Fix build errors before committing${NC}"
|
||
echo ""
|
||
exit 1
|
||
fi
|