priv/documentation/guides/migrations_and_tasks.md

# Migrations

## Tasks

The available tasks are:

- `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)

AshPostgres is built on top of ecto, so much of its behavior is pass-through/orchestration of that tooling.

## Basic Workflow

- 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.

For more information on generating migrations, see the module documentation here:
`Mix.Tasks.AshPostgres.GenerateMigrations`, or run `mix help ash_postgres.generate_migrations`

For running your migrations, there is a mix task that will find all of the repos configured in your apis and run their
migrations. It is a thin wrapper around `mix ecto.migrate`. Ours is called `mix ash_postgres.migrate`

If you want to run or rollback individual migrations, use the corresponding

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.

### 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`.

## Multiple Repos

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.

## Running Migrations in Production

Define a module similar to the following:

```elixir
defmodule MyApp.Release do
  @moduledoc """
  Houses tasks that need to be executed in the released application (because mix is not present in releases).
  """
  @app :my_ap
  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
    apis()
    |> Enum.flat_map(fn api ->
      api
      |> Ash.Api.resources()
      |> Enum.map(&AshPostgres.repo/1)
    end)
    |> Enum.uniq()
  end

  defp apis do
    Application.fetch_env!(:my_app, :ash_apis)
  end

  defp load_app do
    Application.load(@app)
  end
end
```