Previous | Up | Next

Minimalistic migrations in Elixir releases

Ensure your schemas are always in place, in under ten lines of code

If you’re deploying your Elixir project as an OTP release (as opposed to, say, pulling your entire repo and running iex -S mix in production, which you shouldn’t do), you’ve probably run across the problem of ensuring your Ecto migrations run before your application starts.

There are several blog and forum posts out there talking about running migrations as part of the Elixir release startup process. I’m probably not going to add extra value if you already have your workflow figured out, but if you’re just getting started and happen to be using the new release configuration functionality in Elixir, you’ll be happy to know that this problem can be golfed to under 10 lines of code, and no extra dependencies.

Here goes. In config/releases.exs, you need 1 line:

MyApp.ReleaseTasks.migrate()

Now, for the implementation module:

defmodule MyApp.ReleaseTasks do
  def migrate do
    {:ok, _} = Application.ensure_all_started(:my_app)
    path = Application.app_dir(:my_app, "priv/repo/migrations")
    Ecto.Migrator.run(MyApp.DA.Repo, path, :up, all: true)
  end
end

Ecto.Migrator.run will return a list of integers representing the migrations that were actually applied, so an extra 2 lines of code will give you a nice diagnostic log message:

defmodule MyApp.ReleaseTasks do
  require Logger

  def migrate do
    {:ok, _} = Application.ensure_all_started(:my_app)
    path = Application.app_dir(:my_app, "priv/repo/migrations")
    applied = Ecto.Migrator.run(MyApp.DA.Repo, path, :up, all: true)
    Logger.info("Applied migrations: #{applied}")
  end
end
Previous | Up | Next