feat: add migration tracking system (#81) #96
No reviewers
Labels
No Milestone
No project
No Assignees
1 Participants
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: cal/major-domo-database#96
Loading…
Reference in New Issue
Block a user
No description provided.
Delete Branch "issue/81-add-migration-tracking-system"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #81
What
Adds a lightweight migration tracking system to prevent double-application and missed migrations across dev/prod environments.
Changes
migrations/2026-03-27_add_schema_versions_table.sql— createsschema_versions(filename PK, applied_at TIMESTAMP)tablemigrations.py— standalone runner: createsschema_versionsif absent, sorts allmigrations/*.sqlfiles alphabetically, applies any not yet recorded, inserts each filename on success.gitignore— removedmigrations.pyentry (it was listed as a legacy root-level file to ignore; the new script should be tracked)Usage
Reads
POSTGRES_PASSWORD(required),POSTGRES_DB,POSTGRES_USER,POSTGRES_HOST,POSTGRES_PORT— same env vars as the API.First-run behaviour on existing databases
The first run will apply all existing migration files (they weren't previously tracked). This is safe — every existing migration uses
CREATE TABLE IF NOT EXISTS,ALTER TABLE … ADD COLUMN IF NOT EXISTS, orCREATE INDEX IF NOT EXISTS.Other observations
.gitignorehas duplicatedb_engine.pyentries (lines 56, 59, 61) — minor cleanup opportunity, not changed here.AI Code Review
Files Reviewed
.gitignore(modified)migrations.py(added)migrations/2026-03-27_add_schema_versions_table.sql(added)Findings
Correctness
_CREATE_SCHEMA_VERSIONSruns in its ownwith conn:block (commits on exit). TheSELECT filename FROM schema_versionsruns outside awith conn:block, starting an implicit transaction. When the first migration'swith conn:block commits, that implicit transaction is committed along with the migration SQL and INSERT — harmless since the SELECT is read-only.not pending:, the earlyreturnleaves an implicit SELECT transaction open.finally: conn.close()rolls it back silently. Correct behaviour for a read-only transaction.cur.execute(sql)with no parameters uses psycopg2'sPQexecpath, which supports multiple semicolon-separated statements. The existing index migrations (PR #95) with multipleCREATE INDEXstatements will run correctly.appliedsnapshot is safe: The set is computed once before the loop. Since migration SQL files never insert intoschema_versionsthemselves, no re-query is needed between iterations.schema_versionscreated twice on first run: The inline_CREATE_SCHEMA_VERSIONScreates the table, then2026-03-27_add_schema_versions_table.sqlruns the same DDL as a pending migration.CREATE TABLE IF NOT EXISTSmakes the second execution a no-op. Intentional and harmless.add_custom_commands_tables.sql,add_help_commands_table.sql) sort before dated files (2026-…) since'2' < 'a'lexicographically — correct, older migrations run first.Security
%swith a filesystem filename. Migration SQL is operator-controlled (not user input).POSTGRES_PASSWORDis required,RuntimeErrorraised at startup if absent. Same pattern as PR #84.int(os.environ.get("POSTGRES_PORT", "5432"))will raiseValueErroron a malformed env var — caught by the outerexcept Exceptionand exits with code 1. Acceptable for an ops script.Style & Conventions
psycopg2directly (not Peewee ORM) is the right choice for a standalone runner that bootstraps before the ORM is configured.Pathfor file handling, named constants — all clean..gitignoreremoval ofmigrations.pyis correct; the PR body accurately explains the legacy context.Suggestions
SELECTin its ownwith conn:block would make the transaction boundaries more explicit and avoid the implicit-transaction subtlety: Functionally equivalent but clearer intent..gitignoreduplicatedb_engine.pyentries (lines 56, 59, 61) noted in the PR body — a follow-up cleanup issue would be worth filing.Verdict: APPROVED
Clean, well-structured implementation. Transaction model is correct, idempotent on existing databases, fails fast on missing credentials, and stops cleanly on migration failure. The inline table-creation + migration-file approach is a sensible bootstrap pattern.
Automated review by Claude PR Reviewer
Checkout
From your project repository, check out a new branch and test the changes.