Flyway-inspired migration starter for alt-python/boot
Project description
alt-python-boot-flyway
Spring Boot-style CDI auto-configuration for Flyway-inspired database
migrations. Registers a ManagedFlyway CDI bean that runs migrate()
synchronously during init(), reading all configuration from boot.flyway.*.
Port of @alt-javascript/boot-flyway.
Part of the alt-python/boot monorepo.
Install
uv add alt-python-boot-flyway pydbc-sqlite
Requires Python 3.12+, alt-python-boot-pydbc, and alt-python-flyway.
Quick Start
# invoke.py
import os
os.chdir(os.path.dirname(os.path.abspath(__file__)))
import pydbc_sqlite # registers the SQLite driver
from boot import Boot
from cdi import Context, Singleton
from boot_pydbc import pydbc_auto_configuration
from boot_flyway import flyway_starter
from services import NoteRepository, Application
Boot.boot({
'contexts': [
Context(pydbc_auto_configuration() + flyway_starter()),
Context([Singleton(NoteRepository), Singleton(Application)]),
]
})
// config/application.json
{
"boot": {
"datasource": {
"url": "pydbc:sqlite::memory:",
"pool": { "enabled": true, "max": 2 }
},
"flyway": {
"locations": "db/migration"
}
}
}
Boot loads config, creates the datasource, runs all pending Flyway migrations
during CDI startup, then starts the application. By the time any repository
bean's init() is called, the schema is fully applied and seeded.
What's Included
| Class / Function | Description |
|---|---|
ManagedFlyway |
CDI bean that runs migrate() synchronously during init() |
flyway_auto_configuration(prefix, datasource_bean) |
Returns a Singleton list for Context() |
flyway_starter(prefix, datasource_bean) |
Alias for flyway_auto_configuration() |
DEFAULT_FLYWAY_PREFIX |
'boot.flyway' |
Configuration
All properties live under boot.flyway by default. Override with prefix= to
use a different key root.
| Property | Type | Default | Description |
|---|---|---|---|
boot.flyway.enabled |
bool | true |
Set to false to skip migration on start |
boot.flyway.locations |
string | db/migration |
Comma-separated migration file paths |
boot.flyway.table |
string | flyway_schema_history |
History table name |
boot.flyway.baseline-on-migrate |
bool | false |
Run baseline() if history is empty before migrating |
boot.flyway.baseline-version |
string | '1' |
Version to record in the baseline entry |
boot.flyway.baseline-description |
string | 'Flyway Baseline' |
Baseline entry description |
boot.flyway.out-of-order |
bool | false |
Allow applying migrations older than the latest applied |
boot.flyway.validate-on-migrate |
bool | true |
Validate checksums before migrating |
boot.flyway.installed-by |
string | 'flyway' |
User recorded in history |
CDI Lifecycle
ManagedFlyway follows the standard CDI lifecycle:
set_application_context(ctx)— CDI injects the application context.init()— reads config, creates aFlywayinstance, callsmigrate(). The schema is fully applied beforeinit()returns.- Downstream beans start after
init()completes — repositories and services can query the database immediately.
Python vs JavaScript difference. The JS port (
@alt-javascript/boot-flyway) stores the migration promise and exposes aready()method because JS CDI does not awaitasync init(). Python CDI is synchronous —migrate()completes insideinit(), so noready()call is needed.
Accessing the Flyway Instance
ManagedFlyway.get_flyway() returns the underlying Flyway instance for
info(), validate(), repair(), and clean():
managed_flyway = ctx.get('managed_flyway')
flyway = managed_flyway.get_flyway()
# Check migration status
for m in flyway.info():
print(m['version'], m['state'], m['description'])
# Validate checksums against files on disk
flyway.validate()
# Remove failed history entries
result = flyway.repair()
print(result['removed_entries'])
Multiple Datasources
To run Flyway migrations against two independent datasources, use
DataSourceBuilder and two flyway_starter() calls with different prefixes and
explicit CDI bean names.
# invoke.py
from boot import Boot
from cdi import Context, Singleton
from boot_pydbc import pydbc_auto_configuration, DataSourceBuilder
from boot_flyway import flyway_starter, ManagedFlyway
# Primary datasource — uses boot.datasource.* and boot.flyway.*
# Produces: data_source, pydbc_template, managed_flyway
notes_beans = pydbc_auto_configuration() + flyway_starter()
# Secondary datasource — uses boot.datasource-tags.* and boot.flyway-tags.*
# DataSourceBuilder produces beans named tags_data_source, tags_pydbc_template, etc.
tags_ds = (
DataSourceBuilder.create()
.prefix('boot.datasource-tags')
.bean_names({
'data_source': 'tags_data_source',
'pydbc_template': 'tags_pydbc_template',
'named_parameter_pydbc_template': 'tags_named_pydbc_template',
'schema_initializer': 'tags_schema_initializer',
})
.without_schema_initializer()
.build()
)
# flyway_starter for the tags DB — must use a unique CDI name
tags_flyway_raw = flyway_starter(
prefix='boot.flyway-tags',
datasource_bean='tags_data_source',
)
# Re-create with name='managed_flyway_tags' so it coexists with managed_flyway
tags_flyway = []
for comp in tags_flyway_raw:
if comp.name == 'managed_flyway':
tags_flyway.append(Singleton({
'reference': comp.reference,
'name': 'managed_flyway_tags',
'depends_on': 'tags_data_source',
'properties': [{'name': 'data_source', 'reference': 'tags_data_source'}],
}))
else:
tags_flyway.append(comp)
Boot.boot({
'contexts': [
Context(notes_beans + tags_ds + tags_flyway),
Context([Singleton(NoteRepository), Singleton(TagRepository), Singleton(Application)]),
]
})
Config:
{
"boot": {
"datasource": {
"url": "pydbc:sqlite::memory:",
"pool": { "enabled": true, "max": 2 }
},
"datasource-tags": {
"url": "pydbc:sqlite::memory:",
"pool": { "enabled": true, "max": 2 }
},
"flyway": {
"locations": "db/notes-migration"
},
"flyway-tags": {
"locations": "db/tags-migration"
}
}
}
See packages/example-5-4-persistence-flyway-multidb
for a complete runnable example.
Running Tests
uv run pytest packages/boot-flyway -v
Troubleshooting
KeyError: 'managed_flyway' when two Flyway runners share a context
Each flyway_starter() call produces a managed_flyway bean. The second one
will clash with the first. Re-create the second starter's component with a
distinct name (managed_flyway_tags, etc.) as shown in the multi-datasource
example above.
Migrations run but the schema is missing when the repository queries it
Check that ManagedFlyway is listed before your repository beans in the CDI
context, or add depends_on='managed_flyway' to the repository component. CDI
initialises beans in dependency order.
boot.flyway.enabled: false does not suppress migration
The enabled key must be a boolean in the config file, not a string. Use
"enabled": false (JSON) or enabled: false (YAML), not "enabled": "false".
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file alt_python_boot_flyway-1.1.1.tar.gz.
File metadata
- Download URL: alt_python_boot_flyway-1.1.1.tar.gz
- Upload date:
- Size: 6.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.2 {"installer":{"name":"uv","version":"0.11.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c6d9f469b1fb714a410f0fec1e76c7fbdee002edb7c97b4703083fd329aa2d86
|
|
| MD5 |
d7ff32377a6aad09bcceefc31ed10917
|
|
| BLAKE2b-256 |
39a7dad5f661c391befffa71fa0d45da48f9ebd9bbe592eecb0822f696aba0f6
|
File details
Details for the file alt_python_boot_flyway-1.1.1-py3-none-any.whl.
File metadata
- Download URL: alt_python_boot_flyway-1.1.1-py3-none-any.whl
- Upload date:
- Size: 6.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.2 {"installer":{"name":"uv","version":"0.11.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
aad5658b9137ab755552acf1a1fa1dc6d3d766168380c64cc6e5036d5d79cacd
|
|
| MD5 |
863d189a3e17fa6c97984b77c2ccc45a
|
|
| BLAKE2b-256 |
3bde9ea6803c10125e6ed5ac2f1cbac0ed69b530a0d6828ca4411de93b0195c9
|