1.5 KiB · text History 4b6fe3b
defmodule OhMyExOhIrcd.Release.Migrator do
  @moduledoc """
  Synchronous, in-supervision-tree migrator.

  When `:migrate_on_start` is enabled (set in prod via `runtime.exs`),
  this runs every pending migration against the already-started Repo
  *before* the rest of the supervision tree boots, then returns
  `:ignore` so no process lingers.

  It sits immediately after `OhMyExOhIrcd.Repo` in the children list, so
  the Repo is available but the Endpoint / IRC listeners haven't started
  yet — clients never touch a half-migrated schema.

  In a cluster every node runs this at boot; Ecto's migration lock
  serialises them, so only the first node actually applies each
  migration and the others find nothing pending.

  Disabled by default (dev/test, and any env that hasn't opted in), so
  it's a cheap no-op there. If migrations fail the supervisor `start/2`
  fails and the node refuses to boot — fail fast rather than serve on a
  stale schema.
  """
  require Logger

  def child_spec(_opts) do
    %{id: __MODULE__, start: {__MODULE__, :start_link, []}, type: :worker, restart: :transient}
  end

  def start_link do
    if migrate_on_start?() do
      for repo <- repos() do
        versions = Ecto.Migrator.run(repo, :up, all: true)
        Logger.info("#{inspect(repo)}: applied #{length(versions)} migration(s) on startup")
      end
    end

    :ignore
  end

  @doc false
  def migrate_on_start?, do: Application.get_env(:oh_my_ex_oh_ircd, :migrate_on_start, false)

  defp repos, do: Application.fetch_env!(:oh_my_ex_oh_ircd, :ecto_repos)
end