mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-10 03:28:53 +00:00
feat(pre-commit): save full lint output to a per-worktree log file (#36004)
* feat(pre-commit): save full lint output to a per-worktree log file * docs(claude): point agents at the pre-commit log instead of rerunning * fix(pre-commit): warn when the log cannot be created or fully written
This commit is contained in:
parent
f54cd287a2
commit
f047124b5a
3 changed files with 54 additions and 0 deletions
|
|
@ -41,6 +41,8 @@ Python max line length is 120, not 88
|
|||
|
||||
When you fix violations gated by `ruff-strict-budget.json`, `type-discipline-budget.json`, or `basedpyright-code-budget.json`, run `make lint-budget-update` and commit the lowered limits so the ceilings ratchet down instead of leaving stale headroom. It measures the working tree, so it must contain exactly the fixes you're committing
|
||||
|
||||
`make pre-commit` always saves its complete output to a per-worktree log file and prints that path as its first and last output lines. To inspect a run, read or grep that log instead of re-running the multi-minute checks just to see a different slice, and re-run only after the working tree actually changed
|
||||
|
||||
If you're trying to create a new function that relies on untyped stuff, instead of adding more Any's and pushing `reportAny` / `reportExplicitAny` closer to their basedpyright ceilings, just validate it in the caller with Pydantic (a model or `TypeAdapter` that returns the typed thing or raises will do) and then pass the now typed variable in
|
||||
|
||||
If you get an LIT001 or LIT002 fail, refactor the code to follow functional programming best practices rather than introducing mutable data structures. For example, build values in one shot with comprehensions or generators wrapped in `tuple()` / `frozenset()` instead of seeding an empty `list`/`dict`/`set` and mutating it over time. Ideally, `# mutable-ok` is never used; reach for it only as a genuine last resort when an immutable rewrite is truly impossible, and always pair it with a real reason
|
||||
|
|
|
|||
|
|
@ -17,6 +17,23 @@
|
|||
|
||||
set -eu
|
||||
|
||||
if [ -z "${PRE_COMMIT_LINT_INNER:-}" ]; then
|
||||
log_file=$(git rev-parse --path-format=absolute --git-path pre_commit_lint.log)
|
||||
if : > "$log_file" 2>/dev/null; then
|
||||
echo "pre-commit: logging full output to $log_file"
|
||||
PRE_COMMIT_LINT_INNER=1 "$0" "$@" 2>&1 | tee "$log_file"
|
||||
pipe_status=("${PIPESTATUS[@]}")
|
||||
if [ "${pipe_status[1]}" -eq 0 ]; then
|
||||
echo "pre-commit: full log: $log_file"
|
||||
else
|
||||
echo "pre-commit: WARNING - writing $log_file failed; the log may be incomplete" >&2
|
||||
fi
|
||||
exit "${pipe_status[0]}"
|
||||
fi
|
||||
echo "pre-commit: WARNING - cannot write $log_file; output will not be saved" >&2
|
||||
PRE_COMMIT_LINT_INNER=1 exec "$0" "$@"
|
||||
fi
|
||||
|
||||
repo_root=$(git rev-parse --show-toplevel)
|
||||
cd "$repo_root"
|
||||
|
||||
|
|
|
|||
|
|
@ -148,6 +148,41 @@ def test_all_blocks_passing_exits_zero(tmp_path: Path) -> None:
|
|||
assert proc.returncode == 0, proc.stdout + proc.stderr
|
||||
|
||||
|
||||
def test_full_output_is_saved_to_a_log_file_in_the_git_dir(tmp_path: Path) -> None:
|
||||
repo, bin_dir = _sandbox(tmp_path)
|
||||
(repo / "scratch.txt").write_text("")
|
||||
proc = _run(repo, bin_dir, {})
|
||||
assert proc.returncode == 0, proc.stdout + proc.stderr
|
||||
log_file = repo / ".git" / "pre_commit_lint.log"
|
||||
log = log_file.read_text()
|
||||
assert "linting Python" in log
|
||||
assert "linting dashboard" in log
|
||||
assert "API types" in log
|
||||
assert "unstaged/untracked changes" in log
|
||||
assert f"pre-commit: full log: {log_file}" in proc.stdout
|
||||
assert "pre-commit: full log:" not in log
|
||||
|
||||
|
||||
def test_unwritable_log_warns_and_falls_back_to_running_without_one(tmp_path: Path) -> None:
|
||||
repo, bin_dir = _sandbox(tmp_path)
|
||||
(repo / ".git" / "pre_commit_lint.log").mkdir()
|
||||
proc = _run(repo, bin_dir, {})
|
||||
assert proc.returncode == 0, proc.stdout + proc.stderr
|
||||
assert "linting Python" in proc.stdout
|
||||
assert "output will not be saved" in proc.stderr
|
||||
assert "pre-commit: full log:" not in proc.stdout
|
||||
failing = _run(repo, bin_dir, {"STUB_FAIL": "make-lint"})
|
||||
assert failing.returncode == 1
|
||||
|
||||
|
||||
def test_failing_run_exit_code_survives_the_log_pipeline(tmp_path: Path) -> None:
|
||||
repo, bin_dir = _sandbox(tmp_path)
|
||||
proc = _run(repo, bin_dir, {"STUB_FAIL": "make-lint"})
|
||||
assert proc.returncode == 1
|
||||
log = (repo / ".git" / "pre_commit_lint.log").read_text()
|
||||
assert "Python lint failed" in log
|
||||
|
||||
|
||||
def _wait_until(predicate: Callable[[], bool], timeout_seconds: float) -> bool:
|
||||
deadline = time.monotonic() + timeout_seconds
|
||||
while time.monotonic() < deadline:
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue