feat: let any package ship extension migrations - #660
Merged
Conversation
Extension migrations were discovered by mapping the extension_config key onto the hardcoded sqlspec.extensions.<name> module namespace, so a package distributed separately from SQLSpec could not register migrations through any public surface. Doing so logged "Extension <name> not found" and then silently registered nothing. Set migrations_path in an extension_config entry to point at a directory or a '<dotted.module>:<subdir>' specification. Declaring it auto-includes the extension, so it does not also need to appear in include_extensions; exclude_extensions still opts back out. Packages that register at runtime can call config.add_extension_migrations(name, path, settings) instead. Also fixes ext_<name>_<version> parsing for extension names containing underscores. A file such as ext_my_extension_0001_init.sql resolved to the version ext_my_extension, dropping 0001 and recording a malformed version in the tracking table. Three call sites now share parse_extension_stem, which anchors the version to the first all-numeric segment. Failures are now legible and proportionate: a malformed migrations_path raises MigrationError, a missing directory warns, an unimportable module warns naming the module and the consequence, and an extension that ships no migrations directory logs at debug rather than warning - six of the bundled extensions have none by design. module_to_os_path raises ModuleNotFoundError rather than TypeError so callers can catch the real condition, and resolves namespace packages to their search location instead of returning a path named "None". Claude-Session: https://claude.ai/code/session_01CKBtk2CVvc3nNRAx4NJ56K
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #660 +/- ##
==========================================
+ Coverage 77.18% 77.21% +0.03%
==========================================
Files 475 475
Lines 67750 67825 +75
Branches 9308 9322 +14
==========================================
+ Hits 52290 52370 +80
+ Misses 12047 12044 -3
+ Partials 3413 3411 -2
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Extension migrations were found by mapping the
extension_configkey onto the hardcodedsqlspec.extensions.<name>module namespace, so a package distributed separately from SQLSpec could not register migrations at all — it loggedExtension <name> not foundand then silently registered nothing. Any package can now point SQLSpec at its own migrations directory.migrations_pathin anextension_configentry. Declaring it opts the extension in automatically, so it does not also need to be listed ininclude_extensions;exclude_extensionsstill opts it back out.ext_my_extension_0001_init.sqlpreviously resolved to the versionext_my_extension, dropping0001and writing a malformed version to the migration tracking table.migrations_paththat is not a string or path raises instead of being ignored; one that points at a missing directory warns.module_to_os_pathreports the real condition. It raisesModuleNotFoundErrorrather thanTypeError, and resolves namespace packages to their search location instead of returning a path namedNone.How you use it
Point at a directory, or at a module and subdirectory:
The module form is resolved against the installed package, so it stays portable across machines and works in
[tool.sqlspec]configuration. A plain filesystem path is also accepted, resolved relative to the working directory likescript_location.Packages that register at runtime can call the API instead of declaring the key:
Both forms record the extension and opt it into the migration run. Call
add_extension_migrationsbeforeget_migration_commands().Migrations are versioned under an
ext_{name}_prefix that is written to the tracking table, so the extension name must stay stable once migrations have been applied.Known limitation
Extension migrations written as
.sqlfiles still cannot resolve their named query when placed inside an extension's own directory, because the runner and the SQL loader derive the query name differently. The bundled extensions all use Python migrations, so this is unchanged behavior rather than a regression, but third-party packages shipping.sqlmigrations will hit it. Tracked separately.https://claude.ai/code/session_01CKBtk2CVvc3nNRAx4NJ56K