Do a dry-run of an Alembic upgrade

A simple trick to allow this is to inject a conditional rollback into the run_migrations_online function in env.py that fires only when some flag is present indicating that we want a dry-run.

In case yours is already modified, recall the default implementation of the run_migrations_online function created by alembic init looks like this:

def run_migrations_online():
    """Run migrations in 'online' mode.

    In this scenario we need to create an Engine
    and associate a connection with the context.

    """
    connectable = engine_from_config(
        config.get_section(config.config_ini_section),
        prefix="sqlalchemy.",
        poolclass=pool.NullPool,
    )

    with connectable.connect() as connection:
        context.configure(
            connection=connection, target_metadata=target_metadata
        )

        with context.begin_transaction():
            context.run_migrations()

Note that:

  • the __enter__ method of context.begin_transaction() - which we're already calling in the default implementation - gives us a transaction object with a rollback() method, and
  • our context object has a get_x_argument method we can use to support passing custom arguments to the alembic command.

Thus, with the following small change (everything below is the same besides the addition of as transaction plus the final three lines) we can have our dry-run functionality:

def run_migrations_online():
    """Run migrations in 'online' mode.

    In this scenario we need to create an Engine
    and associate a connection with the context.

    """
    connectable = engine_from_config(
        config.get_section(config.config_ini_section),
        prefix="sqlalchemy.",
        poolclass=pool.NullPool,
    )

    with connectable.connect() as connection:
        context.configure(
            connection=connection, target_metadata=target_metadata
        )
        with context.begin_transaction() as transaction:
            context.run_migrations()
            if 'dry-run' in context.get_x_argument():
                print('Dry-run succeeded; now rolling back transaction...')
                transaction.rollback()

Now, to do a dry-run, do:

alembic -x dry-run upgrade head

and to do a real run, just do:

alembic upgrade head

like before.