lib/mix/tasks/ash_postgres.rollback.ex

defmodule Mix.Tasks.AshPostgres.Rollback do
  use Mix.Task

  import AshPostgres.MixHelpers,
    only: [migrations_path: 2, tenant_migrations_path: 2, tenants: 2]

  @shortdoc "Rolls back the repository migrations for all repositories in the provided (or configured) apis"

  @moduledoc """
  Reverts applied migrations in the given repository.
  Migrations are expected at "priv/YOUR_REPO/migrations" directory
  of the current application but it can be configured by specifying
  the `:priv` key under the repository configuration.
  Runs the latest applied migration by default. To roll back to
  a version number, supply `--to version_number`. To roll back a
  specific number of times, use `--step n`. To undo all applied
  migrations, provide `--all`.

  This is only really useful if your api or apis only use a single repo.
  If you have multiple repos and you want to run a single migration and/or
  migrate/roll them back to different points, you will need to use the
  ecto specific task, `mix ecto.migrate` and provide your repo name.

  ## Examples
      mix ash_postgres.rollback
      mix ash_postgres.rollback -r Custom.Repo
      mix ash_postgres.rollback -n 3
      mix ash_postgres.rollback --step 3
      mix ash_postgres.rollback -v 20080906120000
      mix ash_postgres.rollback --to 20080906120000

  ## Command line options
    * `--apis` - the apis who's repos should be rolledback
    * `--all` - revert all applied migrations
    * `--step` / `-n` - revert n number of applied migrations
    * `--to` / `-v` - revert all migrations down to and including version
    * `--quiet` - do not log migration commands
    * `--prefix` - the prefix to run migrations on
    * `--pool-size` - the pool size if the repository is started only for the task (defaults to 1)
    * `--log-sql` - log the raw sql migrations are running
    * `--tenants` - roll back tenant migrations
    * `--only-tenants` - in combo with `--tenants`, only rolls back the provided tenants, e.g `tenant1,tenant2,tenant3`
    * `--except-tenants` - in combo with `--tenants`, does not rollback the provided tenants, e.g `tenant1,tenant2,tenant3`
  """

  @doc false
  def run(args) do
    {opts, _, _} =
      OptionParser.parse(args,
        switches: [
          all: :boolean,
          step: :integer,
          to: :integer,
          start: :boolean,
          quiet: :boolean,
          prefix: :string,
          pool_size: :integer,
          log_sql: :boolean,
          only_tenants: :string,
          except_tenants: :string
        ],
        aliases: [n: :step, v: :to]
      )

    repos = AshPostgres.MixHelpers.repos!(opts, args)

    repo_args =
      Enum.flat_map(repos, fn repo ->
        ["-r", to_string(repo)]
      end)

    rest_opts =
      args
      |> AshPostgres.MixHelpers.delete_arg("--apis")
      |> AshPostgres.MixHelpers.delete_arg("--migrations-path")
      |> AshPostgres.MixHelpers.delete_flag("--tenants")
      |> AshPostgres.MixHelpers.delete_flag("--only-tenants")
      |> AshPostgres.MixHelpers.delete_flag("--except-tenants")

    if opts[:tenants] do
      for repo <- repos do
        Ecto.Migrator.with_repo(repo, fn repo ->
          for tenant <- tenants(repo, opts) do
            rest_opts = AshPostgres.MixHelpers.delete_arg(rest_opts, "--prefix")

            Mix.Task.run(
              "ecto.rollback",
              repo_args ++
                rest_opts ++
                ["--prefix", tenant, "--migrations-path", tenant_migrations_path(opts, repo)]
            )

            Mix.Task.reenable("ecto.rollback")
          end
        end)
      end
    else
      for repo <- repos do
        Mix.Task.run(
          "ecto.rollback",
          repo_args ++ rest_opts ++ ["--migrations-path", migrations_path(opts, repo)]
        )

        Mix.Task.reenable("ecto.rollback")
      end
    end
  end
end