docs(P01): complete minimal-voice-loop phase
---ci--- phase: 1 milestone: v0.1 status: complete requirements: covered: [REQ-VOICE-01, REQ-VOICE-02, REQ-VOICE-03, REQ-VOICE-04, REQ-SCEN-01, REQ-STATE-01, REQ-LLM-01, REQ-LLM-02, REQ-DEBRIEF-01, REQ-ORCH-01, REQ-ORCH-02, REQ-SCEN-FMT-01, REQ-NFR-LAT-01, REQ-NFR-SAFE-01, REQ-NFR-COST-01] partial: [] ---/ci---
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
"""SQLite migration runner — applies db/migrations/*.sql in order."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import sqlite3
|
||||
from pathlib import Path
|
||||
|
||||
_DEFAULT_DB_PATH = 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"]
|
||||
Reference in New Issue
Block a user