litellm/.github/workflows/claude_code_compat_matrix.yml
mateo-berri e87e5c1199 RALPH: compat matrix slice 4 - daily cron VM publishes matrix to docs (#26480, PRD #26476)
Slice 4 of the Claude Code Compatibility Matrix: stand up the daily-cron
pipeline that publishes `compatibility-matrix.json` to the docs repo. After
this slice lands, the hand-authored matrix in the docs repo is replaced by
auto-generated output, and the docs page begins reflecting real test runs
against the latest stable LiteLLM release.

What landed:

- tests/claude_code/resolver.py
  Latest Stable LiteLLM Resolver. Calls the GitHub Releases API and
  returns the newest tag matching `v*-stable`. Sort is numeric on
  (major, minor, patch) so v1.10.0-stable correctly outranks
  v1.9.5-stable. Injectable `http_get` so tests run offline.

- tests/claude_code/publisher.py
  Daily-cron orchestrator. Resolves the latest stable tag, pulls
  `ghcr.io/berriai/litellm:<tag>`, starts it as the proxy, installs
  `@anthropic-ai/claude-code@latest`, runs `pytest tests/claude_code/`,
  invokes the Matrix JSON Builder, and direct-pushes
  `compatibility-matrix.json` to the docs repo's main branch using a
  GitHub App installation token (`DOCS_REPO_TOKEN`). Idempotent: a no-op
  if the JSON is byte-identical to what's already on main.

- tests/claude_code/_publisher_unit_tests/test_resolver.py
  test_publisher.py
  14 unit tests covering the small pure helpers — version sort,
  non-stable filtering, http-get injection, commit message determinism,
  Docker image-name builder, and the file allowlist that enforces the
  "only `compatibility-matrix.json` ever ships" guarantee. Per the PRD's
  "Testing Decisions" section, the publisher's full subprocess
  orchestration intentionally ships without a unit-test harness; the
  daily-cron failure surface is itself the test.

- .github/workflows/claude_code_compat_matrix.yml
  GitHub Actions workflow with three triggers (daily cron at 06:00 UTC,
  `release: published` filtered to `*-stable` tags, and
  `workflow_dispatch`). Mints a docs-repo installation token from a
  GitHub App scoped to `BerriAI/litellm-docs` only with `contents:
  write`, then runs the publisher.

- .gitignore
  Add `compatibility-matrix.json` (cron VM output).

Key decisions:

- "Isolated VM" is realized as a GitHub-hosted ubuntu-latest runner —
  every run gets a fresh ephemeral VM, and the always-latest Claude
  Code CLI is only ever installed inside that ephemeral environment,
  so a malicious or broken Claude Code release cannot affect the
  trusted PR-gate CI in CircleCI.
- File-level restriction on the GitHub App's broad `contents: write`
  scope is enforced by `select_files_to_commit` (script correctness),
  per the PRD's explicit acknowledgement that GitHub does not support
  file-path-scoped tokens.
- `release` runs are filtered to tags ending in `-stable` at the
  workflow level, so a `v1.84.0-rc1` release does not republish the
  matrix.
- Resolver and publisher live under `tests/claude_code/` alongside
  `matrix_builder.py` and `cli_driver.py` — production code that
  supports the test suite, kept colocated with it to match the slice
  1+2 layout.

Out of scope / blockers for next iteration:

- Provisioning the GitHub App itself (creating it under BerriAI's
  org, installing it on litellm-docs only, generating the private key
  and registering `COMPAT_MATRIX_APP_ID` / `COMPAT_MATRIX_APP_PRIVATE_KEY`
  as repo secrets) is an operator/infra step that cannot land via a
  code change in this repo.
- The first successful cron run is what removes the hand-authored
  `compatibility-matrix.json` from the docs repo and replaces it with
  generated output — that happens after this PR merges and the App is
  installed; not a code change here.

Tests: 34 -> 45 passing (added 7 resolver tests + 7 publisher helper
tests, all unit-only and offline). The 12 per-cell failures under
`tests/claude_code/basic_messaging_non_streaming/` remain by design —
they require a running proxy which the cron VM provides.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 05:03:16 +00:00

127 lines
5.1 KiB
YAML

name: Claude Code Compatibility Matrix (daily cron)
# Slice 4 of the Claude Code Compatibility Matrix (PRD #26476, issue #26480).
#
# Three triggers per the PRD's "Daily Cron" section:
# - Daily cron (06:00 UTC) — picks up newly-published Claude Code releases.
# - `release` of a `v*-stable` tag on this repo — re-runs the matrix the
# moment a new stable LiteLLM ships.
# - Manual dispatch — operators can re-run the publisher on demand.
#
# The job runs on a GitHub-hosted ubuntu-latest runner, which gives us a
# fresh VM per run and is "isolated from the main CI environment" in the
# sense that nothing else on this runner survives the run. Since the
# always-latest Claude Code CLI is only installed inside this ephemeral
# VM, a malicious or broken Claude Code release cannot affect the trusted
# build infrastructure used by the PR gate (which lives in CircleCI and
# uses a `latest minus 3 days` Claude Code pin).
#
# Cross-repo authentication (per "Cross-repo authentication" in the PRD):
# A GitHub App installed on `BerriAI/litellm-docs` only, scoped to
# `contents: write`, mints an installation token at job-start. The token
# is only ever used by the publisher, which only ever writes
# `compatibility-matrix.json` (enforced by `select_files_to_commit`).
on:
schedule:
- cron: "0 6 * * *" # daily at 06:00 UTC
release:
types: [published]
workflow_dispatch:
inputs:
skip_publish:
description: "Run the test pipeline but skip the docs-repo push."
required: false
type: boolean
default: false
permissions:
contents: read
jobs:
publish-matrix:
# Skip release runs that aren't tagged `v*-stable`. Plain `v1.84.0-rc1`
# or `v1.84.0` releases must NOT republish the matrix — only the
# latest *stable* tag is reflected on the docs page.
if: |
github.repository == 'BerriAI/litellm' && (
github.event_name != 'release' ||
endsWith(github.event.release.tag_name, '-stable')
)
runs-on: ubuntu-latest
timeout-minutes: 90
steps:
- name: Checkout litellm
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
with:
persist-credentials: false
- 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: Set up Node (for the Claude Code CLI)
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: "20"
- name: Mint docs-repo installation token from GitHub App
id: docs-token
uses: actions/create-github-app-token@d72941d797fd3113feb6b93fd0dec494b13a2547 # v1.12.0
with:
app-id: ${{ secrets.COMPAT_MATRIX_APP_ID }}
private-key: ${{ secrets.COMPAT_MATRIX_APP_PRIVATE_KEY }}
owner: BerriAI
repositories: litellm-docs
- name: Install LiteLLM dev deps
run: uv sync --frozen
- name: Run matrix publisher
env:
# Token used to direct-push compatibility-matrix.json to the docs
# repo's main branch. Comes from the GitHub App installation token
# minted above; scoped to litellm-docs only.
DOCS_REPO_TOKEN: ${{ steps.docs-token.outputs.token }}
# Token used by the resolver to lift the unauthenticated GitHub
# rate limit on the Releases API. The default GITHUB_TOKEN is
# sufficient for read-only access to public release metadata.
GITHUB_TOKEN: ${{ github.token }}
# Real provider credentials needed by the per-cell tests. These
# are the same secrets the LLM-translation workflow uses.
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_REGION_NAME: ${{ secrets.AWS_REGION_NAME }}
VERTEXAI_PROJECT: ${{ secrets.VERTEXAI_PROJECT }}
VERTEXAI_LOCATION: ${{ secrets.VERTEXAI_LOCATION }}
GOOGLE_APPLICATION_CREDENTIALS_JSON: ${{ secrets.GOOGLE_APPLICATION_CREDENTIALS_JSON }}
AZURE_API_KEY: ${{ secrets.AZURE_API_KEY }}
AZURE_API_BASE: ${{ secrets.AZURE_API_BASE }}
SKIP_PUBLISH: ${{ inputs.skip_publish }}
run: |
set -euo pipefail
if [ "${SKIP_PUBLISH:-false}" = "true" ]; then
uv run python -m tests.claude_code.publisher --skip-publish
else
uv run python -m tests.claude_code.publisher
fi
- name: Upload compat-results.json artifact (debugging)
if: always()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: compat-results-${{ github.run_id }}
path: |
compat-results.json
compatibility-matrix.json
if-no-files-found: ignore
retention-days: 30