mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-12 03:48:56 +00:00
* feat(lens): isolate trace storage and investigation in a Rust service
* fix(lens): include Rust sources in the image build context
* feat(lens): wire service setup, scoped delivery receipts and lease attempts
* fix(lens): complete service routing and reject stale investigation results
* fix(lens): retry key propagation and validate isolated Compose setup
* fix(lens): seed through isolated ingestion and preserve upstream queue fixes
* chore: sync schema.prisma copies from root
* fix(lens): bind nullable due timestamps as text for Prisma
* chore(ui): remove stale lint suppressions
* fix(lens): address CI failures and review findings
* refactor(lens): remove retired Python worker and run evaluations in Rust
* fix(lens): reuse control connections and satisfy review checks
* test(lens): install and upgrade both Helm charts on Kubernetes
* test(lens): run connection reuse coverage as an integration test
* fix(ui): upgrade Next.js to 16.3.8 security release
* fix(lens): fence stale attempts and preserve reviewed evidence
* Revert "fix(ui): upgrade Next.js to 16.3.8 security release"
This reverts commit 2f79a51b25.
* fix(lens): stop failed investigations and stream history excerpts
* test(lens): cover model tool and result contracts
* test(lens): fix retired routes and reuse installation build artifacts
* test(lens): use portable grep in Helm installation smoke
* test(lens): wait for migrations before forwarding Helm services
* fix(lens): keep failed evidence reads retryable
* fix(lens): preserve sandbox output during process exit
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
333 lines
13 KiB
YAML
333 lines
13 KiB
YAML
name: Image Scan
|
|
|
|
on:
|
|
pull_request:
|
|
branches:
|
|
- main
|
|
- litellm_oss_branch
|
|
- "litellm_**"
|
|
paths:
|
|
- Dockerfile
|
|
- docker/Dockerfile.non_root
|
|
- docker/Dockerfile.database
|
|
- migrations/Dockerfile
|
|
- migrations/run.py
|
|
- gateway/Dockerfile
|
|
- gateway/main.py
|
|
- gateway/routes/allowlist.py
|
|
- backend/Dockerfile
|
|
- backend/main.py
|
|
- deploy/lens/**
|
|
- litellm-rust/**
|
|
- litellm/proxy/lens/**
|
|
- tests/e2e/migrations/lens_compose_smoke.sh
|
|
- docker/component_entrypoint.sh
|
|
- docker/entrypoint.sh
|
|
- litellm/proxy/prisma_migration.py
|
|
- litellm/proxy/admin_mcp.py
|
|
- litellm/proxy/proxy_server.py
|
|
- backend/routes/allowlist.py
|
|
- pyproject.toml
|
|
- litellm-proxy-extras/**
|
|
- tests/proxy_migration_tests/**
|
|
- uv.lock
|
|
- ui/litellm-dashboard/package-lock.json
|
|
- ui/Dockerfile
|
|
- ui/nginx.conf
|
|
- .github/workflows/image-scan.yml
|
|
- .grype.yaml
|
|
schedule:
|
|
- cron: "41 6 * * *"
|
|
workflow_dispatch:
|
|
|
|
permissions: {}
|
|
|
|
concurrency:
|
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
jobs:
|
|
lens-worker-image:
|
|
name: lens-worker-image (${{ matrix.arch }})
|
|
runs-on: ${{ matrix.runner }}
|
|
if: >-
|
|
github.event_name != 'pull_request' ||
|
|
github.event.pull_request.head.repo.full_name == github.repository
|
|
timeout-minutes: 45
|
|
permissions:
|
|
contents: read
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- arch: amd64
|
|
runner: ubuntu-latest
|
|
grype_sha256: edda0968d8827daab01d32b3cd7de192ae0915005e7bbfcfef9e68e79bc43343
|
|
- arch: arm64
|
|
runner: ubuntu-24.04-arm
|
|
grype_sha256: 553e4c36d9d61349830ba6034d43b8700a7f10576d3e2f4981c0fd2b96086465
|
|
steps:
|
|
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
|
with:
|
|
persist-credentials: false
|
|
- name: Build the release worker
|
|
env:
|
|
RELEASE_TAG: sha-${{ github.sha }}
|
|
run: docker build --build-arg LITELLM_RELEASE_TAG="${RELEASE_TAG}" -f deploy/lens/Dockerfile -t lens-worker-scan .
|
|
- name: Verify the standalone worker on a read-only filesystem
|
|
env:
|
|
RELEASE_TAG: sha-${{ github.sha }}
|
|
run: |
|
|
docker run --rm --network none --read-only --cap-drop ALL \
|
|
--tmpfs /tmp:rw,noexec,nosuid,size=1g --security-opt no-new-privileges \
|
|
lens-worker-scan --version | grep -F "litellm-lens $RELEASE_TAG protocol="
|
|
- name: Download Grype v0.114.0
|
|
env:
|
|
ARCH: ${{ matrix.arch }}
|
|
GRYPE_SHA256: ${{ matrix.grype_sha256 }}
|
|
run: |
|
|
curl -fsSL --retry 3 -o "$RUNNER_TEMP/grype.tar.gz" \
|
|
"https://github.com/anchore/grype/releases/download/v0.114.0/grype_0.114.0_linux_${ARCH}.tar.gz"
|
|
echo "${GRYPE_SHA256} $RUNNER_TEMP/grype.tar.gz" | sha256sum -c -
|
|
tar xzf "$RUNNER_TEMP/grype.tar.gz" -C "$RUNNER_TEMP" grype
|
|
chmod +x "$RUNNER_TEMP/grype"
|
|
- name: Scan the worker for fixable HIGH/CRITICAL CVEs
|
|
env:
|
|
GRYPE_MATCH_PYTHON_USING_CPES: "true"
|
|
run: |
|
|
"$RUNNER_TEMP/grype" lens-worker-scan \
|
|
--config .grype.yaml --only-fixed --fail-on high --output table
|
|
|
|
image-scan:
|
|
name: image-scan
|
|
runs-on: ubuntu-latest
|
|
if: >-
|
|
github.event_name != 'pull_request' ||
|
|
github.event.pull_request.head.repo.full_name == github.repository
|
|
timeout-minutes: 30
|
|
permissions:
|
|
contents: read
|
|
steps:
|
|
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
|
with:
|
|
persist-credentials: false
|
|
|
|
- name: Download Grype v0.114.0
|
|
run: |
|
|
curl -fsSL --retry 3 -o "$RUNNER_TEMP/grype.tar.gz" \
|
|
https://github.com/anchore/grype/releases/download/v0.114.0/grype_0.114.0_linux_amd64.tar.gz
|
|
echo "edda0968d8827daab01d32b3cd7de192ae0915005e7bbfcfef9e68e79bc43343 $RUNNER_TEMP/grype.tar.gz" | sha256sum -c -
|
|
tar xzf "$RUNNER_TEMP/grype.tar.gz" -C "$RUNNER_TEMP" grype
|
|
chmod +x "$RUNNER_TEMP/grype"
|
|
|
|
# Dockerfile.non_root is the rootless variant we ship. The other
|
|
# Dockerfiles share the same wolfi base and apk set, so OS-layer coverage
|
|
# is the same; matrix-scan if those variants ever diverge.
|
|
- name: Build runtime image
|
|
run: docker build -f docker/Dockerfile.non_root -t litellm-image-scan:${{ github.sha }} .
|
|
|
|
- name: Tag the cached builder for Admin MCP schema setup
|
|
run: docker build --target builder -f docker/Dockerfile.non_root -t litellm-admin-mcp-schema:${{ github.sha }} .
|
|
|
|
# The prisma bake must migrate a fresh DB with no egress as an arbitrary
|
|
# non-root uid (OpenShift restricted-v2 / air-gapped / readOnlyRootFilesystem).
|
|
# `docker run` as the default uid with network hides a broken bake because
|
|
# the migration entrypoint exits 0 even when it applied nothing; asserting
|
|
# the schema was created is what catches it.
|
|
- name: Set up Python
|
|
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
|
with:
|
|
python-version: "3.12"
|
|
|
|
- name: Verify offline migration as a non-root uid
|
|
env:
|
|
LITELLM_IMAGE: litellm-image-scan:${{ github.sha }}
|
|
LITELLM_ADMIN_MCP_SCHEMA_IMAGE: litellm-admin-mcp-schema:${{ github.sha }}
|
|
run: |
|
|
python -m pip install "pytest==9.0.3"
|
|
python -m pytest tests/proxy_migration_tests/test_offline_image_migration.py tests/proxy_migration_tests/test_image_bedrock_realtime_extra.py tests/proxy_migration_tests/test_image_admin_mcp.py -v
|
|
|
|
# Scans the whole shipped artifact: OS/apk plus every language package
|
|
# baked into the image, including ones no lockfile declares (e.g. prisma's
|
|
# vendored node engine) that osv-scan cannot see. osv-scan stays the fast
|
|
# source-level gate; this is the customer's-eye-view backstop. Credential-
|
|
# free OSS, run as a pinned, checksum-verified binary; no GitHub Action
|
|
# dependency and no vendor SaaS callout.
|
|
- name: Scan image for fixable HIGH/CRITICAL CVEs
|
|
env:
|
|
GRYPE_MATCH_PYTHON_USING_CPES: "true"
|
|
run: |
|
|
"$RUNNER_TEMP/grype" litellm-image-scan:${{ github.sha }} \
|
|
--config .grype.yaml \
|
|
--only-fixed \
|
|
--fail-on high \
|
|
--output table
|
|
|
|
runtime-image:
|
|
name: runtime-image (${{ matrix.dockerfile }})
|
|
runs-on: ubuntu-latest
|
|
if: >-
|
|
github.event_name != 'pull_request' ||
|
|
github.event.pull_request.head.repo.full_name == github.repository
|
|
timeout-minutes: 45
|
|
permissions:
|
|
contents: read
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
dockerfile: [Dockerfile, docker/Dockerfile.database]
|
|
steps:
|
|
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
|
with:
|
|
persist-credentials: false
|
|
|
|
- name: Build runtime image
|
|
run: docker build --build-arg LITELLM_RELEASE_TAG=v0.0.0-lens-ci -f "${{ matrix.dockerfile }}" -t litellm-runtime-scan:${{ github.sha }} .
|
|
|
|
- name: Tag the cached builder for Admin MCP schema setup
|
|
run: docker build --target builder -f "${{ matrix.dockerfile }}" -t litellm-admin-mcp-schema:${{ github.sha }} .
|
|
|
|
- name: Set up Python
|
|
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
|
with:
|
|
python-version: "3.12"
|
|
|
|
- name: Verify offline migration as a non-root uid
|
|
env:
|
|
LITELLM_IMAGE: litellm-runtime-scan:${{ github.sha }}
|
|
LITELLM_ADMIN_MCP_SCHEMA_IMAGE: litellm-admin-mcp-schema:${{ github.sha }}
|
|
run: |
|
|
python -m pip install "pytest==9.0.3"
|
|
python -m pytest tests/proxy_migration_tests/test_offline_image_migration.py tests/proxy_migration_tests/test_image_bedrock_realtime_extra.py tests/proxy_migration_tests/test_image_admin_mcp.py -v
|
|
|
|
- name: Verify the bundled Lens Compose installation and restart
|
|
if: matrix.dockerfile == 'Dockerfile'
|
|
env:
|
|
LITELLM_IMAGE: litellm-runtime-scan:${{ github.sha }}
|
|
run: bash tests/e2e/migrations/lens_compose_smoke.sh
|
|
|
|
migrations-image:
|
|
name: migrations-image
|
|
runs-on: ubuntu-latest
|
|
if: >-
|
|
github.event_name != 'pull_request' ||
|
|
github.event.pull_request.head.repo.full_name == github.repository
|
|
timeout-minutes: 30
|
|
permissions:
|
|
contents: read
|
|
steps:
|
|
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
|
with:
|
|
persist-credentials: false
|
|
|
|
- name: Build migrations image
|
|
run: docker build -f migrations/Dockerfile -t litellm-migrations-scan:${{ github.sha }} .
|
|
|
|
- name: Set up Python
|
|
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
|
with:
|
|
python-version: "3.12"
|
|
|
|
- name: Verify offline migration as a non-root uid
|
|
env:
|
|
LITELLM_IMAGE: litellm-migrations-scan:${{ github.sha }}
|
|
LITELLM_MIGRATION_INTERPRETER: python3
|
|
LITELLM_MIGRATION_SCRIPT: /app/run.py
|
|
run: |
|
|
python -m pip install "pytest==9.0.3"
|
|
python -m pytest tests/proxy_migration_tests/test_offline_image_migration.py -v
|
|
|
|
gateway-image:
|
|
name: gateway-image
|
|
runs-on: ubuntu-latest
|
|
if: >-
|
|
github.event_name != 'pull_request' ||
|
|
github.event.pull_request.head.repo.full_name == github.repository
|
|
timeout-minutes: 30
|
|
permissions:
|
|
contents: read
|
|
steps:
|
|
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
|
with:
|
|
persist-credentials: false
|
|
|
|
- name: Build gateway image
|
|
run: docker build -f gateway/Dockerfile -t litellm-gateway-scan:${{ github.sha }} .
|
|
|
|
- name: Set up Python
|
|
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
|
with:
|
|
python-version: "3.12"
|
|
|
|
- name: Verify the gateway serves offline as a non-root uid
|
|
env:
|
|
LITELLM_IMAGE: litellm-gateway-scan:${{ github.sha }}
|
|
LITELLM_COMPONENT_PORT: "4000"
|
|
LITELLM_IMAGE_COMPONENT: gateway
|
|
run: |
|
|
python -m pip install "pytest==9.0.3"
|
|
python -m pytest tests/proxy_migration_tests/test_component_image_serves_offline.py tests/proxy_migration_tests/test_image_bedrock_realtime_extra.py tests/proxy_migration_tests/test_image_admin_mcp.py -v
|
|
|
|
ui-image:
|
|
name: ui-image
|
|
runs-on: ubuntu-latest
|
|
if: >-
|
|
github.event_name != 'pull_request' ||
|
|
github.event.pull_request.head.repo.full_name == github.repository
|
|
timeout-minutes: 30
|
|
permissions:
|
|
contents: read
|
|
steps:
|
|
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
|
with:
|
|
persist-credentials: false
|
|
|
|
- name: Build UI image
|
|
run: docker build -f ui/Dockerfile -t litellm-ui-scan:${{ github.sha }} .
|
|
|
|
- name: Set up Python
|
|
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
|
with:
|
|
python-version: "3.12"
|
|
|
|
- name: Verify the UI serves offline as an arbitrary uid with a read-only root fs
|
|
env:
|
|
LITELLM_IMAGE: litellm-ui-scan:${{ github.sha }}
|
|
run: |
|
|
python -m pip install "pytest==9.0.3"
|
|
python -m pytest tests/proxy_migration_tests/test_ui_image_serves_offline.py -v
|
|
|
|
backend-image:
|
|
name: backend-image
|
|
runs-on: ubuntu-latest
|
|
if: >-
|
|
github.event_name != 'pull_request' ||
|
|
github.event.pull_request.head.repo.full_name == github.repository
|
|
timeout-minutes: 30
|
|
permissions:
|
|
contents: read
|
|
steps:
|
|
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
|
with:
|
|
persist-credentials: false
|
|
|
|
- name: Build backend image
|
|
run: docker build -f backend/Dockerfile -t litellm-backend-scan:${{ github.sha }} .
|
|
|
|
- name: Tag the cached builder for Admin MCP schema setup
|
|
run: docker build --target builder -f backend/Dockerfile -t litellm-admin-mcp-schema:${{ github.sha }} .
|
|
|
|
- name: Set up Python
|
|
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
|
with:
|
|
python-version: "3.12"
|
|
|
|
- name: Verify the backend serves offline as a non-root uid
|
|
env:
|
|
LITELLM_IMAGE: litellm-backend-scan:${{ github.sha }}
|
|
LITELLM_ADMIN_MCP_SCHEMA_IMAGE: litellm-admin-mcp-schema:${{ github.sha }}
|
|
LITELLM_COMPONENT_PORT: "4001"
|
|
LITELLM_IMAGE_COMPONENT: backend
|
|
run: |
|
|
python -m pip install "pytest==9.0.3"
|
|
python -m pytest tests/proxy_migration_tests/test_component_image_serves_offline.py tests/proxy_migration_tests/test_image_admin_mcp.py -v
|