mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-13 23:11:40 +00:00
`prisma generate` runs `npm install prisma@<version>` whenever the
prisma-client-py binary cache directory has no CLI entrypoint, pulling ~85 MB
of query and schema engines over the network. Every workflow pointed
PRISMA_BINARY_CACHE_DIR at `${{ runner.temp }}/prisma-cache`, which GitHub
wipes and recreates per job, so that cache was empty on every job of every
run and the download was never avoidable.
The download is normally a few seconds and occasionally minutes. On one
proxy-db run it took 5m18s on a single shard against 3.8s on its eleven
siblings, which pushed the job past its 15 minute timeout and cancelled a
shard whose tests were at 99% and all passing.
Leave PRISMA_BINARY_CACHE_DIR unset so the binaries land in the
prisma-client-py default, which is already keyed by prisma and engine
version, and restore both that path and the @prisma/engines staging cache
through a shared composite action.
Job timeouts also counted setup against the test budget. `timeout-minutes`
now bounds the pytest step, with a separate allowance for checkout,
dependency install, and client generation, so slow setup shows up as a slow
job instead of a cancelled test run.
check_prisma_binary_cache.py guards all three invariants: no workflow
reintroduces the override, every job that generates the client restores the
cache, and the version the action greps out of uv.lock still resolves.
212 lines
7.5 KiB
YAML
212 lines
7.5 KiB
YAML
name: LiteLLM Linting
|
|
|
|
on:
|
|
pull_request:
|
|
branches:
|
|
- main
|
|
- litellm_internal_staging
|
|
- litellm_oss_staging
|
|
- "litellm_**"
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
concurrency:
|
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
|
|
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
|
|
|
jobs:
|
|
lint:
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 15
|
|
# actions: read lets scripts/type_check_gate.py download the base-counts
|
|
# artifact published by publish-basedpyright-base-counts.yml instead of
|
|
# re-running basedpyright over the merge-base tree.
|
|
permissions:
|
|
contents: read
|
|
actions: read
|
|
|
|
steps:
|
|
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
|
# Check out the PR head, not the default refs/pull/N/merge: the merge ref
|
|
# folds in newer base commits, which the diff-based gates (ruff delta,
|
|
# Any-discipline) would otherwise blame on this branch.
|
|
with:
|
|
ref: ${{ github.event.pull_request.head.sha }}
|
|
fetch-depth: 1
|
|
clean: true
|
|
persist-credentials: false
|
|
|
|
- name: Fetch gate base (merge-base with target branch)
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
BASE_SHA: ${{ github.event.pull_request.base.sha }}
|
|
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
|
run: |
|
|
MERGE_BASE=$(gh api "repos/${{ github.repository }}/compare/${BASE_SHA}...${HEAD_SHA}?per_page=1" --jq '.merge_base_commit.sha')
|
|
test -n "$MERGE_BASE"
|
|
git fetch --no-tags --depth=1 origin "$MERGE_BASE"
|
|
echo "GATE_BASE_SHA=$MERGE_BASE" >> "$GITHUB_ENV"
|
|
|
|
- name: Set up Python
|
|
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
|
with:
|
|
python-version: "3.12"
|
|
|
|
- name: Set up uv
|
|
uses: ./.github/actions/setup-uv-with-retries
|
|
with:
|
|
version: "0.10.9"
|
|
|
|
- name: Clean Python cache
|
|
run: |
|
|
find . -type d -name "__pycache__" -exec rm -rf {} + || true
|
|
find . -name "*.pyc" -delete || true
|
|
|
|
- name: Check uv.lock is up to date
|
|
run: |
|
|
uv lock --check || (echo "❌ uv.lock is out of sync with pyproject.toml. Run 'uv lock' locally and commit the result." && exit 1)
|
|
|
|
- name: Install dependencies
|
|
run: |
|
|
uv sync --frozen --group proxy-dev --group e2e-dev
|
|
|
|
- name: Cache Prisma binaries
|
|
uses: ./.github/actions/cache-prisma-binaries
|
|
|
|
# basedpyright resolves Prisma's generated client (litellm/proxy/schema.prisma)
|
|
# only after `prisma generate` writes prisma/client.py et al. Without this the
|
|
# DB wrappers typed against the generated client would degrade to Unknown.
|
|
- name: Generate Prisma client
|
|
run: |
|
|
uv run --no-sync prisma generate --schema litellm/proxy/schema.prisma
|
|
|
|
- name: Check ruff format
|
|
run: |
|
|
git diff --name-only --diff-filter=ACMR "$GATE_BASE_SHA" HEAD -- 'litellm/**/*.py' | grep -v '^litellm/enterprise/' > "$RUNNER_TEMP/ruff_format_files.txt" || true
|
|
if [ ! -s "$RUNNER_TEMP/ruff_format_files.txt" ]; then
|
|
echo "No changed litellm Python files to check with ruff format."
|
|
exit 0
|
|
fi
|
|
xargs uv run --no-sync ruff format --check --exclude '/enterprise/' < "$RUNNER_TEMP/ruff_format_files.txt"
|
|
|
|
- name: Debug - Check file state
|
|
run: |
|
|
echo "Current branch:"
|
|
git branch --show-current
|
|
echo "Last 3 commits:"
|
|
git log --oneline -3
|
|
echo "File content around line 43:"
|
|
head -50 litellm/litellm_core_utils/custom_logger_registry.py | tail -10
|
|
|
|
- name: Run Ruff linting
|
|
run: |
|
|
cd litellm
|
|
uv run --no-sync ruff check .
|
|
cd ..
|
|
|
|
- name: Check strict-rule budget (delta vs base)
|
|
run: |
|
|
uv run --no-sync python scripts/ruff_strict_gate.py --base "$GATE_BASE_SHA"
|
|
|
|
- name: Check type-discipline budget (mutable collections / casts / type guards / kwargs / unexplained suppressions, delta vs base)
|
|
run: |
|
|
uv run --no-sync python scripts/type_discipline_gate.py --base "$GATE_BASE_SHA"
|
|
|
|
- name: Print OpenAI version
|
|
run: |
|
|
uv run --no-sync python -c "import openai; print(f'OpenAI version: {openai.__version__}')"
|
|
|
|
- name: Check basedpyright budget (delta vs base)
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
run: |
|
|
uv run --no-sync python scripts/type_check_gate.py --base "$GATE_BASE_SHA"
|
|
|
|
- name: Check tests/e2e basedpyright (zero errors)
|
|
run: |
|
|
if git diff --name-only --diff-filter=ACMRD "$GATE_BASE_SHA" HEAD -- 'tests/e2e/**/*.py' | grep -q .; then
|
|
uv run --no-sync basedpyright tests/e2e
|
|
else
|
|
echo "No changed tests/e2e Python files; skipping."
|
|
fi
|
|
|
|
- name: Check for circular imports
|
|
run: |
|
|
cd litellm
|
|
uv run --no-sync python ../tests/documentation_tests/test_circular_imports.py
|
|
cd ..
|
|
|
|
- name: Check import safety
|
|
run: |
|
|
uv run --no-sync python -c "from litellm import *" || (echo '🚨 import failed, this means you introduced unprotected imports! 🚨'; exit 1)
|
|
|
|
# Intentionally NON-GATING. This job turns red when a *-budget.json ceiling is
|
|
# raised (or a rule/budget is dropped) so a loosening is obvious in review, but it
|
|
# must be kept OUT of the branch-protection required-checks list so a justified
|
|
# bump can still be merged by a human who has seen and accepted the red.
|
|
budget-ratchet:
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 5
|
|
permissions:
|
|
contents: read
|
|
|
|
steps:
|
|
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
|
with:
|
|
fetch-depth: 1
|
|
persist-credentials: false
|
|
|
|
- name: Fetch ratchet base
|
|
env:
|
|
BASE_SHA: ${{ github.event.pull_request.base.sha }}
|
|
run: |
|
|
git fetch --no-tags --depth=1 origin "$BASE_SHA"
|
|
|
|
- name: Set up Python
|
|
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
|
with:
|
|
python-version: "3.12"
|
|
|
|
- name: Ratchet check (budgets may only decrease; non-gating)
|
|
env:
|
|
BASE_SHA: ${{ github.event.pull_request.base.sha }}
|
|
run: |
|
|
python scripts/budget_ratchet_check.py --base "$BASE_SHA"
|
|
|
|
secret-scan:
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 5
|
|
permissions:
|
|
contents: read
|
|
|
|
steps:
|
|
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
|
with:
|
|
fetch-depth: 1
|
|
persist-credentials: false
|
|
|
|
- name: Set up Python
|
|
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
|
with:
|
|
python-version: "3.12"
|
|
|
|
- name: Set up uv
|
|
uses: ./.github/actions/setup-uv-with-retries
|
|
with:
|
|
version: "0.10.9"
|
|
|
|
- name: Run secret scan test
|
|
run: |
|
|
uv run --no-project --with 'pytest==9.0.2' pytest tests/litellm/test_no_hardcoded_secrets.py -v
|
|
|
|
- name: Run ggshield secret scan
|
|
env:
|
|
GITGUARDIAN_API_KEY: ${{ secrets.GITGUARDIAN_API_KEY }}
|
|
run: |
|
|
if [ -n "$GITGUARDIAN_API_KEY" ]; then
|
|
git fetch --no-tags --unshallow origin
|
|
uv tool run --from 'ggshield==1.48.0' ggshield secret scan repo .
|
|
else
|
|
echo "GITGUARDIAN_API_KEY not set, skipping ggshield scan"
|
|
fi
|