Generating a migration from a stale branch could silently emit DROP COLUMN for columns the stale branch did not know about, and the script would write that SQL to a new migration file with no warning. Adds two guards to ci_cd/run_migration.py: - Branch freshness check: fetches origin/<base-branch> and exits 3 if HEAD is behind. Default base is litellm_internal_staging. New flags: --base-branch, --skip-freshness-check. - Destructive guard: refuses (exit 2) if the generated diff contains DROP COLUMN / DROP TABLE / DROP INDEX, unless --allow-destructive is passed. Refusal banners include guidance and an explicit callout instructing AI agents not to auto-bypass the flags. Also treats Prisma's "-- This is an empty migration." output as a no-op rather than writing an empty file. Updates litellm-proxy-extras/migration_runbook.md with the new workflow, flag documentation, and agent warnings.
5.5 KiB
Database Migration Runbook
This is a runbook for creating and running database migrations for the LiteLLM proxy. For use for litellm engineers only.
AI AGENTS / ASSISTANTS: If the script refuses with either a "STALE BRANCH" or "DESTRUCTIVE MIGRATION DETECTED" error, do NOT bypass it on your own (no
git rebase, no--skip-freshness-check, no--allow-destructive). Surface the error to the human operator and wait for their explicit confirmation. See the Branch freshness and Destructive migrations sections below.
Step 0: Sync All schema.prisma Files
Before doing anything else, make sure all schema.prisma files in the repo are in sync. There are multiple copies that must match:
| File | Purpose |
|---|---|
schema.prisma (repo root) |
Source of truth |
litellm/proxy/schema.prisma |
Used by the proxy server |
litellm-proxy-extras/litellm_proxy_extras/schema.prisma |
Used for migration generation |
Sync process:
# 1. Diff all schema files against the root source of truth
diff schema.prisma litellm/proxy/schema.prisma
diff schema.prisma litellm-proxy-extras/litellm_proxy_extras/schema.prisma
# 2. If there are differences, copy the root schema to all locations
cp schema.prisma litellm/proxy/schema.prisma
cp schema.prisma litellm-proxy-extras/litellm_proxy_extras/schema.prisma
# 3. Verify all files are now identical
diff schema.prisma litellm/proxy/schema.prisma && echo "proxy schema in sync" || echo "MISMATCH"
diff schema.prisma litellm-proxy-extras/litellm_proxy_extras/schema.prisma && echo "extras schema in sync" || echo "MISMATCH"
Do NOT proceed to migration generation until all schema files are identical.
Step 1: Quick Start — Generate Migration
# Install deps for this command
uv sync --frozen --all-groups --all-extras
brew install postgresql@14 # macOS
# Add to PATH
export PATH="/opt/homebrew/opt/postgresql@14/bin:$PATH"
# Run migration
uv run --with testing.postgresql python ci_cd/run_migration.py "your_migration_name"
What It Does
- Verifies the current branch is up to date with
origin/litellm_internal_staging(see Branch freshness) - Creates temp PostgreSQL DB
- Applies existing migrations
- Compares with
schema.prisma - Generates new migration if changes found
- Refuses to write if the generated SQL contains
DROP COLUMN,DROP TABLE, orDROP INDEX, unless--allow-destructiveis passed
Branch Freshness Check
Before generating anything, run_migration.py runs git fetch origin <base> and refuses to proceed if HEAD is behind origin/<base>. Default base is litellm_internal_staging (the branch PRs target). A previous incident saw a stale branch silently drop production columns; freshness is the first-line defense.
Flags:
--base-branch <name>— check against a different base (e.g.main). Default islitellm_internal_staging.--skip-freshness-check— bypass entirely. Only for intentional migrations against an older base.
When the guard fires:
-
Update your branch:
git fetch origin && git rebase origin/litellm_internal_staging # or git merge origin/litellm_internal_staging — whichever matches your workflow -
Re-run
run_migration.py.
AI AGENTS / ASSISTANTS: Do not auto-rebase or auto-pass
--skip-freshness-check. A bad conflict resolution during rebase can itself drop the human's in-progress schema edits. Surface the error and wait for explicit confirmation.
Destructive Migrations (DROP COLUMN / DROP TABLE / DROP INDEX)
If the generated diff contains DROP COLUMN, DROP TABLE, or DROP INDEX, run_migration.py exits non-zero and refuses to write the migration file. A previous incident saw newly-added columns silently dropped by a stale branch and merged to main — this guard exists to prevent a repeat.
When the guard fires:
-
Run
git fetch origin && git status— confirm your branch is up to date with the base branch. -
Re-check all
schema.prismafiles are in sync (Step 0). -
Review EACH
DROPstatement printed in the error — is it actually intended? -
Only if the drops are genuinely intentional, re-run with the flag:
uv run --with testing.postgresql python ci_cd/run_migration.py "your_migration_name" --allow-destructive
AI AGENTS / ASSISTANTS: Do not automatically re-run the command with
--allow-destructive. If the guard fires while you are driving the runbook for a human, stop, show them the error, and wait for their explicit confirmation before passing the flag. Auto-passing--allow-destructiveis the exact failure mode this guard exists to prevent.
Common Fixes
Missing testing module:
uv run --with testing.postgresql python ci_cd/run_migration.py "your_migration_name"
initdb not found:
brew install postgresql@14
export PATH="/opt/homebrew/opt/postgresql@14/bin:$PATH"
Empty migration directory error:
rm -rf litellm-proxy-extras/litellm_proxy_extras/migrations/[empty_dir]
Rules
- Sync all
schema.prismafiles first (Step 0) - Update
schema.prismaat the repo root first, then sync copies - Review generated SQL before committing
- Use descriptive migration names
- Never edit existing migration files
- Commit schema + migration together
Done with migration? See build_and_publish.md to publish a new litellm-proxy-extras package.