[Infra] SQLite migration runner — Rails-style numbered migrations per user DB #43

Closed
opened 2026-03-23 11:10:11 -07:00 by pyr0ball · 1 comment
Owner

Goal

Implement a lightweight, Rails-inspired migration system for staging.db. Each cloud user and self-hosted instance has their own SQLite file; migrations must apply to each DB independently on first use after a deploy.

Design

  • migrations/ directory — numbered .sql files (001_baseline.sql, 002_add_xyz.sql)
  • schema_migrations table in each DB tracks applied versions
  • scripts/db_migrate.pymigrate_db(db_path) runner called at API startup
  • Migration 001 uses CREATE TABLE IF NOT EXISTS everywhere so existing DBs silently skip it
  • New DBs built from scratch by running all migrations in order

Checklist

  • scripts/db_migrate.py with migrate_db(db_path: Path) runner
  • migrations/001_baseline.sql capturing current full schema
  • Wire migrate_db() into dev_api.py lifespan startup
  • Wire migrate_db() into Streamlit app.py startup
  • Convention: each future schema change ships a new numbered .sql file
  • Document the migration convention in docs/contributing.md

Why not Alembic?

Alembic assumes a single shared DB. Here each user has their own file — migrations must be lazy (run on first request) and path-agnostic. A ~50-line custom runner fits better than pulling in SQLAlchemy for this.

## Goal Implement a lightweight, Rails-inspired migration system for `staging.db`. Each cloud user and self-hosted instance has their own SQLite file; migrations must apply to each DB independently on first use after a deploy. ## Design - `migrations/` directory — numbered `.sql` files (`001_baseline.sql`, `002_add_xyz.sql`) - `schema_migrations` table in each DB tracks applied versions - `scripts/db_migrate.py` — `migrate_db(db_path)` runner called at API startup - Migration 001 uses `CREATE TABLE IF NOT EXISTS` everywhere so existing DBs silently skip it - New DBs built from scratch by running all migrations in order ## Checklist - [ ] `scripts/db_migrate.py` with `migrate_db(db_path: Path)` runner - [ ] `migrations/001_baseline.sql` capturing current full schema - [ ] Wire `migrate_db()` into `dev_api.py` lifespan startup - [ ] Wire `migrate_db()` into Streamlit `app.py` startup - [ ] Convention: each future schema change ships a new numbered `.sql` file - [ ] Document the migration convention in `docs/contributing.md` ## Why not Alembic? Alembic assumes a single shared DB. Here each user has their own file — migrations must be lazy (run on first request) and path-agnostic. A ~50-line custom runner fits better than pulling in SQLAlchemy for this.
pyr0ball added this to the Paid Tier GA milestone 2026-04-04 16:33:18 -07:00
Author
Owner

Implemented in 64554db. Rails-style numbered SQL migration runner:

  • migrations/001_baseline.sql — full schema baseline
  • scripts/db_migrate.py — runner with schema_migrations tracking
  • Wired into FastAPI + Streamlit startup paths
  • 6 passing tests
  • Contributing guide updated
Implemented in `64554db`. Rails-style numbered SQL migration runner: - `migrations/001_baseline.sql` — full schema baseline - `scripts/db_migrate.py` — runner with `schema_migrations` tracking - Wired into FastAPI + Streamlit startup paths - 6 passing tests - Contributing guide updated
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: Circuit-Forge/peregrine#43
No description provided.