#!/bin/bash # Documentation Validation Script # Checks for documentation violations and ensures SSOT is maintained set -e # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' # No Color # Track errors ERRORS=0 WARNINGS=0 # Function to print error error() { echo -e "${RED}❌ ERROR:${NC} $1" ERRORS=$((ERRORS + 1)) } # Function to print warning warning() { echo -e "${YELLOW}⚠️ WARNING:${NC} $1" WARNINGS=$((WARNINGS + 1)) } # Function to print success success() { echo -e "${GREEN}✅${NC} $1" } echo "🔍 Checking documentation compliance..." echo "" # Get script directory and project root SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" cd "$PROJECT_ROOT" # Check 1: No new .md files outside docs/ without approval echo "📋 Check 1: Markdown files outside docs/ directory" APPROVED_DIR=".docs-approval" ALLOWED_PATHS=( "README.md" "docs/" "conductor/" "supabase/migrations/" ) # Find all .md files MD_FILES=$(find . -name "*.md" -type f -not -path "*/node_modules/*" -not -path "*/.git/*" | sort) VIOLATIONS=0 for file in $MD_FILES; do # Remove leading ./ file="${file#./}" # Check if file is in allowed path ALLOWED=false for allowed_path in "${ALLOWED_PATHS[@]}"; do if [[ "$file" == "$allowed_path"* ]] || [[ "$file" == "$allowed_path" ]]; then ALLOWED=true break fi done if [ "$ALLOWED" = false ]; then # Check if there's an approval file filename=$(basename "$file") approval_file="$APPROVED_DIR/${filename}.approved" if [ ! -f "$approval_file" ]; then error "Markdown file outside docs/: $file (no approval file found)" VIOLATIONS=$((VIOLATIONS + 1)) else success "Markdown file outside docs/ has approval: $file" fi fi done if [ $VIOLATIONS -eq 0 ]; then success "All markdown files are in allowed locations or have approval" fi echo "" # Check 2: SSOT.md exists and is up to date echo "📋 Check 2: SSOT.md exists and is valid" SSOT_FILE="docs/SSOT.md" if [ ! -f "$SSOT_FILE" ]; then error "SSOT.md not found at $SSOT_FILE" else success "SSOT.md exists" # Check if SSOT has required sections REQUIRED_SECTIONS=( "Overview" "Architecture" "Local Development Setup" "Environment Configuration" "Database & Supabase" "Deployment" "Troubleshooting" "Operations" ) for section in "${REQUIRED_SECTIONS[@]}"; do if ! grep -q "$section" "$SSOT_FILE"; then warning "SSOT.md missing section: $section" fi done fi echo "" # Check 3: SSOT.md updated when key files change echo "📋 Check 3: SSOT.md updated when key files change" KEY_FILES=( "package.json" "docker-compose.yml" "prisma/schema.prisma" ) # If in CI, check if key files changed if [ -n "$CI" ] && [ -n "$GITHUB_BASE_REF" ]; then for key_file in "${KEY_FILES[@]}"; do if [ -f "$key_file" ]; then # Check if file changed in this PR/branch if git diff --name-only "$GITHUB_BASE_REF"..HEAD | grep -q "$key_file"; then # Check if SSOT was also updated if ! git diff --name-only "$GITHUB_BASE_REF"..HEAD | grep -q "$SSOT_FILE"; then warning "$key_file changed but SSOT.md not updated. Please update SSOT.md to reflect changes." else success "$key_file changed and SSOT.md updated" fi fi fi done else # Not in CI, just check if files exist for key_file in "${KEY_FILES[@]}"; do if [ -f "$key_file" ]; then success "Key file exists: $key_file" fi done fi echo "" # Check 4: No duplicate setup instruction blocks echo "📋 Check 4: Duplicate setup instruction blocks" # Look for common setup instruction patterns SETUP_PATTERNS=( "npm install" "npm run dev" "cp .env" "prisma generate" "prisma migrate" ) DUPLICATES=0 for pattern in "${SETUP_PATTERNS[@]}"; do # Count occurrences in all .md files COUNT=$(grep -r "$pattern" docs/*.md 2>/dev/null | wc -l | tr -d ' ') if [ "$COUNT" -gt 3 ]; then warning "Possible duplicate setup instructions for '$pattern' (found $COUNT occurrences)" DUPLICATES=$((DUPLICATES + 1)) fi done if [ $DUPLICATES -eq 0 ]; then success "No obvious duplicate setup instruction blocks found" fi echo "" # Check 5: DOCS_INDEX.md exists and links to SSOT echo "📋 Check 5: DOCS_INDEX.md exists and links to SSOT" INDEX_FILE="docs/DOCS_INDEX.md" if [ ! -f "$INDEX_FILE" ]; then error "DOCS_INDEX.md not found" else success "DOCS_INDEX.md exists" # Check if it links to SSOT if ! grep -q "SSOT.md" "$INDEX_FILE"; then error "DOCS_INDEX.md does not link to SSOT.md" else success "DOCS_INDEX.md links to SSOT.md" fi fi echo "" # Check 6: README.md points to documentation echo "📋 Check 6: README.md points to documentation" README_FILE="README.md" if [ ! -f "$README_FILE" ]; then warning "README.md not found" else # Check if README links to docs if ! grep -qi "docs/" "$README_FILE" && ! grep -qi "DOCS_INDEX" "$README_FILE" && ! grep -qi "SSOT" "$README_FILE"; then warning "README.md may not link to documentation. Ensure it points to docs/DOCS_INDEX.md or docs/SSOT.md" else success "README.md appears to link to documentation" fi fi echo "" # Check 7: Validate markdown links (basic check) echo "📋 Check 7: Basic markdown link validation" # Check for broken internal links (files that don't exist) LINK_VIOLATIONS=0 for file in docs/*.md; do if [ -f "$file" ]; then # Extract markdown links links=$(grep -oE '\[.*\]\([^)]+\)' "$file" | sed 's/.*(\(.*\))/\1/' | grep -v '^http' | grep -v '^#' | grep -v '^mailto:') for link in $links; do # Remove anchor if present file_path="${link%%#*}" # Skip empty or special links if [ -z "$file_path" ] || [[ "$file_path" == "http"* ]]; then continue fi # Check if linked file exists if [ ! -f "$file_path" ] && [ ! -f "docs/$file_path" ]; then warning "Possible broken link in $file: $link" LINK_VIOLATIONS=$((LINK_VIOLATIONS + 1)) fi done fi done if [ $LINK_VIOLATIONS -eq 0 ]; then success "No obvious broken internal links found" fi echo "" # Summary echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "📊 Summary" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" if [ $ERRORS -eq 0 ] && [ $WARNINGS -eq 0 ]; then echo -e "${GREEN}✅ All checks passed!${NC}" exit 0 elif [ $ERRORS -eq 0 ]; then echo -e "${YELLOW}⚠️ $WARNINGS warning(s) found (non-blocking)${NC}" exit 0 else echo -e "${RED}❌ $ERRORS error(s) and $WARNINGS warning(s) found${NC}" echo "" echo "Please fix the errors before merging." exit 1 fi