Skip to main content

Flyway-inspired migration starter for alt-python/boot

Project description

alt-python-boot-flyway

Language Python License: MIT

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:

  1. set_application_context(ctx) — CDI injects the application context.
  2. init() — reads config, creates a Flyway instance, calls migrate(). The schema is fully applied before init() returns.
  3. 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 a ready() method because JS CDI does not await async init(). Python CDI is synchronous — migrate() completes inside init(), so no ready() 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

alt_python_boot_flyway-1.1.1.tar.gz (6.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

alt_python_boot_flyway-1.1.1-py3-none-any.whl (6.2 kB view details)

Uploaded Python 3

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

Hashes for alt_python_boot_flyway-1.1.1.tar.gz
Algorithm Hash digest
SHA256 c6d9f469b1fb714a410f0fec1e76c7fbdee002edb7c97b4703083fd329aa2d86
MD5 d7ff32377a6aad09bcceefc31ed10917
BLAKE2b-256 39a7dad5f661c391befffa71fa0d45da48f9ebd9bbe592eecb0822f696aba0f6

See more details on using hashes here.

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

Hashes for alt_python_boot_flyway-1.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 aad5658b9137ab755552acf1a1fa1dc6d3d766168380c64cc6e5036d5d79cacd
MD5 863d189a3e17fa6c97984b77c2ccc45a
BLAKE2b-256 3bde9ea6803c10125e6ed5ac2f1cbac0ed69b530a0d6828ca4411de93b0195c9

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page