Migrate group_run.status to allow 'dry_run'
schema.sql has carried 'dry_run' in the group_run.status CHECK for a while, but CREATE TABLE IF NOT EXISTS never re-applies a constraint to a database that already has the table. Any DB created before that value was added kept the old four-value CHECK, so run_group's `final = "dry_run"` (runner.py:230) hit an IntegrityError in finish_group_run on every group dry run. The per-module work was unaffected -- run_log already permits 'dry_run', so each member recorded its resolved source and merge SQL correctly. Only the group-level bookkeeping failed, leaving the group_run row stuck at 'running'. Via the web UI the failure was invisible: _run_group_in_background swallows the exception and writes status='error', mislabeling a successful dry run. SQLite has no ALTER for CHECK constraints, so this rebuilds the table, the same treatment run_log received earlier. Idempotent on the constraint text, so it runs once. Verified on the live DB: 10,209 rows preserved, no foreign key violations, sqlite_sequence intact. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
e4c17bfc98
commit
ae959aedad
@ -48,6 +48,31 @@ def _apply_migrations(conn: sqlite3.Connection) -> None:
|
|||||||
if "last_fired_at" not in sc_cols:
|
if "last_fired_at" not in sc_cols:
|
||||||
conn.execute("ALTER TABLE schedule ADD COLUMN last_fired_at TEXT")
|
conn.execute("ALTER TABLE schedule ADD COLUMN last_fired_at TEXT")
|
||||||
|
|
||||||
|
# group_run.status predates 'dry_run'. schema.sql carries the correct CHECK, but
|
||||||
|
# CREATE TABLE IF NOT EXISTS never re-applies it to an existing DB, so a group
|
||||||
|
# dry run raised IntegrityError in finish_group_run. CHECK constraints need a
|
||||||
|
# table rebuild (SQLite has no ALTER for them); run_log got the same treatment
|
||||||
|
# earlier. Idempotent: keyed on the constraint text itself.
|
||||||
|
gr_ddl = conn.execute(
|
||||||
|
"SELECT sql FROM sqlite_master WHERE type='table' AND name='group_run'"
|
||||||
|
).fetchone()
|
||||||
|
if gr_ddl and "dry_run" not in gr_ddl[0]:
|
||||||
|
conn.executescript("""
|
||||||
|
CREATE TABLE group_run_new (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
group_id INTEGER NOT NULL REFERENCES grp(id),
|
||||||
|
started_at TEXT DEFAULT (datetime('now')),
|
||||||
|
finished_at TEXT,
|
||||||
|
status TEXT NOT NULL DEFAULT 'running'
|
||||||
|
CHECK (status IN ('running','success','error','cancelled','dry_run')),
|
||||||
|
triggered_by TEXT
|
||||||
|
);
|
||||||
|
INSERT INTO group_run_new (id, group_id, started_at, finished_at, status, triggered_by)
|
||||||
|
SELECT id, group_id, started_at, finished_at, status, triggered_by FROM group_run;
|
||||||
|
DROP TABLE group_run;
|
||||||
|
ALTER TABLE group_run_new RENAME TO group_run;
|
||||||
|
""")
|
||||||
|
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def connect(db_path: Path | None = None):
|
def connect(db_path: Path | None = None):
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user