* build: migrate packaging metadata to uv * ci: move automation and local tooling to uv * docker: migrate image builds and runtime setup to uv * docs: update install and deployment guidance for uv * chore: align auxiliary scripts and tests with uv * test: harden test_litellm isolation * fix: keep release and health check images self-contained * build: pin uv tooling and health check deps * test: isolate bedrock image request formatting from suite state * test: cover sandbox executor requirements flow * ci: fix circleci no-op command steps * ci: fix circleci publish workflow parsing * fix: stabilize remaining uv migration CI checks * ci: increase matrix test timeout headroom * fix: restore published docker and license coverage * fix: restore proxy runtime build parity * fix: restore proxy extras parity and venv migrations * ci: persist uv path across circleci steps * fix: keep psycopg binary in default test env * docker: preserve prisma cache across stages * test: run local proxy checks through uv python * build: restore runtime deps moved into ci * build: refresh uv lock after upstream merge * fix: restore module import in test_check_migration after merge The conflict resolution imported only the function but the test body references check_migration as a module throughout. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: revert dependency promotions, remove nodejs-wheel-binaries, fix Docker layer caching - Move google-generativeai, Pillow, tenacity back to ci group (they are lazily imported and bloat the base SDK install needlessly) - Remove nodejs-wheel-binaries from extra_proxy and proxy-dev (redundant in Docker where system Node.js is already installed via apk) - Remove all nodejs-wheel node replacement and venv npm patching blocks from Dockerfiles since the wheel is no longer installed - Add --no-default-groups to CodSpeed benchmark workflow so the benchmark environment matches the old minimal pip install footprint - Apply standard uv two-phase Docker pattern: copy metadata first, install deps (cached layer), then copy source and install project - Replace CircleCI enterprise no-op with proper uv sync command Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: regenerate uv.lock after removing nodejs-wheel-binaries Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ci): use cache/restore instead of cache to prevent cache poisoning The old workflow used actions/cache/restore (read-only). The uv migration changed it to actions/cache (read-write), which zizmor flags as a cache poisoning risk. Restore the safer read-only variant. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ci): disable setup-uv built-in cache to silence cache-poisoning alert The setup-uv action enables caching by default, which zizmor flags as a cache poisoning risk. Disable it since we already use a read-only cache/restore step. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ci): disable setup-uv cache in publish workflow Silences zizmor cache-poisoning alert. Publishing workflow runs infrequently on protected branches so caching adds no real benefit. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(test): remove duplicate verbose_logger mock in test_check_migration The logger was patched twice — first via mocker.patch() then via mocker.patch.object(autospec=True). The second call fails because autospec cannot inspect an already-mocked attribute. Remove the redundant first patch. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ci): free disk space before Docker build in test-server-root-path The Dockerfile.non_root build ran out of disk on the CI runner. Remove Android SDK, .NET, Boost, and GHC toolchains (~12GB) to free space. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
154 lines
5.2 KiB
YAML
154 lines
5.2 KiB
YAML
name: Publish to PyPI
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
|
|
jobs:
|
|
preflight-checks:
|
|
name: Preflight Checks
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 10
|
|
permissions:
|
|
contents: read
|
|
# No environment — read-only checks, no approval needed
|
|
outputs:
|
|
needs_publish: ${{ steps.check-litellm.outputs.needs_publish }}
|
|
version: ${{ steps.check-litellm.outputs.version }}
|
|
|
|
steps:
|
|
- name: Checkout repo
|
|
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
|
|
|
- name: Set up Python
|
|
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
|
with:
|
|
python-version: "3.12"
|
|
|
|
- name: Set up uv
|
|
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7
|
|
with:
|
|
version: "0.10.9"
|
|
enable-cache: false
|
|
|
|
- name: Check litellm version on PyPI
|
|
id: check-litellm
|
|
run: |
|
|
VERSION=$(python - <<'PY'
|
|
import tomllib
|
|
|
|
with open("pyproject.toml", "rb") as f:
|
|
print(tomllib.load(f)["project"]["version"])
|
|
PY
|
|
)
|
|
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
|
echo "Checking if litellm $VERSION exists on PyPI..."
|
|
|
|
HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" "https://pypi.org/pypi/litellm/$VERSION/json")
|
|
if [ "$HTTP_STATUS" = "200" ]; then
|
|
echo "litellm $VERSION already exists on PyPI. Skipping publish."
|
|
echo "needs_publish=false" >> "$GITHUB_OUTPUT"
|
|
else
|
|
echo "litellm $VERSION not found on PyPI. Publish needed."
|
|
echo "needs_publish=true" >> "$GITHUB_OUTPUT"
|
|
fi
|
|
|
|
- name: Sanity check proxy-extras version
|
|
run: |
|
|
# Read pinned version from project optional dependencies
|
|
PYPROJECT_VERSION=$(python3 - <<'PY'
|
|
import sys
|
|
import tomllib
|
|
|
|
with open("pyproject.toml", "rb") as f:
|
|
proxy_requirements = tomllib.load(f)["project"]["optional-dependencies"]["proxy"]
|
|
|
|
version = None
|
|
for requirement in proxy_requirements:
|
|
normalized = requirement.split(";", 1)[0].strip()
|
|
if not normalized.startswith("litellm-proxy-extras"):
|
|
continue
|
|
parts = normalized.split("==", 1)
|
|
if len(parts) == 2 and parts[0].strip() == "litellm-proxy-extras":
|
|
candidate = parts[1].strip()
|
|
if candidate:
|
|
version = candidate
|
|
break
|
|
|
|
if version is None:
|
|
print(
|
|
"::error::Could not find an exact litellm-proxy-extras pin in project.optional-dependencies.proxy",
|
|
file=sys.stderr,
|
|
)
|
|
sys.exit(1)
|
|
|
|
print(version)
|
|
PY
|
|
)
|
|
echo "pyproject.toml pins litellm-proxy-extras version: $PYPROJECT_VERSION"
|
|
|
|
# Check that the pinned version exists on PyPI
|
|
echo "Checking if litellm-proxy-extras $PYPROJECT_VERSION exists on PyPI..."
|
|
HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" "https://pypi.org/pypi/litellm-proxy-extras/$PYPROJECT_VERSION/json")
|
|
if [ "$HTTP_STATUS" != "200" ]; then
|
|
echo "::error::litellm-proxy-extras $PYPROJECT_VERSION is not published on PyPI yet. Publish it before releasing litellm."
|
|
exit 1
|
|
fi
|
|
echo "litellm-proxy-extras $PYPROJECT_VERSION exists on PyPI. Sanity check passed."
|
|
|
|
publish-litellm:
|
|
name: Publish litellm to PyPI
|
|
needs: preflight-checks
|
|
if: needs.preflight-checks.outputs.needs_publish == 'true'
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 10
|
|
permissions:
|
|
id-token: write
|
|
contents: read
|
|
environment: pypi-publish
|
|
|
|
steps:
|
|
- name: Checkout repo
|
|
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
|
|
|
- name: Set up Python
|
|
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
|
with:
|
|
python-version: "3.12"
|
|
|
|
- name: Set up uv
|
|
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7
|
|
with:
|
|
version: "0.10.9"
|
|
enable-cache: false
|
|
|
|
- name: Copy model prices backup
|
|
run: cp model_prices_and_context_window.json litellm/model_prices_and_context_window_backup.json
|
|
|
|
- name: Build package
|
|
run: |
|
|
rm -rf build dist
|
|
uv build
|
|
|
|
- name: Verify build artifacts
|
|
env:
|
|
EXPECTED_VERSION: ${{ needs.preflight-checks.outputs.version }}
|
|
run: |
|
|
echo "Contents of dist/:"
|
|
ls -la dist/
|
|
# Ensure we have both sdist and wheel
|
|
ls dist/*.tar.gz
|
|
ls dist/*.whl
|
|
# Verify built version matches expected
|
|
ls dist/ | grep -q "litellm-${EXPECTED_VERSION}" || {
|
|
echo "::error::Built artifacts do not match expected version $EXPECTED_VERSION"
|
|
ls dist/
|
|
exit 1
|
|
}
|
|
|
|
- name: Validate package metadata
|
|
run: |
|
|
uv tool run --from 'twine==6.2.0' twine check dist/*
|
|
|
|
- name: Publish to PyPI
|
|
uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
|