Skip to content

build

build #31

Workflow file for this run

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'