pre-commit
1 #!/usr/bin/env bash 2 # pre-commit hook for brenner-axiom/docs 3 # Validates Jekyll front matter, permalinks, and index.md coverage. 4 # 5 # Install: cp scripts/pre-commit .git/hooks/pre-commit && chmod +x .git/hooks/pre-commit 6 # Or: ln -sf ../../scripts/pre-commit .git/hooks/pre-commit 7 8 set -euo pipefail 9 10 REPO_ROOT="$(git rev-parse --show-toplevel)" 11 EXIT_CODE=0 12 WARNINGS=0 13 14 # Colors (disabled if not a terminal) 15 if [ -t 1 ]; then 16 RED='\033[0;31m'; YELLOW='\033[1;33m'; GREEN='\033[0;32m'; NC='\033[0m' 17 else 18 RED=''; YELLOW=''; GREEN=''; NC='' 19 fi 20 21 error() { echo -e "${RED}ERROR:${NC} $1"; EXIT_CODE=1; } 22 warn() { echo -e "${YELLOW}WARN:${NC} $1"; WARNINGS=$((WARNINGS + 1)); } 23 ok() { echo -e "${GREEN}OK:${NC} $1"; } 24 25 # Get staged .md files (Added/Modified) 26 STAGED_MD=$(git diff --cached --name-only --diff-filter=AM -- '*.md' | grep -v '^README.md$' || true) 27 28 if [ -z "$STAGED_MD" ]; then 29 exit 0 30 fi 31 32 echo "🔍 Checking $(echo "$STAGED_MD" | wc -l) markdown file(s)..." 33 echo 34 35 # --- Check 1: Front matter exists --- 36 for f in $STAGED_MD; do 37 filepath="$REPO_ROOT/$f" 38 [ -f "$filepath" ] || continue 39 40 # Skip files that are pure data (README files) 41 case "$f" in 42 */README.md|README.md) continue ;; 43 esac 44 45 # Check for YAML front matter 46 if ! head -1 "$filepath" | grep -q '^---$'; then 47 error "$f: Missing Jekyll front matter (must start with ---)" 48 continue 49 fi 50 51 # Extract front matter 52 FM=$(sed -n '1,/^---$/{ /^---$/d; p; }' "$filepath" | head -20) 53 54 # Check required fields: layout and title 55 if ! echo "$FM" | grep -q '^layout:'; then 56 warn "$f: Missing 'layout' in front matter" 57 fi 58 if ! echo "$FM" | grep -q '^title:'; then 59 warn "$f: Missing 'title' in front matter" 60 fi 61 62 # --- Check 2: Permalink must not start with /docs/ --- 63 PERMALINK=$(echo "$FM" | grep '^permalink:' | sed 's/^permalink: *//' | tr -d '"'"'" || true) 64 if [ -n "$PERMALINK" ]; then 65 if echo "$PERMALINK" | grep -q '^/docs/'; then 66 error "$f: Permalink '$PERMALINK' starts with /docs/ — baseurl already adds this. Use '${PERMALINK#/docs}' instead." 67 fi 68 fi 69 done 70 71 # --- Check 3: Index.md coverage --- 72 # Content directories that should have entries in index.md 73 CONTENT_DIRS="research dao ops pitch tutorials playbooks fleet" 74 INDEX_FILE="$REPO_ROOT/index.md" 75 76 if [ -f "$INDEX_FILE" ]; then 77 INDEX_CONTENT=$(cat "$INDEX_FILE") 78 79 for f in $STAGED_MD; do 80 # Only check files in content directories 81 DIR=$(echo "$f" | cut -d/ -f1) 82 case " $CONTENT_DIRS " in 83 *" $DIR "*) ;; 84 *) continue ;; 85 esac 86 87 # Skip index/README files 88 BASENAME=$(basename "$f") 89 case "$BASENAME" in 90 index.md|README.md) continue ;; 91 esac 92 93 # Derive the expected permalink or relative_url reference 94 # Files are typically referenced by their permalink or path 95 STEM="${f%.md}" 96 97 # Check if referenced in index.md (by filename stem, permalink, or path) 98 if ! echo "$INDEX_CONTENT" | grep -q "$STEM\|$(basename "$STEM")"; then 99 warn "$f: Not referenced in index.md — add a link so it's discoverable on the homepage." 100 fi 101 done 102 fi 103 104 echo 105 if [ $EXIT_CODE -ne 0 ]; then 106 echo -e "${RED}Pre-commit failed.${NC} Fix errors above before committing." 107 elif [ $WARNINGS -gt 0 ]; then 108 echo -e "${YELLOW}$WARNINGS warning(s).${NC} Commit proceeding, but consider fixing." 109 else 110 ok "All checks passed." 111 fi 112 113 exit $EXIT_CODE