mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-07 08:26:10 +00:00
Coverage is the single biggest time lever on the unit shards: the legacy
no-coverage workflow ran the same directory in about 5 minutes against 11 to 13
with coverage on. coverage.py's sys.monitoring backend (PEP 669) is the cheapest
core it ships, and it is not in use here today.
It has to be asked for explicitly. coverage 7.14 only defaults to sysmon from
Python 3.14 (`SYSMON_DEFAULT = CPYTHON and PYVERSION >= (3, 14)`) and these
shards pin 3.12, so without `COVERAGE_CORE` they get the slow tracer.
The audit left open whether sysmon survives turning on branch coverage. It does
not, at this Python. coverage gates branch measurement under sysmon on
`branch_right_left`, which needs newer than 3.14.0a5; on 3.12 it refuses and
falls back to the default core with a `no-sysmon` warning. Verified directly
against Python 3.12.13 with coverage 7.14.0:
$ COVERAGE_CORE=sysmon python -m coverage run --branch --source=. run.py
CoverageWarning: Can't use core=sysmon: sys.monitoring can't measure
branches in this version, using default core (no-sysmon)
So this speedup and `branch = true` are mutually exclusive until the runners
move to 3.14. Nothing here turns branch coverage on, so the two never collide
in this change, but whoever does turn it on is choosing to give this back.
215 lines
7.4 KiB
YAML
215 lines
7.4 KiB
YAML
name: _Unit Test Base (Reusable)
|
|
|
|
on:
|
|
workflow_call:
|
|
inputs:
|
|
test-path:
|
|
description: "Pytest path(s) to run"
|
|
required: true
|
|
type: string
|
|
workers:
|
|
description: "Number of pytest-xdist workers"
|
|
required: false
|
|
type: number
|
|
default: 2
|
|
reruns:
|
|
description: "Number of reruns for flaky tests"
|
|
required: false
|
|
type: number
|
|
default: 2
|
|
timeout-minutes:
|
|
description: >-
|
|
Timeout for the test step alone. Setup (checkout, dependency install,
|
|
Prisma client generation) gets its own allowance on top, so a slow
|
|
runner or a cold binary download can never cancel passing tests.
|
|
required: false
|
|
type: number
|
|
default: 20
|
|
job-timeout-minutes:
|
|
description: >-
|
|
Backstop for the whole job. Keep it >= `timeout-minutes` plus 35: 30 for
|
|
the per-step ceilings on the setup steps below, and 5 for the runner
|
|
overhead the job clock charges but no step owns (job init, step
|
|
transitions, post-job cleanup). That headroom is what makes the test
|
|
budget a floor rather than a hope, since setup cannot overrun into it
|
|
without failing its own step first. GitHub expressions have no
|
|
arithmetic, so the sum is passed in rather than computed.
|
|
required: false
|
|
type: number
|
|
default: 55
|
|
max-failures:
|
|
description: "Stop after this many failures"
|
|
required: false
|
|
type: number
|
|
default: 10
|
|
dist:
|
|
description: "pytest-xdist distribution mode (loadscope|load|worksteal|loadfile|no)"
|
|
required: false
|
|
type: string
|
|
default: "loadscope"
|
|
artifact-name:
|
|
description: "Unique name for the coverage artifact (must be unique per run)"
|
|
required: true
|
|
type: string
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
jobs:
|
|
run:
|
|
name: Run tests
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: ${{ inputs.job-timeout-minutes }}
|
|
permissions:
|
|
contents: read
|
|
pull-requests: read
|
|
outputs:
|
|
decision: ${{ steps.changes.outputs.decision }}
|
|
|
|
steps:
|
|
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
|
timeout-minutes: 3
|
|
with:
|
|
persist-credentials: false
|
|
|
|
- name: Detect relevant changes
|
|
id: changes
|
|
timeout-minutes: 2
|
|
uses: ./.github/actions/detect-changes
|
|
|
|
- name: Set up Python
|
|
if: steps.changes.outputs.decision != 'skip'
|
|
timeout-minutes: 3
|
|
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
|
with:
|
|
python-version: "3.12"
|
|
|
|
- name: Set up uv
|
|
if: steps.changes.outputs.decision != 'skip'
|
|
timeout-minutes: 3
|
|
uses: ./.github/actions/setup-uv-with-retries
|
|
with:
|
|
version: "0.10.9"
|
|
|
|
- name: Cache uv dependencies
|
|
if: steps.changes.outputs.decision != 'skip'
|
|
timeout-minutes: 5
|
|
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
|
with:
|
|
path: |
|
|
~/.cache/uv
|
|
.venv
|
|
key: ${{ runner.os }}-uv-${{ hashFiles('uv.lock') }}
|
|
restore-keys: |
|
|
${{ runner.os }}-uv-
|
|
|
|
- name: Install dependencies
|
|
if: steps.changes.outputs.decision != 'skip'
|
|
timeout-minutes: 8
|
|
run: |
|
|
.github/scripts/uv_sync_with_retries.sh --frozen --group ci --group proxy-dev --extra google --extra proxy --extra semantic-router --extra saml
|
|
|
|
- name: Cache Prisma binaries
|
|
if: steps.changes.outputs.decision != 'skip'
|
|
timeout-minutes: 3
|
|
uses: ./.github/actions/cache-prisma-binaries
|
|
|
|
- name: Generate Prisma client
|
|
if: steps.changes.outputs.decision != 'skip'
|
|
timeout-minutes: 3
|
|
run: |
|
|
uv run --no-sync prisma generate --schema litellm/proxy/schema.prisma
|
|
|
|
- name: Run tests
|
|
if: steps.changes.outputs.decision != 'skip'
|
|
timeout-minutes: ${{ inputs.timeout-minutes }}
|
|
env:
|
|
TEST_PATH: ${{ inputs.test-path }}
|
|
MAX_FAILURES: ${{ inputs.max-failures }}
|
|
WORKERS: ${{ inputs.workers }}
|
|
RERUNS: ${{ inputs.reruns }}
|
|
DIST: ${{ inputs.dist }}
|
|
# coverage.py's sys.monitoring backend (PEP 669), the cheapest core it has.
|
|
# It is only the default from Python 3.14, and these shards run 3.12, so it
|
|
# has to be asked for. Coverage refuses it when branch measurement is on
|
|
# (`branch_right_left` needs > 3.14.0a5) and falls back to the slow core with
|
|
# a `no-sysmon` warning, so turning on `branch = true` here means giving this
|
|
# back until the runners move to 3.14.
|
|
COVERAGE_CORE: sysmon
|
|
run: |
|
|
if [ "${WORKERS}" = "0" ]; then
|
|
uv run --no-sync pytest ${TEST_PATH:?} \
|
|
--tb=short -vv \
|
|
--maxfail="${MAX_FAILURES}" \
|
|
--reruns "${RERUNS}" \
|
|
--reruns-delay 1 \
|
|
--durations=20 \
|
|
--cov=./litellm \
|
|
--cov-report=xml:coverage.xml \
|
|
--cov-config=pyproject.toml
|
|
else
|
|
uv run --no-sync pytest ${TEST_PATH:?} \
|
|
--tb=short -vv \
|
|
--maxfail="${MAX_FAILURES}" \
|
|
-n "${WORKERS}" \
|
|
--reruns "${RERUNS}" \
|
|
--reruns-delay 1 \
|
|
--dist="${DIST}" \
|
|
--durations=20 \
|
|
--cov=./litellm \
|
|
--cov-report=xml:coverage.xml \
|
|
--cov-config=pyproject.toml
|
|
fi
|
|
|
|
- name: Save coverage report
|
|
if: always() && steps.changes.outputs.decision != 'skip'
|
|
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
|
|
with:
|
|
name: coverage-${{ inputs.artifact-name }}-${{ github.run_id }}-${{ github.run_attempt }}
|
|
path: coverage.xml
|
|
retention-days: 1
|
|
|
|
upload-coverage:
|
|
name: Upload coverage to Codecov
|
|
needs: run
|
|
if: always() && needs.run.outputs.decision != 'skip'
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: read
|
|
id-token: write
|
|
pull-requests: write
|
|
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
|
with:
|
|
persist-credentials: false
|
|
|
|
- name: Download coverage report
|
|
uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1
|
|
with:
|
|
pattern: coverage-${{ inputs.artifact-name }}-${{ github.run_id }}-${{ github.run_attempt }}
|
|
path: coverage-reports
|
|
merge-multiple: true
|
|
|
|
- name: Upload to Codecov
|
|
id: codecov-upload
|
|
continue-on-error: true
|
|
uses: codecov/codecov-action@75cd11691c0faa626561e295848008c8a7dddffe # v5.5.4
|
|
with:
|
|
use_oidc: true
|
|
directory: coverage-reports
|
|
root_dir: ${{ github.workspace }}
|
|
flags: ${{ inputs.artifact-name }}
|
|
fail_ci_if_error: false
|
|
|
|
- name: Upload to Codecov (retry)
|
|
if: steps.codecov-upload.outcome == 'failure'
|
|
continue-on-error: true
|
|
uses: codecov/codecov-action@75cd11691c0faa626561e295848008c8a7dddffe # v5.5.4
|
|
with:
|
|
use_oidc: true
|
|
directory: coverage-reports
|
|
root_dir: ${{ github.workspace }}
|
|
flags: ${{ inputs.artifact-name }}
|
|
fail_ci_if_error: false
|