[Infra] Automated schema.prisma sync and drift detection#24705
[Infra] Automated schema.prisma sync and drift detection#24705yuneng-berri merged 4 commits intomainfrom
Conversation
Sync all 3 schema.prisma copies and add GHA workflows to keep them in sync automatically. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
|
Greptile SummaryThis PR introduces automated Key findings:
Confidence Score: 4/5Safe to merge only after confirming that litellm-proxy-extras 0.4.60 was never deployed to a database that ran prisma migrate deploy; the deleted migration file creates an unrecoverable Prisma history conflict for any such deployment. One P1 remains: the deleted migration litellm-proxy-extras/litellm_proxy_extras/migrations/20260311180521_schema_sync/migration.sql (deleted) — primary concern.
|
| Filename | Overview |
|---|---|
| litellm-proxy-extras/litellm_proxy_extras/migrations/20260311180521_schema_sync/migration.sql | Deleted migration that contained DROP COLUMN statements; deletion without a compensating migration or neutralized placeholder will cause prisma migrate deploy to fail for any existing 0.4.60 deployment. |
| .github/workflows/check-schema-sync.yml | New CI gate that diffs all three schema.prisma copies against root; uses read-only permissions, SHA-pinned checkout, and symlink guard — well-hardened. |
| .github/workflows/sync-schema.yml | New auto-sync workflow that copies root schema on PR; prior threads cover the GITHUB_HEAD_REF injection risk and incomplete protected-branch guard — not re-flagged here. |
| litellm-proxy-extras/litellm_proxy_extras/schema.prisma | Synced to root schema — adds source_url, changes approval_status to nullable with "active" default, adds @@index; covered by existing March-9 forward migrations that use IF NOT EXISTS. |
| schema.prisma | Root schema now matches litellm/proxy/schema.prisma — adds source_url, approval_status (String? default "active"), BYOM lifecycle fields, and @@index([approval_status]) to LiteLLM_MCPServerTable. |
| tests/litellm-proxy-extras/test_litellm_proxy_extras_utils.py | Additive test: new test_no_drop_column_statements enforces no DROP COLUMN in new migrations, with an allowlist for three pre-existing migrations; strengthens coverage without weakening existing tests. |
| ui/litellm-dashboard/src/components/networking.tsx | getTeamPermissionsCall now silently returns empty permissions on API failure instead of calling handleError, hiding failures from the user. |
| litellm-proxy-extras/pyproject.toml | Version bump from 0.4.60 to 0.4.61; both tool.poetry and tool.commitizen updated consistently. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[PR opens / schema.prisma touched] --> B{sync-schema.yml\nfork PR?}
B -- fork --> C[Skip: read-only token]
B -- same-repo --> D{Branch named\nmain/master?}
D -- yes --> E[Skip: protected branch guard]
D -- no --> F[Checkout by head SHA]
F --> G[Symlink guard check]
G -- symlink found --> H[Fail: symlink not allowed]
G -- ok --> I[cp root schema to\nproxy + proxy-extras]
I --> J{Git diff:\nany changes?}
J -- no change --> K[Skip commit]
J -- changed --> L[git checkout -B branch\ngit commit + push]
A2[PR opens / any schema file touched] --> M[check-schema-sync.yml]
M --> N[Symlink guard check]
N -- symlink found --> O[Fail]
N -- ok --> P{diff root vs\nproxy schema}
P -- diverged --> Q[Fail: schemas out of sync]
P -- identical --> R[Pass: all in sync]
Comments Outside Diff (1)
-
litellm-proxy-extras/litellm_proxy_extras/migrations/20260311180521_schema_sync/migration.sqlDeleted migration will break
prisma migrate deployfor existing 0.4.60 deploymentsThe migration
20260311180521_schema_syncis being deleted, but if any deployment already ranprisma migrate deploywithlitellm-proxy-extras==0.4.60, this migration is recorded in the_prisma_migrationstable. Prisma will refuse to run any future migrations with:"The migration
20260311180521_schema_syncis recorded in the database but not found on the file system."Even though
20260309000000_add_mcp_approval_statusand20260309000001_add_mcp_source_urluseADD COLUMN IF NOT EXISTSto re-add the same columns, Prisma will not even attempt those migrations while the history conflict exists — blocking all future schema rollouts for affected deployments.The safer approaches are either:
- Keep the file but neutralize it — replace the body with a no-op comment so Prisma history remains consistent:
-- This migration contained erroneous DROP COLUMN statements and has been -- superseded. It is kept as a no-op to preserve migration history integrity.
- Ship a forward migration — add a new migration that re-adds the dropped columns with
ADD COLUMN IF NOT EXISTSso deployments that applied the bad migration can recover automatically.
If 0.4.60 was never deployed to any database (i.e., the package was never published or never had migrations applied), please document that explicitly so reviewers can confirm it is safe to delete.
- Keep the file but neutralize it — replace the body with a no-op comment so Prisma history remains consistent:
Reviews (4): Last reviewed commit: "[Infra] Mirror litellm_table_patch sourc..." | Re-trigger Greptile
| byok_api_key_help_url String? | ||
| source_url String? | ||
| // BYOM submission lifecycle | ||
| approval_status String? @default("active") |
There was a problem hiding this comment.
Breaking default value change will silently drop existing MCP servers
The approval_status default changed from "approved" to "active", and the type changed from non-nullable String to nullable String?. Any existing LiteLLM_MCPServerTable rows that were created with the old default ("approved") will not be returned by the active-server query in mcp_server_manager.py:
# litellm/proxy/_experimental/mcp_server/mcp_server_manager.py:2425
db_mcp_servers = await get_all_mcp_servers(prisma_client, approval_status="active")After this migration, all previously-active servers (with approval_status = "approved") will be silently absent from the runtime registry, making them unreachable without any error surfaced to the admin. A data migration is needed alongside this schema change, e.g.:
UPDATE "LiteLLM_MCPServerTable"
SET approval_status = 'active'
WHERE approval_status = 'approved';Without this migration, existing deployments that already have MCP servers in the DB will lose them after upgrading.
| git checkout -B "$GITHUB_HEAD_REF" | ||
| git add -- litellm/proxy/schema.prisma litellm-proxy-extras/litellm_proxy_extras/schema.prisma | ||
| git commit -m "chore: sync schema.prisma copies from root" | ||
| git push origin "HEAD:$GITHUB_HEAD_REF" |
There was a problem hiding this comment.
GITHUB_HEAD_REF usage contradicts the checkout-by-SHA rationale
The PR description (and the inline comment at line 33-35) explains that github.head_ref is "an attacker-controlled string (the branch name) and could contain unusual characters that cause unexpected git behavior" — which is why the checkout uses the head SHA. However, the commit step then uses $GITHUB_HEAD_REF directly in shell commands:
git checkout -B "$GITHUB_HEAD_REF"
git push origin "HEAD:$GITHUB_HEAD_REF"Double quotes prevent shell word-splitting injection, but git still receives the raw branch name as the refspec argument for the push. A branch name with characters that git interprets specially (e.g. .., @{, leading -) could cause unexpected git behavior or workflow failure. Consider validating the branch name before use, e.g.:
# validate: only allow safe branch name characters
if [[ ! "$GITHUB_HEAD_REF" =~ ^[a-zA-Z0-9/_.-]+$ ]]; then
echo "::error::Unsafe branch name: $GITHUB_HEAD_REF"
exit 1
fi
git checkout -B "$GITHUB_HEAD_REF"
git push origin "HEAD:$GITHUB_HEAD_REF"This is consistent with the security rationale already articulated for the SHA-based checkout.
| if: >- | ||
| github.event.pull_request.head.repo.full_name == github.repository | ||
| && github.head_ref != 'main' | ||
| && github.head_ref != 'master' |
There was a problem hiding this comment.
Protected-branch guard is incomplete — only covers
main and master
The guard prevents sync-schema.yml from pushing to PRs from branches named exactly main or master:
&& github.head_ref != 'main'
&& github.head_ref != 'master'However, other commonly-protected branch names (e.g. develop, release, staging, or repo-specific patterns like release/v*) are not covered. If those branches are also protected in this repository, a PR opened from them would still trigger the sync workflow and attempt a forced push. The actual last line of defense is GitHub's branch-protection rules, but the workflow comment implies the guard itself is the safeguard. Consider either expanding the list or relying solely on branch protection.
| on: | ||
| pull_request: | ||
| paths: | ||
| - 'schema.prisma' | ||
| - 'litellm/proxy/schema.prisma' | ||
| - 'litellm-proxy-extras/litellm_proxy_extras/schema.prisma' | ||
|
|
There was a problem hiding this comment.
CI gate won't fire if none of the 3 schema files are touched
The check-schema-sync.yml workflow uses a paths filter, so it only runs when at least one of the three schema files is modified. A PR that adds Python code relying on the new source_url column (or any other schema field) without touching any schema file would skip the check entirely.
If the intent is to guarantee all three schemas are always identical on every PR (not just when schemas are changed), consider either:
- Removing the
pathsfilter so the check runs on every PR, or - Documenting explicitly that the gate only applies to schema-touching PRs, and relying on the
sync-schema.ymlauto-commit to keep them in sync.
Add persist-credentials: false to check-schema-sync (read-only, no push needed). Explicitly set persist-credentials: true on sync-schema (required for git push). Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
The schema sync adopted the proxy version which includes source_url, approval_status, and other BYOM fields. These were previously dropped in migration 20260311180521 due to schema drift. This migration restores them to match the now-unified schema. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Cherry-pick source-only changes from litellm_table_patch, excluding build artifacts from the incident response period. - Remove destructive DROP COLUMN migration (20260311180521_schema_sync) - Remove now-unnecessary restore migration (20260327232350) - Bump litellm-proxy-extras 0.4.60 → 0.4.61 - Add regression test to block future DROP COLUMN migrations - Fix double error handling in getTeamPermissionsCall Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Summary
Problem
schema.prismaexists in 3 locations that must stay identical. Engineers manually sync these and frequently forget, causing schema drift. This drift has led to issues when publishinglitellm-proxy-extras— the published package schema doesn't match the proxy schema. The current publish workflow also depends on long-lived PyPI tokens and manual version bumping.This PR is the first step toward automating
litellm-proxy-extrasreleases: ensuring schema consistency so that automated publishes are safe.Changes
schema.prismafiles to matchlitellm/proxy/schema.prisma(the most up-to-date version), addingsource_url, BYOM lifecycle fields, and@@index([approval_status])sync-schema.yml— auto-copies rootschema.prismato the other 2 locations when a PR modifies the root schemacheck-schema-sync.yml— CI gate that fails if any of the 3 schemas diverge, blocking mergelitellm_table_patch(excluding build artifacts — build binaries will be handled by the upcoming automated publish workflow):20260311180521_schema_sync)litellm-proxy-extras0.4.60 → 0.4.61getTeamPermissionsCallSecurity measures
These workflows were audited for supply chain risks. Hardening applied:
GITHUB_TOKEN— no PATs, no PyPI tokens, no secretsactions/checkoutpinned to full 40-char SHA, preventing tag-mutation attackssync-schema.ymlchecks outgithub.event.pull_request.head.shainstead ofgithub.head_refto prevent ref injection via crafted branch namesmain/masterto prevent the sync workflow from pushing to protected branchesgit add -Acheck-schema-sync.ymlhascontents: readonly — zero write permissionssync-schema.ymlusescontents: write(minimum required forgit push), no other permissions grantedlitellm_table_patchchanges were cherry-picked as source only, excluding binariesRecommended repo setting
Enable "Dismiss stale pull request approvals when new commits are pushed" on
mainbranch protection to ensure the auto-sync commit triggers re-review.Testing
schema.prisma— confirmsync-schema.ymlauto-copies to the other 2litellm/proxy/schema.prismawithout updating root — confirmcheck-schema-sync.ymlfailscheck-schema-syncas a required status check onmainType
🚄 Infrastructure
✅ Test