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