build #31
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: build | |
| on: | |
| schedule: | |
| - cron: "23 5 * * *" # daily: pick up new upstream stable releases | |
| push: | |
| branches: [main] | |
| paths: | |
| - Dockerfile | |
| - patch/** | |
| - .github/workflows/build.yml | |
| workflow_dispatch: | |
| inputs: | |
| litellm_version: | |
| description: "LiteLLM release tag (e.g. v1.93.0). Empty = latest stable release." | |
| required: false | |
| permissions: | |
| contents: read | |
| packages: write | |
| actions: read # notify job reads previous run conclusions | |
| concurrency: | |
| group: build | |
| cancel-in-progress: false | |
| jobs: | |
| build: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| version: ${{ steps.version.outputs.version }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| # plain image: consumed by litellm-lagoon (Lagoon/docker-compose) | |
| - upstream: litellm | |
| package: litellm-lagoon-base | |
| # -database image: consumed by the litellm helm chart (k0rdent) | |
| - upstream: litellm-database | |
| package: litellm-lagoon-base-database | |
| # The docker build pushes to this job-local registry first. Its result | |
| # still DISTRIBUTES the enterprise code: `rm -rf` in the Dockerfile only | |
| # masks files in the top layer, the inherited upstream layers keep the | |
| # content. The publish step crane-flattens it into a single layer at | |
| # $IMAGE, so only the post-rm filesystem ever leaves the runner. (A | |
| # GHCR staging package won't do: packages created from a public repo's | |
| # workflow are born public.) | |
| services: | |
| registry: | |
| image: registry:2 | |
| ports: | |
| - 5000:5000 | |
| env: | |
| IMAGE: ghcr.io/${{ github.repository_owner }}/${{ matrix.package }} | |
| STAGING: localhost:5000/${{ matrix.package }} | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - name: Resolve LiteLLM version | |
| id: version | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| VERSION='${{ inputs.litellm_version }}' | |
| if [ -z "$VERSION" ]; then | |
| VERSION="$(gh api repos/BerriAI/litellm/releases --jq '[.[] | select(.prerelease | not)][0].tag_name')" | |
| fi | |
| [ -n "$VERSION" ] || { echo "could not resolve a LiteLLM version"; exit 1; } | |
| echo "version=$VERSION" | tee -a "$GITHUB_OUTPUT" | |
| - uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| # On scheduled runs, skip versions we already published. Pushes to | |
| # main (patch/Dockerfile changes) always republish the current version. | |
| - name: Check if already published | |
| id: published | |
| if: github.event_name == 'schedule' | |
| run: | | |
| if docker manifest inspect "$IMAGE:${{ steps.version.outputs.version }}" >/dev/null 2>&1; then | |
| echo "exists=true" | tee -a "$GITHUB_OUTPUT" | |
| fi | |
| # GHCR shows org.opencontainers.image.description as the package | |
| # description; build it from the patch filenames so the package page | |
| # lists what was applied. | |
| - name: Describe patches | |
| id: patches | |
| run: | | |
| PATCHES="$(ls patch/*.patch 2>/dev/null | xargs -rn1 basename | sed 's/\.patch$//' | paste -sd, -)" | |
| echo "description=LiteLLM ${{ steps.version.outputs.version }}, enterprise code stripped, patches: ${PATCHES:-none}" | tee -a "$GITHUB_OUTPUT" | |
| - uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4.2.0 | |
| if: steps.published.outputs.exists != 'true' | |
| - uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0 | |
| if: steps.published.outputs.exists != 'true' | |
| with: | |
| # Let the buildkit container reach the job-local registry service. | |
| driver-opts: network=host | |
| - uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0 | |
| if: steps.published.outputs.exists != 'true' | |
| with: | |
| context: . | |
| push: true | |
| platforms: linux/amd64,linux/arm64 | |
| build-args: | | |
| LITELLM_IMAGE=ghcr.io/berriai/${{ matrix.upstream }} | |
| LITELLM_VERSION=${{ steps.version.outputs.version }} | |
| # No provenance attestations: crane flatten drops them anyway. | |
| provenance: false | |
| tags: | | |
| ${{ env.STAGING }}:${{ steps.version.outputs.version }} | |
| # GHCR reads the description from the image index annotation on | |
| # multi-arch images; the label covers single-manifest consumers. | |
| labels: | | |
| org.opencontainers.image.description=${{ steps.patches.outputs.description }} | |
| org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }} | |
| annotations: | | |
| index:org.opencontainers.image.description=${{ steps.patches.outputs.description }} | |
| index:org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }} | |
| - uses: imjasonh/setup-crane@feee3b6bb0d4c68370f256a4502498c9227e5c6b # v0.7 | |
| if: steps.published.outputs.exists != 'true' | |
| # Squash to a single layer per arch so the upstream layers containing | |
| # the enterprise code are not part of the public image. Flatten | |
| # preserves the image config (env, entrypoint, labels) and the index | |
| # annotations; crane reuses the docker login from above. | |
| - name: Flatten into public image | |
| if: steps.published.outputs.exists != 'true' | |
| run: | | |
| crane flatten "$STAGING:${{ steps.version.outputs.version }}" -t "$IMAGE:${{ steps.version.outputs.version }}" | |
| crane tag "$IMAGE:${{ steps.version.outputs.version }}" latest | |
| # Keep the Dockerfile's ARG default in sync with the version just | |
| # published, purely for readability — CI always overrides it via | |
| # --build-arg. Stable releases only: pre-release builds (manual rc/dev | |
| # dispatches) shouldn't move the default. [skip ci] stops the push from | |
| # retriggering this workflow. | |
| sync-dockerfile: | |
| runs-on: ubuntu-latest | |
| needs: build | |
| permissions: | |
| contents: write | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - name: Sync ARG LITELLM_VERSION with published version | |
| env: | |
| VERSION: ${{ needs.build.outputs.version }} | |
| run: | | |
| case "$VERSION" in *rc*|*dev*|*beta*|*nightly*) echo "pre-release, skipping"; exit 0;; esac | |
| sed -i "s|^ARG LITELLM_VERSION=.*|ARG LITELLM_VERSION=${VERSION}|" Dockerfile | |
| git diff --quiet && exit 0 | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| git commit -am "chore: sync LITELLM_VERSION default to ${VERSION} [skip ci]" | |
| git push | |
| # One Slack message per breakage, not per matrix leg or per scheduled | |
| # retry: skip posting when the previous completed run also failed. | |
| notify: | |
| runs-on: ubuntu-latest | |
| needs: build | |
| if: failure() | |
| steps: | |
| - name: Check previous run conclusion | |
| id: prev | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| PREV="$(gh api "repos/${{ github.repository }}/actions/workflows/build.yml/runs?status=completed&per_page=1" --jq '.workflow_runs[0].conclusion')" | |
| echo "conclusion=$PREV" | tee -a "$GITHUB_OUTPUT" | |
| - name: Post to Slack | |
| if: steps.prev.outputs.conclusion != 'failure' | |
| env: | |
| SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} | |
| SLACK_CHANNEL_ID: ${{ vars.SLACK_CHANNEL_ID }} | |
| TEXT: ":rotating_light: litellm-lagoon-base build failed for LiteLLM ${{ needs.build.outputs.version || 'unknown' }} — the patch may no longer apply. ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" | |
| run: | | |
| curl -fsS -X POST https://slack.com/api/chat.postMessage \ | |
| -H "Authorization: Bearer $SLACK_BOT_TOKEN" \ | |
| -H 'Content-type: application/json; charset=utf-8' \ | |
| --data "$(jq -n --arg c "$SLACK_CHANNEL_ID" --arg t "$TEXT" '{channel:$c,text:$t}')" \ | |
| | jq -e '.ok' |