/ scripts / pre-commit
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