Python - Packaging Alembic Migrations with Setuptools
alembic, python, setuptools
Solution
I am not sure this is the right way but I did it this way:
First, you can add sort of custom options to alembic using the -x option and you can find details explained in this great answer. This allows you to specify the `db_url` at runtime and make it override the value in the `config.ini`.
Then I managed to package alembic and my migrations by moving the `alembic.ini` file and the `alembic` directory from my project root to my top-level python package:
<project root>
├── src
│ └── <top-level package dir>
│ ├── alembic
│ │ ├── env.py
│ │ ├── README
│ │ ├── script.py.mako
│ │ └── versions
│ │ ├── 58c8dcd5fbdc_revision_1.py
│ │ └── ec385b47da23_revision_2.py
│ ├── alembic.ini
│ ├── __init__.py
│ └── <other files and dirs>
└── <other files and dirs>
This allows to use the setuptools `package_data` directive inside my `setup.py`:
setup(
name=<package_name>,
package_dir={'': 'src'},
packages=find_packages(where='src'),
package_data={
'<top-level package dir>': ['alembic.ini', 'alembic/*', 'alembic/**/*'],
},
[...]
)
A this point, the alembic config and revisions are correctly packaged but the `alembic.ini` settings have to be tweaked to reflect the new directory tree. It can be done using the `%(here)s` param which contains the absolute path of the directory containing the `alembic.ini` file:
# A generic, single database configuration.
[alembic]
# path to migration scripts
script_location = %(here)s/alembic
[...]
# version location specification; this defaults
# to alembic/versions. When using multiple version
# directories, initial revisions must be specified with --version-path
# version_locations = %(here)s/bar %(here)s/bat alembic/versions
version_locations = %(here)s/alembic/versions
[...]
Finally, you have to call `alembic` with the `-c` option which allows to provide the path of the config file:
alembic -c <path to alembic.ini> ...
Problem
What is the right way to package Alembic migration files in a Setuptools `setup.py` file? Everything is in my repo root as `alembic/`. This is a Python application, not a library. My desired installation flow is that someone can `pip install` the wheel that is my application. They would then be able to initialize the application database by running something like `<app> alembic upgrade --sqlalchemy.url=<db_url>`. Upgrades would then require a `pip install -U`, after which they can run the Alembic command again. Is this unorthodox? If not, how would I accomplish this? Certainly a `console_scripts` `entry_points`. But beyond that?