mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-13 23:11:40 +00:00
* fix(docker): bake non_root prisma engines at /opt/prisma so migrations run offline for any uid The non_root image baked the prisma CLI and engines under /app/.cache and used the CLI's default (library) engine mode. Prisma stopped baking the library engine, so `prisma migrate deploy` fell back to downloading it at startup, which needs network egress and a writable cache. Under an arbitrary non-root uid (OpenShift restricted-v2), an air-gapped network, or a readOnlyRootFilesystem, that download fails and the proxy starts on an empty schema while every DB endpoint returns 500. The migration entrypoint exits 0 on that failure, so a default-uid `docker run` with network never surfaced it Bake to /opt/prisma, a fixed world-readable path no cache mount shadows, and pin PRISMA_CLI_PATH plus PRISMA_CLI_QUERY_ENGINE_TYPE=binary so the baked binary engine is used directly, matching Dockerfile and Dockerfile.database. A build-time guard asserts the binary query engine is present, so a future prisma change that stops baking it fails the image build instead of silently degrading migrations Adds docker/test_offline_migration.sh, run from image-scan, which migrates a fresh Postgres with no egress as a non-root uid and asserts the schema was created, the case a default-uid `docker run` with network cannot catch * test(docker): move the offline migration check into a gated pytest and stop pinning XDG_CACHE_HOME at the read-only bake The offline migration check lived in docker/ as a shell script. It now lives in tests/proxy_migration_tests/ as a pytest gated on LITELLM_IMAGE, matching the sibling schema-migration test gated on DATABASE_URL, and image-scan invokes it with pytest instead of bash. It also asserts the migration entrypoint's exit code alongside the table count, so a crash or a container-startup failure fails loudly rather than only surfacing as a low table count Runtime XDG_CACHE_HOME pointed at /opt/prisma/.cache, which is baked a+rX with no write, so any XDG-aware library writing a cache at runtime would be denied for every uid. Leave it unset so it falls back to $HOME/.cache (/app/.cache, created here and owned by the runtime uid), matching Dockerfile and Dockerfile.database which never pin XDG at runtime. A second test guards against a future edit pointing a cache or home var back at the read-only bake
85 lines
3.3 KiB
YAML
85 lines
3.3 KiB
YAML
name: Image Scan
|
|
|
|
on:
|
|
pull_request:
|
|
branches:
|
|
- main
|
|
- litellm_internal_staging
|
|
- litellm_oss_branch
|
|
- "litellm_**"
|
|
paths:
|
|
- docker/Dockerfile.non_root
|
|
- tests/proxy_migration_tests/test_offline_image_migration.py
|
|
- uv.lock
|
|
- ui/litellm-dashboard/package-lock.json
|
|
- .github/workflows/image-scan.yml
|
|
schedule:
|
|
- cron: "41 6 * * *"
|
|
workflow_dispatch:
|
|
|
|
permissions: {}
|
|
|
|
concurrency:
|
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
jobs:
|
|
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 }} .
|
|
|
|
# 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 }}
|
|
run: |
|
|
python -m pip install "pytest==9.0.3"
|
|
python -m pytest tests/proxy_migration_tests/test_offline_image_migration.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 }} \
|
|
--only-fixed \
|
|
--fail-on high \
|
|
--output table
|