defmodule Mix.Tasks.Recall.Rollback do @shortdoc "Rolls back the repository's applied Recall migrations" @moduledoc """ Reverts applied migrations for the given Recall-backed repository. The Recall counterpart to `mix ecto.rollback` (see `Mix.Tasks.Recall.Migrate` for why a dedicated task exists). Like `ecto.rollback`, it rolls back a single migration by default; pass `--all`, `--step`, or `--to` to roll back more. mix recall.rollback # rolls back the last migration mix recall.rollback --step 3 mix recall.rollback -r MyApp.Repo --to 20240101000000 ## Options * `-r`, `--repo` — the repo to roll back * `--all` — roll back all applied migrations * `--step` — roll back N migrations (default 1) * `--to` — roll back down to and including a target version * `--migrations-path` — a migrations directory (may be given more than once) * `--log-level` — the `Logger` level for migration logs (default `info`) """ use Mix.Task import Mix.Ecto @aliases [r: :repo, n: :step, v: :to] @switches [ all: :boolean, step: :integer, to: :integer, repo: [:string, :keep], migrations_path: [:string, :keep], log_level: :string ] @impl true def run(args) do repos = parse_repo(args) {opts, _} = OptionParser.parse!(args, strict: @switches, aliases: @aliases) opts = opts |> default_step() |> normalize_log_level() |> normalize_paths() for repo <- repos do ensure_repo(repo, args) Ecto.Migrator.with_repo(repo, fn repo -> Recall.Migrator.run(repo, :down, opts) end) end end # Roll back exactly one migration unless a broader bound was given. defp default_step(opts) do if Keyword.has_key?(opts, :all) or Keyword.has_key?(opts, :step) or Keyword.has_key?(opts, :to) do opts else Keyword.put(opts, :step, 1) end end defp normalize_log_level(opts) do case Keyword.pop(opts, :log_level) do {nil, opts} -> opts {level, opts} -> Keyword.put(opts, :log, String.to_existing_atom(level)) end end defp normalize_paths(opts) do case Keyword.get_values(opts, :migrations_path) do [] -> Keyword.delete(opts, :migrations_path) paths -> opts |> Keyword.delete(:migrations_path) |> Keyword.put(:migrations_paths, paths) end end end