2021-01-27 13:16:29 +13:00
# Migrations
2023-09-21 10:01:09 +12:00
## Migration Generator Primer
< iframe width = "560" height = "315" src = "https://www.youtube.com/embed/GtsL_lIis4Q?si=5G6-5ckzBEzL4zko" title = "YouTube video player" frameborder = "0" allow = "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen > < / iframe >
2021-01-27 13:20:46 +13:00
## Tasks
The available tasks are:
2021-05-06 07:20:28 +12:00
- `mix ash_postgres.generate_migrations`
- `mix ash_postgres.create`
- `mix ash_postgres.drop`
- `mix ash_postgres.migrate` (use `mix ash_postgres.migrate --tenants` to run tenant migrations)
2021-01-27 13:20:46 +13:00
2021-01-27 13:16:29 +13:00
AshPostgres is built on top of ecto, so much of its behavior is pass-through/orchestration of that tooling.
2021-01-27 13:20:46 +13:00
## Basic Workflow
2021-05-06 07:20:28 +12:00
- Make resource changes
- Run `mix ash_postgres.generate_migrations` to generate migrations and resource snapshots
- Run `mix ash_postgres.migrate` to run those migrations
- Run `mix ash_postgres.migrate --tenants` _as well_ if you have multi-tenant resources.
2021-01-27 13:20:46 +13:00
For more information on generating migrations, see the module documentation here:
2021-01-27 13:16:29 +13:00
`Mix.Tasks.AshPostgres.GenerateMigrations` , or run `mix help ash_postgres.generate_migrations`
2024-03-28 09:52:28 +13:00
For running your migrations, there is a mix task that will find all of the repos configured in your domains and run their
2021-01-27 13:16:29 +13:00
migrations. It is a thin wrapper around `mix ecto.migrate` . Ours is called `mix ash_postgres.migrate`
2021-05-06 07:20:28 +12:00
If you want to run or rollback individual migrations, use the corresponding
2021-01-27 13:16:29 +13:00
For tenant migrations (see the multitenancy guides for more) generated by multitenant resources, make sure you are using
`mix ash_postgres.generate_migrations` . It is not sufficient to run `mix ash_postgres.migrate --migrations_path tenant_migrations_path` . You will also need to define a `list_tenants/0` function in your repo module. See `AshPostgres.Repo` for more.
2021-05-06 07:20:28 +12:00
### Regenerating Migrations
Often, you will run into a situation where you want to make a slight change to a resource after you've already generated and run migrations. If you are using git and would like to undo those changes, then regenerate the migrations, this script may prove useful:
```bash
#!/bin/bash
# Get count of untracked migrations
N_MIGRATIONS=$(git ls-files --others priv/repo/migrations | wc -l)
# Rollback untracked migrations
mix ecto.rollback -n $N_MIGRATIONS
# Delete untracked migrations and snapshots
git ls-files --others priv/repo/migrations | xargs rm
git ls-files --others priv/resource_snapshots | xargs rm
# Regenerate migrations
mix ash_postgres.generate_migrations
# Run migrations if flag
if echo $* | grep -e "-m" -q
then
mix ecto.migrate
fi
```
After saving this file to something like `regen.sh` , make it executable with `chmod +x regen.sh` . Now you can run it with `./regen.sh` . If you would like the migrations to automatically run after regeneration, add the `-m` flag: `./regen.sh -m` .
2021-01-27 13:16:29 +13:00
## Multiple Repos
2021-01-27 13:20:46 +13:00
If you are using multiple repos, you will likely need to use `mix ecto.migrate` and manage it separately for each repo, as the options would
be applied to both repo, which wouldn't make sense.
2021-11-25 13:58:20 +13:00
## Running Migrations in Production
Define a module similar to the following:
```elixir
defmodule MyApp.Release do
@moduledoc """
2023-10-25 05:33:17 +13:00
Tasks that need to be executed in the released application (because mix is not present in releases).
2021-11-25 13:58:20 +13:00
"""
2023-10-25 05:33:17 +13:00
@app :my_app
2021-11-25 13:58:20 +13:00
def migrate do
load_app()
for repo < - repos ( ) do
{:ok, _, _ } = Ecto.Migrator.with_repo(repo, & Ecto.Migrator.run(& 1, :up, all: true))
end
end
# only needed if you are using postgres multitenancy
def migrate_tenants do
load_app()
for repo < - repos ( ) do
repo_name = repo |> Module.split() |> List.last() |> Macro.underscore()
path =
"priv/"
|> Path.join(repo_name)
|> Path.join("tenant_migrations")
# This may be different for you if you are not using the default tenant migrations
{:ok, _, _ } =
Ecto.Migrator.with_repo(
repo,
fn repo ->
for tenant < - repo . all_tenants ( ) do
Ecto.Migrator.run(repo, path, :up, all: true, prefix: tenant)
end
end
)
end
end
# only needed if you are using postgres multitenancy
def migrate_all do
load_app()
migrate()
migrate_tenants()
end
def rollback(repo, version) do
load_app()
{:ok, _, _ } = Ecto.Migrator.with_repo(repo, & Ecto.Migrator.run(& 1, :down, to: version))
end
# only needed if you are using postgres multitenancy
def rollback_tenants(repo, version) do
load_app()
repo_name = repo |> Module.split() |> List.last() |> Macro.underscore()
path =
"priv/"
|> Path.join(repo_name)
|> Path.join("tenant_migrations")
# This may be different for you if you are not using the default tenant migrations
for tenant < - repo . all_tenants ( ) do
{:ok, _, _ } =
Ecto.Migrator.with_repo(
repo,
& Ecto.Migrator.run(& 1, path, :down,
to: version,
prefix: tenant
)
)
end
end
defp repos do
2024-03-28 09:52:28 +13:00
domains()
|> Enum.flat_map(fn domain ->
domain
|> Ash.Domain.Info.resources()
2023-10-25 05:33:17 +13:00
|> Enum.map(& AshPostgres.DataLayer.Info.repo/1)
2021-11-25 13:58:20 +13:00
end)
|> Enum.uniq()
end
2024-03-28 09:52:28 +13:00
defp domains do
Application.fetch_env!(@app, :ash_domains)
2021-11-25 13:58:20 +13:00
end
defp load_app do
Application.load(@app)
end
end
```