"""SQLite migration runner — applies db/migrations/*.sql in order.""" from __future__ import annotations import os import sqlite3 from pathlib import Path # G-102 FIX: read PRAXIS_DB_PATH from env (must match db/store.py). _DEFAULT_DB_PATH = Path(os.environ.get("PRAXIS_DB_PATH", "praxis.db")) _DEFAULT_MIGRATIONS_DIR = Path(__file__).resolve().parent / "migrations" def apply_migrations( db_path: Path | str | None = None, migrations_dir: Path | None = None, ) -> list[str]: """Apply all pending migrations in order. Returns the list of applied names. Uses a `_migrations` tracking table so re-running is idempotent. """ db = Path(db_path) if db_path else _DEFAULT_DB_PATH mdir = migrations_dir or _DEFAULT_MIGRATIONS_DIR conn = sqlite3.connect(str(db)) try: conn.execute( "CREATE TABLE IF NOT EXISTS _migrations (id TEXT PRIMARY KEY, applied_at TEXT NOT NULL DEFAULT (datetime('now')))" ) applied: list[str] = [] for sql_path in sorted(mdir.glob("*.sql")): mid = sql_path.stem already = conn.execute( "SELECT 1 FROM _migrations WHERE id = ?", (mid,) ).fetchone() if already: continue sql = sql_path.read_text(encoding="utf-8") conn.executescript(sql) conn.execute("INSERT INTO _migrations (id) VALUES (?)", (mid,)) conn.commit() applied.append(mid) return applied finally: conn.close() __all__ = ["apply_migrations"]