Commit graph

19 commits

Author SHA1 Message Date
mateo-berri
eafddaaa12 feat(scripts): queue heavy gates behind a machine-wide slot lock 2026-08-14 17:22:32 -07:00
mateo-berri
3fbe40c9b1 fix(scripts): print the make check verdict on early informational exits 2026-08-13 19:13:44 -07:00
mateo-berri
cfbd43172a fix(scripts): end make check with a ran/skipped summary and verdict 2026-08-13 18:49:07 -07:00
mateo-berri
fb7861fbfd build(lint): count deleted files toward check triggers 2026-08-08 10:41:06 -07:00
mateo-berri
f038be22db build(lint): rename make pre-commit to make check with a working-tree fallback 2026-08-08 03:25:35 -07:00
Mateo Wang
f047124b5a
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
2026-08-05 15:16:52 -07:00
mateo-berri
d7dbb28b32 fix(pre-commit): scope interrupt cleanup to the job process groups 2026-08-04 21:37:01 -07:00
mateo-berri
a1f497c7c0 fix(pre-commit): kill background jobs and remove their logs on interrupt 2026-08-04 21:28:46 -07:00
mateo-berri
2f36625e7f perf(pre-commit): run python, dashboard, and gen-api checks concurrently 2026-08-04 21:18:00 -07:00
mateo-berri
4a2ceed595 Stop advising a pre-commit re-run for stale dashboard API types
The stale-types failure already writes the regenerated schema.d.ts to the
working tree, and staging it cannot introduce a new failure: the file is
listed in .prettierignore and the eslint config ignores, so no lint pass
sees it, and gen:api derives it purely from the Python proxy code, so a
second regeneration is a no-op. The only reason left to re-run is when
other checks also failed, so say exactly that in the script message and
CLAUDE.md instead of prescribing an unconditional re-run.
2026-08-04 19:38:34 -07:00
mateo-berri
560dc6dd0d ci: run check_e2e_no_raw_requests in make pre-commit for staged tests/e2e files
Mirrors the new test-code-quality.yml step locally so a green pre-commit stays predictive: the sub-second checker fires only when tests/e2e Python files are staged, matching the script's staged-file gating for every other block.
2026-07-21 16:47:47 -07:00
mateo-berri
c9beaf85ff build(dev-env): add make bootstrap and unprovisioned-checkout preflight to pre-commit 2026-07-11 19:25:39 -07:00
yuneng-jiang
b21c4ce865
Merge pull request #32930 from BerriAI/litellm_/remove-eslint-metrics-63b302
chore(ui): remove eslint-metrics.json lint-count snapshot
2026-07-11 13:29:08 -07:00
Yuneng Jiang
7cdf42d770
chore(ui): remove eslint-metrics.json lint-count snapshot
The eslint-metrics.json snapshot duplicated the violation counts already
enforced by eslint-budgets.json. Keeping it current added a CI drift check,
a pre-commit regenerate-and-flag step, and a standalone npm run lint:metrics
script, none of which caught anything the budget gate did not, yet all of
which failed noisily whenever the snapshot went stale. This drops the file
and that machinery while leaving eslint-budgets.json as the actual ratchet
gate
2026-07-11 11:54:42 -07:00
mateo-berri
55c8ca41b5 ci: gate tests/e2e on zero basedpyright errors in pre-commit and lint CI 2026-07-11 10:25:22 -07:00
ryan-crabbe-berri
270406b8ad
build(pre-commit): regenerate eslint-metrics.json instead of failing on drift (#32717)
The dashboard lint-budgets step ran check-lint-budgets.mjs in --check mode,
which fails and tells you to run `npm run lint:metrics` and re-stage by hand.
Add a --write mode that rewrites eslint-metrics.json from the same eslint
report, and have pre-commit use it, then flag drift via git diff so you
re-stage; this mirrors how the block below regenerates schema.d.ts. CI keeps
using --check, so it still fails on a stale committed metrics file.
2026-07-10 11:51:58 -07:00
Mateo Wang
cf6fdac304
perf(lint): skip and cache base gate passes, parallelize make lint, skip redundant prisma generate (#32000)
* perf(lint): skip and cache base gate passes, parallelize make lint, skip redundant prisma generate

make pre-commit paid for a full second basedpyright pass over a merge-base
worktree on every run even when no rule was over its ceiling, re-generated an
unchanged Prisma client, and ran seven independent checks sequentially. The
basedpyright and ruff strict gates now skip the base pass when head is within
every limit (the same early-out type_discipline_gate already had), the
basedpyright base counts are cached under the git common dir keyed by
merge-base commit, pyrightconfig.json, and uv.lock, prisma generate only runs
when the schema or prisma version changed, and make lint fans its checks out
through a parallel sub-make after a single setup phase

* fix(lint): keep the base-cache scratch file out of the prune glob

The tmp+rename scratch in store_counts was named basedpyright-base-<hash>.json.tmp,
which the stale-entry prune glob (basedpyright-base-*) also matches, so a concurrent
lint run from another worktree sharing the same git common dir could unlink it between
write_text and replace and crash the gate with FileNotFoundError. The scratch is now
dot-prefixed so the glob can never see it, pid-suffixed so concurrent writers of the
same entry never share a scratch, and the prune glob is restricted to committed
*.json entries
2026-07-02 19:24:00 -07:00
Mateo Wang
e141596204
refactor(lint): collapse type/lint budgets to a single per-rule limit (#31883)
* chore(lint): raise basedpyright per-rule slack to 50% of baseline

The per-rule ceilings in basedpyright-code-budget.json sat at roughly 10% slack over baseline, which several in-flight PRs are already bumping into. Raise the slack on every rule to at least 50% of its baseline so there is ample headroom for a long while, while never lowering any rule that already had more generous slack (e.g. reportReturnType stays at 100).

Co-authored-by: Mateo Wang <mateo-berri@users.noreply.github.com>

* refactor(lint): collapse type/lint budgets to a single per-rule limit

The three non-frontend budget files (ruff-strict, type-discipline, basedpyright-code) tracked a per-rule baseline and slack whose sum was the ceiling. Nothing consumed the split beyond that sum, so this replaces both keys with a single limit equal to the old baseline + slack; the original baselines live in git history if anyone needs them.

The gate scripts and the ratchet guard now read limit directly. lint-budget-update no longer re-captures raw counts; it ratchets each rule's limit down by the number of violations this branch cleared since its branch point (the merge-base), so the granted headroom shrinks by exactly what was fixed and a limit never rises. The ratchet guard reads either schema so it still compares correctly across the migration boundary.

Co-authored-by: Mateo Wang <mateo-berri@users.noreply.github.com>

* chore(lint): surface staged-vs-working parity for pre-commit and budget-update

make pre-commit selects which checks to run from the staged index but runs the linters over the working tree, so unstaged edits to tracked files and untracked files skew a green/red away from what a commit of only the staged changes would produce. There is no safe in-place way to lint the index, so the script now warns when unstaged or untracked changes are present, and CLAUDE.md documents that you must stage everything first for both make pre-commit and make lint-budget-update to predict CI correctly.

Co-authored-by: Mateo Wang <mateo-berri@users.noreply.github.com>

* docs(lint): list type-discipline budget in lint-budget-update instruction

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Mateo Wang <mateo-berri@users.noreply.github.com>
2026-07-01 18:12:35 +03:00
Mateo Wang
0965a4d1f4
chore: shift CI lint left with an opt-in make pre-commit and CLAUDE.md rule (#31544)
* chore: shift CI lint left with a pre-commit hook and CLAUDE.md rule

Add an opt-in pre-commit hook (.githooks/pre-commit, active after
make install-hooks) that runs the CI-equivalent checks against staged
files: make lint for Python, prettier plus eslint for the dashboard,
and a gen:api drift check for the proxy OpenAPI types. Document the
same expectation in CLAUDE.md so reds surface locally instead of in CI.

* fix: make `make lint` isomorphic to the CI lint job

`make lint` diverged from test-linting.yml in ways that produced both
false reds and false greens: its format-check ran over the whole repo
(CI scopes it to changed files vs the base), its ruff-strict budget ran
in absolute mode (CI runs it as a delta vs base), and it omitted the
type-discipline gate entirely. Recompose `lint` to replay CI's exact
sequence: diff-scoped ruff format check, whole-tree ruff check, the
strict / type-discipline / basedpyright budgets as a delta resolved the
same way CI resolves it (merge-base with origin/litellm_internal_staging),
then circular-import and import-safety. Factor the repeated base fetch
into one shared prerequisite so the chain hits the network once.

Align the pre-commit hook's eslint invocation with the CI frontend-lint
job (`--pass-on-unpruned-suppressions`) and fix the CLAUDE.md guidance to
point at the diff-scoped frontend commands instead of the whole-folder
npm scripts, which are broader than CI.

* fix(githooks): make pre-commit 1:1 with CI frontend-lint, lint, and type-gen

The shift-left pre-commit hook diverged from the CI jobs it claims to mirror, so a clean commit did not actually mean a green CI lint.

The dashboard block only ran prettier and eslint over js/jsx/ts/tsx/mjs/cjs, but CI's frontend-lint runs prettier over a wider set (also json, css, scss, md, mdx, yml, yaml, html) and additionally gates the whole-folder eslint lint budgets via scripts/check-lint-budgets.mjs. The hook now mirrors that split and runs the budget check, so a dashboard commit that passes locally passes the job.

The API-types block ran npm run gen:api without LITELLM_PYTHON, so it shelled out to the system python3 which has no litellm installed and always failed with a false 'could not regenerate API types' red. It now passes LITELLM_PYTHON="uv run --no-sync python" the way check-ui-api-types.yml does.

make lint format-checks the files in origin/base...HEAD, which at pre-commit time predates the staged change, so a brand-new commit's formatting went unchecked. The Python block now also runs ruff format --check over the staged litellm files directly to cover that case, and its trigger is scoped to staged litellm/ files (the only tree CI's lint job inspects) so a tests-only or scripts-only commit skips the slow make lint instead of wasting time on a run that could not catch anything.

CLAUDE.md's shift-left rule was cut off mid-sentence and understated the frontend checks; it now describes all three gates accurately and points agents at make install-hooks to run them automatically before each commit.

Co-authored-by: Mateo Wang <mateo-berri@users.noreply.github.com>

* fix(githooks): scope the API-types check to all of check-ui-api-types.yml's triggers

spec_files was filtered from the staged Python files, so the gen:api drift
check only fired for .py changes under litellm/proxy or litellm/types. CI's
check-ui-api-types.yml triggers on any file under those directories (Prisma
schema, configs) plus the generator script and the dashboard package files,
so a non-Python proxy/types change could pass the hook and still fail CI.
Match the workflow's full trigger set instead.

* fix(pre-commit): run prisma generate before gen:api to mirror CI

* refactor(githooks): run shift-left lint via on-demand make pre-commit, not an auto-firing hook

The pre-commit hook ran make lint plus the dashboard eslint budgets, which are minutes of work (basedpyright over litellm/, a whole-folder eslint . pass at ~40s). Wiring that into core.hooksPath via make install-hooks meant every human commit, not just an agent's, paid that cost, which is real friction for interactive committers.

Move the staged-file checks out of .githooks/ into scripts/pre_commit_lint.sh and expose them as make pre-commit, and keep .githooks/ to only the fast Conventional Commits / Branches hooks so make install-hooks no longer makes commits slow. Agents run make pre-commit right before each commit (CLAUDE.md instructs this), so the slow gates fire only for the commits an agent is making and never auto-fire for a human typing git commit. The script stays hook-compatible for anyone who still wants it to fire automatically via a symlink.

Preferred this over sniffing an agent env var to auto-fire only for agents: that is fragile (misses agents when the var is unset, fires on humans when it leaks into their shell, and silently no-ops a hook a human deliberately installed), whereas an on-demand command achieves the same humans-never, agents-per-commit outcome deterministically.

Co-authored-by: Mateo Wang <mateo-berri@users.noreply.github.com>

* fix(pre-commit): run make lint last so it can't prune the proxy deps gen:api needs

make lint's install-dev prerequisite runs uv sync --frozen, which prunes the proxy extras (prisma, websockets, ...) from the venv. With the Python block running first, the subsequent API-types block then failed: gen:api imports litellm.proxy.proxy_server, which needs those deps, so every litellm/proxy change (the main trigger for the API-types check) hit a false 'could not regenerate API types' red. Run the dashboard and API-types blocks before the Python block so gen:api sees an intact env; CI is unaffected because there the lint and check-ui-api-types jobs run in separate environments.

Co-authored-by: Mateo Wang <mateo-berri@users.noreply.github.com>

* fix: make CLAUDE.md more concise

* fix(makefile): give make lint the CI lint env and stop it pruning the venv

make lint diverged from test-linting.yml's lint job in two ways: it never generated the Prisma client (so basedpyright resolved the DB wrappers as Unknown, drifting from CI's counts), and its bare uv sync --frozen pruned the proxy extras (prisma, websockets, ...) out of the venv on every run, which broke the gen:api step that imports litellm.proxy.proxy_server and left a dev unable to run the proxy until re-syncing.

Add a lint-install target that mirrors the job's environment (the proxy-dev group plus prisma generate) and runs before the checks, and make both it and install-dev use uv sync --inexact so they top up the venv instead of tearing packages out. CI is unaffected since it installs its own env per job.

Because make lint no longer prunes, the pre-commit reorder that ran it last (to dodge the prune) is no longer needed, so restore the original block order.

Co-authored-by: Mateo Wang <mateo-berri@users.noreply.github.com>

* fix(makefile): drop lint-install so make lint matches CI's slimmer env

test-linting.yml's lint job installs deps with a bare uv sync --frozen
(default dev group only, no proxy-dev, no prisma generate), but the
lint-install target chained into make lint pulled in --group proxy-dev
and ran prisma generate. Because the basedpyright budget step compares
head and base counts against fixed thresholds, the extra symbols and
Prisma client locally resolved can shift error counts away from CI's,
producing false greens or false reds on the type-check gate.

Remove the lint-install target and its slot in lint. The remaining
sub-targets already chain install-dev, which now uses
uv sync --inexact --frozen, so the venv still isn't pruned but the
installed set stays aligned with what CI sees.

* ci(linting): install proxy-dev and generate prisma in lint job, matching make lint

make lint now installs the proxy-dev group and generates the Prisma client so basedpyright resolves the DB wrappers; the lint job here still installed only the base env, so a local pre-commit could pass while the required CI lint failed (or vice versa). Bring this job in line, which is the same environment litellm_internal_staging's lint job already uses.

Co-authored-by: Mateo Wang <mateo-berri@users.noreply.github.com>

* fix(makefile): keep make lint on the proxy-dev + prisma env to match CI

A concurrent change dropped lint-install to match what looked like CI's slim env, but test-linting.yml's lint job (and the merge ref this PR's CI actually runs) installs --group proxy-dev and generates the Prisma client. With make lint slim and CI fat, basedpyright resolves fewer symbols locally than CI, so a prisma-typed error can stay Unknown locally (green) while CI catches it (red). Restore lint-install so make lint installs the same env CI does; the previous commit also brought this PR's test-linting.yml in line with that env, so the two now match.

Co-authored-by: Mateo Wang <mateo-berri@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Mateo Wang <mateo-berri@users.noreply.github.com>
2026-06-30 09:37:12 -07:00