2020-09-11 12:26:47 +12:00
|
|
|
defmodule Mix.Tasks.AshPostgres.GenerateMigrations do
|
2020-09-11 15:47:14 +12:00
|
|
|
@moduledoc """
|
|
|
|
Generates migrations, and stores a snapshot of your resources.
|
|
|
|
|
|
|
|
Options:
|
|
|
|
|
|
|
|
* `apis` - a comma separated list of API modules, for which migrations will be generated
|
2021-01-23 09:49:34 +13:00
|
|
|
* `snapshot-path` - a custom path to store the snapshots, defaults to "priv/resource_snapshots"
|
|
|
|
* `migration-path` - a custom path to store the migrations, defaults to "priv".
|
2020-09-11 15:47:14 +12:00
|
|
|
Migrations are stored in a folder for each repo, so `priv/repo_name/migrations`
|
2021-01-23 09:49:34 +13:00
|
|
|
* `tenant-migration-path` - Same as `migration_path`, except for any tenant specific migrations
|
|
|
|
* `drop-columns` - whether or not to drop columns as attributes are removed. See below for more
|
2020-09-11 15:47:14 +12:00
|
|
|
|
|
|
|
Flags:
|
|
|
|
|
|
|
|
* `quiet` - messages for file creations will not be printed
|
2021-01-23 09:49:34 +13:00
|
|
|
* `no-format` - files that are created will not be formatted with the code formatter
|
|
|
|
* `dry-run` - no files are created, instead the new migration is printed
|
|
|
|
* `check-migrated` - no files are created, returns an exit(1) code if the current snapshots and resources don't fit
|
2020-09-11 15:47:14 +12:00
|
|
|
|
2021-01-07 18:37:41 +13:00
|
|
|
#### Snapshots
|
|
|
|
|
|
|
|
Snapshots are stored in a folder for each table that migrations are generated for. Each snapshot is
|
|
|
|
stored in a file with a timestamp of when it was generated.
|
|
|
|
This is important because it allows for simultaneous work to be done on separate branches, and for rolling back
|
|
|
|
changes more easily, e.g removing a generated migration, and deleting the most recent snapshot, without having to redo
|
|
|
|
all of it
|
2020-11-20 16:09:26 +13:00
|
|
|
|
|
|
|
#### Dropping columns
|
|
|
|
|
|
|
|
Generally speaking, it is bad practice to drop columns when you deploy a change that
|
|
|
|
would remove an attribute. The main reasons for this are backwards compatibility and rolling restarts.
|
|
|
|
If you deploy an attribute removal, and run migrations. Regardless of your deployment sstrategy, you
|
|
|
|
won't be able to roll back, because the data has been deleted. In a rolling restart situation, some of
|
|
|
|
the machines/pods/whatever may still be running after the column has been deleted, causing errors. With
|
|
|
|
this in mind, its best not to delete those columns until later, after the data has been confirmed unnecessary.
|
|
|
|
To that end, the migration generator leaves the column dropping code commented. You can pass `--drop_columns`
|
|
|
|
to tell it to uncomment those statements. Additionally, you can just uncomment that code on a case by case
|
|
|
|
basis.
|
|
|
|
|
2020-09-11 15:47:14 +12:00
|
|
|
#### Conflicts/Multiple Resources
|
|
|
|
|
|
|
|
The migration generator can support multiple schemas using the same table.
|
|
|
|
It will raise on conflicts that it can't resolve, like the same field with different
|
|
|
|
types. It will prompt to resolve conflicts that can be resolved with human input.
|
|
|
|
For example, if you remove an attribute and add an attribute, it will ask you if you are renaming
|
|
|
|
the column in question. If not, it will remove one column and add the other.
|
|
|
|
|
|
|
|
Additionally, it lowers things to the database where possible:
|
|
|
|
|
|
|
|
#### Defaults
|
|
|
|
There are three anonymous functions that will translate to database-specific defaults currently:
|
|
|
|
|
|
|
|
* `&Ash.uuid/0` - Only if `uuid-ossp` is in your `c:AshPostgres.Repo.installed_extensions()`
|
|
|
|
* `&Ecto.UUID.generate/0` - Only if `uuid-ossp` is in your `c:AshPostgres.Repo.installed_extensions()`
|
|
|
|
* `&DateTime.utc_now/0`
|
|
|
|
|
|
|
|
Non-function default values will be dumped to their native type and inspected. This may not work for some types,
|
|
|
|
and may require manual intervention/patches to the migration generator code.
|
|
|
|
|
|
|
|
#### Identities
|
|
|
|
|
|
|
|
Identities will cause the migration generator to generate unique constraints. If multiple
|
|
|
|
resources target the same table, you will be asked to select the primary key, and any others
|
|
|
|
will be added as unique constraints.
|
|
|
|
"""
|
2020-09-11 12:26:47 +12:00
|
|
|
use Mix.Task
|
|
|
|
|
2020-09-11 15:47:14 +12:00
|
|
|
@shortdoc "Generates migrations, and stores a snapshot of your resources"
|
2020-09-11 12:26:47 +12:00
|
|
|
def run(args) do
|
|
|
|
{opts, _} =
|
|
|
|
OptionParser.parse!(args,
|
|
|
|
strict: [
|
|
|
|
apis: :string,
|
|
|
|
snapshot_path: :string,
|
|
|
|
migration_path: :string,
|
2020-12-06 14:17:39 +13:00
|
|
|
tenant_migration_path: :string,
|
2020-09-11 12:26:47 +12:00
|
|
|
quiet: :boolean,
|
2020-09-20 10:08:09 +12:00
|
|
|
no_format: :boolean,
|
2020-11-20 16:09:26 +13:00
|
|
|
dry_run: :boolean,
|
2021-01-13 14:47:17 +13:00
|
|
|
check_migrated: :boolean,
|
2020-11-20 16:09:26 +13:00
|
|
|
drop_columns: :boolean
|
2020-09-11 12:26:47 +12:00
|
|
|
]
|
|
|
|
)
|
|
|
|
|
2021-01-27 13:16:29 +13:00
|
|
|
apis = AshPostgres.MixHelpers.apis(opts, args)
|
2020-09-11 12:26:47 +12:00
|
|
|
|
|
|
|
if apis == [] do
|
2021-01-23 10:44:39 +13:00
|
|
|
raise "must supply the --apis argument, or set `config :my_app, ash_apis: [...]` in config"
|
2020-09-11 12:26:47 +12:00
|
|
|
end
|
|
|
|
|
2020-09-20 10:08:09 +12:00
|
|
|
opts =
|
|
|
|
opts
|
|
|
|
|> Keyword.put(:format, !opts[:no_format])
|
|
|
|
|> Keyword.delete(:no_format)
|
|
|
|
|
2020-09-11 12:26:47 +12:00
|
|
|
AshPostgres.MigrationGenerator.generate(apis, opts)
|
|
|
|
end
|
|
|
|
end
|