Skip to content

refactor: extract _require_ai helper to deduplicate API key validation - #13

Merged
leochong merged 16 commits into
masterfrom
refactor/ai-key-validation
Aug 30, 2026
Merged

leochong merged 16 commits into
masterfrom
refactor/ai-key-validation

Conversation

@leochong

Copy link
Copy Markdown

Summary

Extracts 6 duplicated AI key validation blocks into a single _require_ai() helper function in migra/command.py.

Before

Each of the 6 AI modes (--explain-drift, --generate, --advise x2, --explain, --rollback) had its own inline block that:

  • Checked if anthropic package was installed
  • Resolved the API key via resolve_api_key(cli_key=args.api_key)
  • Printed an identical multi-line error message directing the user to --setup-ai or setting ANTHROPIC_API_KEY

After

A single shared helper at command.py:279:

def _require_ai(out, err, cli_key=None):
    """Resolve an Anthropic API key, printing standard help text on failure."""

Impact

  • Net -101 lines (48 insertions, 149 deletions)
  • Single source of truth for AI setup error messages
  • Easier to update messaging without touching 6 call sites
  • No behavioral changes — same error messages, same exit codes

Diff

-149 / +48 lines in migra/command.py

leochong and others added 16 commits June 18, 2026 08:57
…-record-rollback)

Adds a migradiff_history table recorded in target databases via
migra/history.py, plus CLI flags to view history, record generated
migrations, chain migrations across environments with conflict
detection, and close the audit loop on executed rollbacks.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Both files still described v1.5.1 as current and --explain-drift/
multi-language README as future work, even though v1.7.2 is on PyPI
and both features shipped long ago. Updates completed-features,
current version, roadmap, version history, and known limitations
(including that docker-compose.yml is referenced but missing from
the repo) to match the real code and CHANGELOG.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
CLAUDE.md documents build/test/lint commands and the core diff-engine,
CLI, history-tracking, and AI-feature architecture for future agent
sessions. decisions.md and status.md are empty/lightweight tracking
docs to log decisions and branch status going forward.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Add migration state tracking (--status, --history, --promote, --record-rollback)
The GitHub Actions CI workflow only triggered on push/PR to `master`,
but `main` is the repo's actual default branch (confirmed via
`gh repo view --json defaultBranchRef`). PRs targeting `main` — like
the one that just merged the migration state tracking feature — got
no CI run at all. Adds `main` to the trigger branches alongside
`master` and documents the finding in CI_NOTES.md, including that
`main` still lacks the branch protection rule that `master` has
(a separate repo-settings change, not covered here).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Three s.execute() calls passed raw strings directly instead of
wrapping them in sqlalchemy.text(). SQLAlchemy 1.4 (installed locally)
only warns about this; SQLAlchemy 2.x — which CI resolves to since
pyproject.toml pins sqlalchemy to "*" — raises ArgumentError and fails
the test. Verified against SQLAlchemy 2.0.51 in an isolated venv:
these 3 tests plus the other 2 new test files (32 tests total) and
the full 342-test suite all pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…hemy2-text

fix: wrap raw SQL in text() in test_history.py for SQLAlchemy 2.x
Closes the gap where MigraDiff only ever generated SQL and left users
to pipe it to psql themselves, with no reliable way to know whether
migradiff_history reflected a migration that actually ran.

--apply executes the generated migration against dburl_from (the
database being migrated, matching the CLI's own help text and the
README's psql dburl_from < migration.sql convention) inside a single
transaction -- any failing statement rolls back everything, and
nothing is recorded, so migradiff_history only ever reflects migrations
that were confirmed applied. On success it auto-records history against
dburl_from without needing a separate --record-history call.

Rejected up front with --from-file (dburl_from would be an ephemeral
throwaway database) and --promote (the chain's from/to direction isn't
yet reconciled with which database --apply should execute against --
tracked as a follow-up in PROJECT_PLAN.md rather than guessed at here).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
feat: add --apply flag to execute migrations directly
--schema public,reporting was documented as supported but the raw
comma-joined string was passed straight through to schemainspect's
filter_schema(), which compares it for exact equality against each
object's schema name -- "public,reporting" never equals "public" or
"reporting", so the diff was always empty with no error. Single-schema
and no-schema calls were never affected.

Adds parse_schema_arg()/filter_inspector_schemas() in migra/util.py
(the latter reuses schemainspect's own PROPS list rather than a
hardcoded copy, so it can't drift out of sync) and _get_inspector() in
migra/migra.py, wired into all 5 of Migration's inspector-construction
call sites. Single/no-schema calls pass straight through to the
original get_inspector() unchanged; only 2+ comma-separated names take
the new post-filter path.

New tests each include a third, unlisted schema in the fixture data to
prove real filtering rather than "happens to work with one schema",
and exercise both the CLI path and Migration.apply() directly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@leochong
leochong merged commit bf010e4 into master Aug 30, 2026
14 checks passed
@leochong
leochong deleted the refactor/ai-key-validation branch August 30, 2026 20:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant