pre-commit 3.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. #!/usr/bin/env bash
  2. # Pre-commit hook: run clang-format and clang-tidy on staged C/H files.
  3. set -e
  4. # ── Find clang-format ────────────────────────────────────────────────
  5. CLANG_FORMAT=""
  6. for cmd in clang-format clang-format-14 clang-format-15 clang-format-16; do
  7. if command -v "$cmd" &>/dev/null; then
  8. CLANG_FORMAT="$cmd"
  9. break
  10. fi
  11. done
  12. if [ -z "$CLANG_FORMAT" ]; then
  13. for path in /opt/homebrew/bin/clang-format /usr/local/bin/clang-format; do
  14. if [ -x "$path" ]; then
  15. CLANG_FORMAT="$path"
  16. break
  17. fi
  18. done
  19. fi
  20. if [ -z "$CLANG_FORMAT" ]; then
  21. echo "pre-commit: clang-format not found — skipping format check"
  22. echo " Install: brew install clang-format"
  23. exit 0
  24. fi
  25. # ── Collect staged C/H files ────────────────────────────────────────
  26. # Exclude vendored/third-party paths. The local clang-format (22+) drifts
  27. # from CI's clang-format (18) on alignment in components/u8g2-hal-esp-idf/;
  28. # auto-reformatting there causes CI format-check failures.
  29. STAGED=$(git diff --cached --name-only --diff-filter=ACMR -- '*.c' '*.h' \
  30. | { grep -v '^components/u8g2-hal-esp-idf/' || true; } \
  31. | { grep -v '^components/u8g2/' || true; })
  32. if [ -z "$STAGED" ]; then
  33. exit 0
  34. fi
  35. # ── Format check ────────────────────────────────────────────────────
  36. UNFORMATTED=""
  37. for file in $STAGED; do
  38. if ! "$CLANG_FORMAT" --dry-run --Werror "$file" &>/dev/null; then
  39. UNFORMATTED="$UNFORMATTED $file"
  40. fi
  41. done
  42. if [ -n "$UNFORMATTED" ]; then
  43. echo "pre-commit: formatting issues found — fixing automatically"
  44. for file in $UNFORMATTED; do
  45. "$CLANG_FORMAT" -i "$file"
  46. git add "$file"
  47. done
  48. echo "pre-commit: formatted and re-staged:$UNFORMATTED"
  49. fi
  50. # ── Lint (clang-tidy) ───────────────────────────────────────────────
  51. # Only run if compile_commands.json exists (requires a prior build).
  52. if [ ! -f build/compile_commands.json ]; then
  53. echo "pre-commit: build/compile_commands.json not found — skipping lint"
  54. exit 0
  55. fi
  56. # Prefer brew llvm if available (macOS)
  57. if command -v brew &>/dev/null; then
  58. LLVM_PREFIX="$(brew --prefix llvm 2>/dev/null)" || true
  59. [ -n "$LLVM_PREFIX" ] && export PATH="$LLVM_PREFIX/bin:$PATH"
  60. fi
  61. if ! command -v clang-tidy &>/dev/null; then
  62. echo "pre-commit: clang-tidy not found — skipping lint"
  63. exit 0
  64. fi
  65. echo "pre-commit: running clang-tidy on staged files..."
  66. WARNINGS=0
  67. for file in $STAGED; do
  68. OUTPUT=$(clang-tidy -p build "$file" 2>&1 || true)
  69. FILTERED=$(echo "$OUTPUT" | grep -v "file not found \[clang-diagnostic" || true)
  70. if echo "$FILTERED" | grep -q ": warning:"; then
  71. echo "$FILTERED" | grep -E "(: warning:|: note:)"
  72. WARNINGS=1
  73. fi
  74. done
  75. if [ "$WARNINGS" -ne 0 ]; then
  76. echo ""
  77. echo "pre-commit: clang-tidy found issues — commit aborted"
  78. exit 1
  79. fi