mirror of
https://github.com/DeusData/codebase-memory-mcp.git
synced 2026-11-04 10:48:20 +00:00
Merge pull request #1310 from Yyunozor/fix/1264-hook-diff-aware-clang-tidy
fix(hooks): scope pre-commit clang-tidy to staged changes (#1264)
This commit is contained in:
3 files changed
+120
-2
No files matched your search
+7
-1
@@ -729,7 +729,7 @@ PP_OBJ_TSAN = $(BUILD_DIR)/tsan_preprocessor.o
|
||||
|
||||
# ── Targets ──────────────────────────────────────────────────────
|
||||
|
||||
.PHONY: test test-par test-repro test-foundation test-tsan test-daemon-smoke cbm cbm-with-ui frontend embed clean-c lint lint-tidy lint-cppcheck lint-format security
|
||||
.PHONY: test test-par test-repro test-foundation test-tsan test-daemon-smoke cbm cbm-with-ui frontend embed clean-c lint lint-tidy lint-tidy-diff lint-cppcheck lint-format security
|
||||
|
||||
$(BUILD_DIR):
|
||||
mkdir -p $(BUILD_DIR)
|
||||
@@ -1103,6 +1103,12 @@ lint-tidy:
|
||||
@echo "=== clang-tidy ==="
|
||||
@$(CLANG_TIDY) --quiet $(LINT_SRCS) -- $(CFLAGS_COMMON) $(SYSROOT_FLAG)
|
||||
|
||||
# clang-tidy, scoped to this commit's staged changes only (scripts/lint-tidy-diff.sh).
|
||||
# The pre-commit hook runs this instead of the full lint-tidy sweep above, so a
|
||||
# clean commit is not blocked by pre-existing findings elsewhere in the tree.
|
||||
lint-tidy-diff:
|
||||
@scripts/lint-tidy-diff.sh "$(CLANG_TIDY)" $(LINT_SRCS) -- $(CFLAGS_COMMON) $(SYSROOT_FLAG)
|
||||
|
||||
# cppcheck: complementary analysis (config in .cppcheck)
|
||||
lint-cppcheck:
|
||||
@echo "=== cppcheck ==="
|
||||
|
||||
@@ -7,7 +7,11 @@ set -euo pipefail
|
||||
# git config core.hooksPath scripts/hooks
|
||||
|
||||
echo "pre-commit: running all linters in parallel..."
|
||||
make -j3 -f Makefile.cbm lint
|
||||
# clang-tidy runs diff-scoped (lint-tidy-diff, see scripts/lint-tidy-diff.sh):
|
||||
# the full lint-tidy sweep surfaces thousands of pre-existing findings that
|
||||
# have nothing to do with this commit and would block every contributor who
|
||||
# has clang-tidy on PATH. `make lint-tidy` remains available for a full audit.
|
||||
make -j3 -f Makefile.cbm lint-ci lint-tidy-diff
|
||||
|
||||
echo "pre-commit: security audit (source-level)..."
|
||||
scripts/security-audit.sh
|
||||
|
||||
Executable
+108
@@ -0,0 +1,108 @@
|
||||
#!/usr/bin/env bash
|
||||
# lint-tidy-diff.sh — clang-tidy scoped to the lines this commit changes.
|
||||
#
|
||||
# `make lint-tidy` (the full sweep) checks the entire LINT_SRCS tree, which
|
||||
# currently surfaces ~5,100 pre-existing findings unrelated to any given
|
||||
# commit (see Makefile.cbm's `lint-tidy` target). That is fine for a manual
|
||||
# deep audit but makes the pre-commit hook unusable: it blocks every commit,
|
||||
# including ones that touch files with zero findings on the touched lines.
|
||||
#
|
||||
# This script runs the same clang-tidy binary/config against the SAME staged
|
||||
# .c files, but passes clang-tidy's own `-line-filter` so only lines the
|
||||
# commit actually added/modified can produce a diagnostic. Pre-existing
|
||||
# findings on untouched lines — in the same file or elsewhere — are not
|
||||
# reported.
|
||||
#
|
||||
# Invoked by `make lint-tidy-diff` (scripts/hooks/pre-commit), which supplies
|
||||
# the resolved clang-tidy binary, then LINT_SRCS, then `--`, then the
|
||||
# compiler flags — the same argv shape as the `lint-tidy` target itself.
|
||||
#
|
||||
# Known limitation: only staged *.c files that are members of LINT_SRCS are
|
||||
# checked (matching the full sweep's own scope: vendored code and tests/ are
|
||||
# never clang-tidy'd). clang-tidy reports a header's diagnostics under
|
||||
# whatever path it was #include-d with, which is not reliably the same
|
||||
# string as its path in the working tree, so a commit that touches only a
|
||||
# header is not covered here even if a .c file including it also changed;
|
||||
# run `make lint-tidy` for a full pass in that case.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
cd "$ROOT"
|
||||
|
||||
if [ "$#" -lt 1 ]; then
|
||||
echo "usage: lint-tidy-diff.sh <clang-tidy-bin> [LINT_SRCS...] -- [CFLAGS...]" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
CLANG_TIDY="$1"
|
||||
shift
|
||||
|
||||
LINT_SRCS=()
|
||||
while [ "$#" -gt 0 ] && [ "$1" != "--" ]; do
|
||||
LINT_SRCS+=("$1")
|
||||
shift
|
||||
done
|
||||
[ "$#" -gt 0 ] && shift # drop the `--` separator
|
||||
CFLAGS=("$@")
|
||||
|
||||
# bash 3.2 (macOS default, see scripts/check-lsp-originality.sh) has neither
|
||||
# `mapfile` nor associative arrays — read loop + linear membership check.
|
||||
is_lint_src() {
|
||||
local needle="$1" f
|
||||
[ "${#LINT_SRCS[@]}" -eq 0 ] && return 1
|
||||
for f in "${LINT_SRCS[@]}"; do
|
||||
[ "$f" = "$needle" ] && return 0
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
# Staged .c files this commit actually changed (added/copied/modified/
|
||||
# renamed — a delete has no new lines to check) that are members of
|
||||
# LINT_SRCS.
|
||||
CHANGED=()
|
||||
while IFS= read -r f; do
|
||||
[ -n "$f" ] && is_lint_src "$f" && CHANGED+=("$f")
|
||||
done < <(git diff --cached --name-only --diff-filter=ACMR -- '*.c')
|
||||
|
||||
if [ "${#CHANGED[@]}" -eq 0 ]; then
|
||||
echo "lint-tidy-diff: no staged changes to a linted .c file, skipping clang-tidy"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# For each changed file, turn its staged hunks into a clang-tidy line-filter
|
||||
# range: `@@ -a,b +c,d @@` -> lines [c, c+d-1]. Zero-context (-U0) so a range
|
||||
# never includes untouched, pre-existing lines. Pure-deletion hunks (d == 0)
|
||||
# add no new lines and are skipped.
|
||||
LINE_FILTER='['
|
||||
FILTER_SEP=''
|
||||
TARGETS=()
|
||||
for f in "${CHANGED[@]}"; do
|
||||
[ -f "$f" ] || continue # renamed away / not present in the working tree
|
||||
ranges=''
|
||||
range_sep=''
|
||||
while IFS= read -r hunk; do
|
||||
if [[ "$hunk" =~ ^@@\ -[0-9]+(,[0-9]+)?\ \+([0-9]+)(,([0-9]+))?\ @@ ]]; then
|
||||
new_start="${BASH_REMATCH[2]}"
|
||||
new_count="${BASH_REMATCH[4]:-1}"
|
||||
[ "$new_count" -eq 0 ] && continue
|
||||
new_end=$((new_start + new_count - 1))
|
||||
ranges+="${range_sep}[${new_start},${new_end}]"
|
||||
range_sep=','
|
||||
fi
|
||||
done < <(git diff --cached -U0 --diff-filter=ACMR -- "$f" | grep -E '^@@ ')
|
||||
[ -z "$ranges" ] && continue
|
||||
|
||||
LINE_FILTER+="${FILTER_SEP}{\"name\":\"${f}\",\"lines\":[${ranges}]}"
|
||||
FILTER_SEP=','
|
||||
TARGETS+=("$f")
|
||||
done
|
||||
LINE_FILTER+=']'
|
||||
|
||||
if [ "${#TARGETS[@]}" -eq 0 ]; then
|
||||
echo "lint-tidy-diff: staged .c changes are deletion-only, skipping clang-tidy"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "lint-tidy-diff: ${#TARGETS[@]} changed file(s), diff-scoped clang-tidy"
|
||||
"$CLANG_TIDY" --quiet "-line-filter=${LINE_FILTER}" "${TARGETS[@]}" -- "${CFLAGS[@]}"
|
||||
Reference in new issue
Block a user