ecto.rollback.ex (4523B)
1 defmodule Mix.Tasks.Ecto.Rollback do 2 use Mix.Task 3 import Mix.Ecto 4 import Mix.EctoSQL 5 6 @shortdoc "Rolls back the repository migrations" 7 8 @aliases [ 9 r: :repo, 10 n: :step 11 ] 12 13 @switches [ 14 all: :boolean, 15 step: :integer, 16 to: :integer, 17 to_exclusive: :integer, 18 quiet: :boolean, 19 prefix: :string, 20 pool_size: :integer, 21 log_sql: :boolean, 22 log_migrations_sql: :boolean, 23 log_migrator_sql: :boolean, 24 repo: [:keep, :string], 25 no_compile: :boolean, 26 no_deps_check: :boolean, 27 migrations_path: :keep 28 ] 29 30 @moduledoc """ 31 Reverts applied migrations in the given repository. 32 33 Migrations are expected at "priv/YOUR_REPO/migrations" directory 34 of the current application, where "YOUR_REPO" is the last segment 35 in your repository name. For example, the repository `MyApp.Repo` 36 will use "priv/repo/migrations". The repository `Whatever.MyRepo` 37 will use "priv/my_repo/migrations". 38 39 You can configure a repository to use another directory by specifying 40 the `:priv` key under the repository configuration. The "migrations" 41 part will be automatically appended to it. For instance, to use 42 "priv/custom_repo/migrations": 43 44 config :my_app, MyApp.Repo, priv: "priv/custom_repo" 45 46 This task rolls back the last applied migration by default. To roll 47 back to a version number, supply `--to version_number`. To roll 48 back a specific number of times, use `--step n`. To undo all applied 49 migrations, provide `--all`. 50 51 The repositories to rollback are the ones specified under the 52 `:ecto_repos` option in the current app configuration. However, 53 if the `-r` option is given, it replaces the `:ecto_repos` config. 54 55 If a repository has not yet been started, one will be started outside 56 your application supervision tree and shutdown afterwards. 57 58 ## Examples 59 60 $ mix ecto.rollback 61 $ mix ecto.rollback -r Custom.Repo 62 63 $ mix ecto.rollback -n 3 64 $ mix ecto.rollback --step 3 65 66 $ mix ecto.rollback --to 20080906120000 67 68 ## Command line options 69 70 * `--all` - run all pending migrations 71 72 * `--log-migrations-sql` - log SQL generated by migration commands 73 74 * `--log-migrator-sql` - log SQL generated by the migrator, such as 75 transactions, table locks, etc 76 77 * `--migrations-path` - the path to load the migrations from, defaults to 78 `"priv/repo/migrations"`. This option may be given multiple times in which 79 case the migrations are loaded from all the given directories and sorted 80 as if they were in the same one 81 82 * `--no-compile` - does not compile applications before migrating 83 84 * `--no-deps-check` - does not check dependencies before migrating 85 86 * `--pool-size` - the pool size if the repository is started 87 only for the task (defaults to 2) 88 89 * `--prefix` - the prefix to run migrations on 90 91 * `--quiet` - do not log migration commands 92 93 * `-r`, `--repo` - the repo to migrate 94 95 * `--step`, `-n` - revert n migrations 96 97 * `--strict-version-order` - abort when applying a migration with old 98 timestamp (otherwise it emits a warning) 99 100 * `--to` - revert all migrations down to and including version 101 102 * `--to-exclusive` - revert all migrations down to and excluding version 103 104 """ 105 106 @impl true 107 def run(args, migrator \\ &Ecto.Migrator.run/4) do 108 repos = parse_repo(args) 109 {opts, _} = OptionParser.parse!(args, strict: @switches, aliases: @aliases) 110 111 opts = 112 if opts[:to] || opts[:to_exclusive] || opts[:step] || opts[:all], 113 do: opts, 114 else: Keyword.put(opts, :step, 1) 115 116 opts = 117 if opts[:quiet], 118 do: Keyword.merge(opts, [log: false, log_migrations_sql: false, log_migrator_sql: false]), 119 else: opts 120 121 # Start ecto_sql explicitly before as we don't need 122 # to restart those apps if migrated. 123 {:ok, _} = Application.ensure_all_started(:ecto_sql) 124 125 for repo <- repos do 126 ensure_repo(repo, args) 127 paths = ensure_migrations_paths(repo, opts) 128 pool = repo.config[:pool] 129 130 fun = 131 if Code.ensure_loaded?(pool) and function_exported?(pool, :unboxed_run, 2) do 132 &pool.unboxed_run(&1, fn -> migrator.(&1, paths, :down, opts) end) 133 else 134 &migrator.(&1, paths, :down, opts) 135 end 136 137 case Ecto.Migrator.with_repo(repo, fun, [mode: :temporary] ++ opts) do 138 {:ok, _migrated, _apps} -> :ok 139 {:error, error} -> Mix.raise "Could not start repo #{inspect repo}, error: #{inspect error}" 140 end 141 end 142 143 :ok 144 end 145 end