At my workplace, we use Alembic to handle migrations, but we
frequently have minor issues like forgetting to downgrade to the most
recent common migration before switching git branches, or not noticing
that a branch has migrations to be run when checking it out. This
causes a bit of aggravation.

I've just written a post-checkout git hook which helps with this by
inspecting the Alembic revision history and determining if the current
revision is present, and if so does it have any child revisions.
Unfortunately, I'm not very proud of the code I had to write to do
this.

Here's the relevant bits; the full git hook is at
https://gist.github.com/inklesspen/3289015398d14b740074

class CaptureCurrentContext(EnvironmentContext):
    # This is a sham EnvironmentContext which only captures the current revision
    def __init__(self, cfg, script, **kw):
        super(CaptureCurrentContext, self).__init__(cfg, script, **kw)
        # we use a set because we have multiple DBs configured in env.py,
        # so run_migrations will be called once for each DB.
        self.current_revisions = set()

    def run_migrations(self, **kw):
        self.current_revisions.add(self.get_context().get_current_revision())

with py.path.local(alembic_root).as_cwd():
    # as_cwd is a context manager for the current directory
    # since the alembic script_location seems to be interpreted relative to cwd,
    # rather than the alembic.ini location
    fake_cmd_opts = type('args', (object,), {'x': []})()
    # Would be nice if I could pass None for cmd_opts, but that causes
a traceback.
    cfg = Config(file_="alembic.ini", cmd_opts=fake_cmd_opts)
    script = ScriptDirectory.from_config(cfg)

    sham = CaptureCurrentContext(cfg, script)
    with sham:
        script.run_env()

    # now we check that both DBs are on the same revision using
sham.current_revisions
    # and use script's .get_heads() and .walk_revisions() methods to
get info about the tree

Annoyances:

* have to make a fake cmd_opts

* have to change working directory instead of being able to infer the
script directory from the ini file or an argument

* Have to make a sham EnvironmentContext and actually run the env.py
script because Alembic's env setup relies on module-level code to run
the migration instead of calling a main() function in env.py

* ScriptDirectory.get_revision() raises a util.CommandError -- the
same exception raised by nearly every error case -- instead of
something appropriate to a missing key; also doesn't have a
.has_revision(), so my code has to implement that check with a
try/catch

* In general, I have to get my hands dirty with alembic implementation
details instead of calling cfg.revision_tree() or
cfg.current_revision(). Alembic has commands to get this information,
but they print to stdout instead of returning useful python objects.
IMO it would be better for Alembic to have one layer which produces
the Python objects and then a wrapper layer to print those to stdout.

-- 
You received this message because you are subscribed to the Google Groups 
"sqlalchemy-alembic" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to sqlalchemy-alembic+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to