Fix seeds to run in production #462
5 changed files with 57 additions and 4 deletions
|
|
@ -2,24 +2,26 @@
|
|||
|
||||
## Overview
|
||||
|
||||
- **Admin bootstrap:** In production, no seeds run. The first admin user is created/updated from environment variables in the Docker entrypoint (after migrate, before server). Password can be changed without redeploy via `bin/mv eval "Mv.Release.seed_admin()"`.
|
||||
- **Admin bootstrap:** In production, the Docker entrypoint runs migrate, then `Mv.Release.run_seeds/0` (bootstrap seeds; set `RUN_DEV_SEEDS=true` to also run dev seeds), then `seed_admin/0` from ENV, then the server. Password can be changed without redeploy via `bin/mv eval "Mv.Release.seed_admin()"`.
|
||||
- **OIDC role sync:** Optional mapping from OIDC groups (e.g. from Authentik profile scope) to the Admin role. Users in the configured admin group get the Admin role on registration and on each sign-in.
|
||||
|
||||
## Admin Bootstrap (Part A)
|
||||
|
||||
### Environment Variables
|
||||
|
||||
- `RUN_DEV_SEEDS` – If set to `"true"`, `run_seeds/0` also runs dev seeds (members, groups, sample data). Otherwise only bootstrap seeds run.
|
||||
- `ADMIN_EMAIL` – Email of the admin user to create/update. If unset, seed_admin/0 does nothing.
|
||||
- `ADMIN_PASSWORD` – Password for the admin user. If unset (and no file), no new user is created; if a user with ADMIN_EMAIL already exists (e.g. OIDC-only), their role is set to Admin (no password change).
|
||||
- `ADMIN_PASSWORD_FILE` – Path to a file containing the password (e.g. Docker secret).
|
||||
|
||||
### Release Task
|
||||
### Release Tasks
|
||||
|
||||
- `Mv.Release.run_seeds/0` – Runs bootstrap seeds (fee types, custom fields, roles, settings). If `RUN_DEV_SEEDS` env is `"true"`, also runs dev seeds (members, groups, sample data). Idempotent.
|
||||
- `Mv.Release.seed_admin/0` – Reads ADMIN_EMAIL and password from ADMIN_PASSWORD or ADMIN_PASSWORD_FILE. If both email and password are set: creates or updates the user with the Admin role. If only ADMIN_EMAIL is set: sets the Admin role on an existing user with that email (for OIDC-only admins); does not create a user. Idempotent.
|
||||
|
||||
### Entrypoint
|
||||
|
||||
- rel/overlays/bin/docker-entrypoint.sh – After migrate, runs seed_admin(), then starts the server.
|
||||
- rel/overlays/bin/docker-entrypoint.sh – After migrate, runs run_seeds(), then seed_admin(), then starts the server.
|
||||
|
||||
### Seeds (Dev/Test)
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ defmodule Mv.Release do
|
|||
## Tasks
|
||||
|
||||
- `migrate/0` - Runs all pending Ecto migrations.
|
||||
- `run_seeds/0` - Runs bootstrap seeds (fee types, custom fields, roles, settings).
|
||||
In production, set `RUN_DEV_SEEDS=true` to also run dev seeds (members, groups, sample data).
|
||||
- `seed_admin/0` - Ensures an admin user exists from ENV (ADMIN_EMAIL, ADMIN_PASSWORD
|
||||
or ADMIN_PASSWORD_FILE). Idempotent; can be run on every deployment or via shell
|
||||
to update the admin password without redeploying.
|
||||
|
|
@ -26,6 +28,40 @@ defmodule Mv.Release do
|
|||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Runs seed scripts so the database has required bootstrap data (and optionally dev data).
|
||||
|
||||
- Always runs bootstrap seeds (fee types, custom fields, roles, system user, settings).
|
||||
- If `RUN_DEV_SEEDS` env is set to `"true"`, also runs dev seeds (members, groups, sample data).
|
||||
|
||||
Uses paths from the application's priv dir so it works in releases (no Mix). Idempotent.
|
||||
"""
|
||||
def run_seeds do
|
||||
case Application.ensure_all_started(@app) do
|
||||
{:ok, _} -> :ok
|
||||
{:error, {app, reason}} -> raise "Failed to start #{inspect(app)}: #{inspect(reason)}"
|
||||
end
|
||||
|
||||
priv = :code.priv_dir(@app)
|
||||
bootstrap_path = Path.join(priv, "repo/seeds_bootstrap.exs")
|
||||
dev_path = Path.join(priv, "repo/seeds_dev.exs")
|
||||
|
||||
prev = Code.compiler_options()
|
||||
Code.compiler_options(ignore_module_conflict: true)
|
||||
|
||||
try do
|
||||
Code.eval_file(bootstrap_path)
|
||||
IO.puts("✅ Bootstrap seeds completed.")
|
||||
|
||||
if System.get_env("RUN_DEV_SEEDS") == "true" do
|
||||
Code.eval_file(dev_path)
|
||||
IO.puts("✅ Dev seeds completed.")
|
||||
end
|
||||
after
|
||||
Code.compiler_options(prev)
|
||||
end
|
||||
end
|
||||
|
||||
def rollback(repo, version) do
|
||||
load_app()
|
||||
{:ok, _, _} = Ecto.Migrator.with_repo(repo, &Ecto.Migrator.run(&1, :down, to: version))
|
||||
|
|
|
|||
|
|
@ -5,6 +5,9 @@
|
|||
# Bootstrap runs in all environments. Dev seeds (members, groups, sample data)
|
||||
# run only in dev and test.
|
||||
#
|
||||
# In production (release): seeds are run via Mv.Release.run_seeds/0 from the
|
||||
# container entrypoint. Set RUN_DEV_SEEDS=true to also run dev seeds there.
|
||||
#
|
||||
# Compiler option ignore_module_conflict is set only during seed evaluation
|
||||
# so that eval_file of bootstrap/dev does not emit "redefining module" warnings;
|
||||
# it is always restored in `after` to avoid hiding real conflicts elsewhere.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,15 @@
|
|||
# Bootstrap seeds: run in all environments (dev, test, prod).
|
||||
# Creates only data required for system startup: fee types, custom fields,
|
||||
# roles, admin user, system user, global settings. No members, no groups.
|
||||
#
|
||||
# Safe to run from release (no Mix): env is taken from MIX_ENV when Mix.env/0 is not available.
|
||||
|
||||
mix_env =
|
||||
try do
|
||||
Mix.env()
|
||||
rescue
|
||||
UndefinedFunctionError -> (System.get_env("MIX_ENV") || "prod") |> String.to_atom()
|
||||
end
|
||||
|
||||
alias Mv.Accounts
|
||||
alias Mv.Membership
|
||||
|
|
@ -121,7 +130,7 @@ end
|
|||
admin_email = System.get_env("ADMIN_EMAIL") || "admin@localhost"
|
||||
System.put_env("ADMIN_EMAIL", admin_email)
|
||||
|
||||
if Mix.env() in [:dev, :test] and is_nil(System.get_env("ADMIN_PASSWORD")) and
|
||||
if mix_env in [:dev, :test] and is_nil(System.get_env("ADMIN_PASSWORD")) and
|
||||
is_nil(System.get_env("ADMIN_PASSWORD_FILE")) do
|
||||
System.put_env("ADMIN_PASSWORD", "testpassword")
|
||||
end
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@ set -e
|
|||
echo "==> Running database migrations..."
|
||||
/app/bin/migrate
|
||||
|
||||
echo "==> Running seeds (bootstrap; dev if RUN_DEV_SEEDS=true)..."
|
||||
/app/bin/mv eval "Mv.Release.run_seeds()"
|
||||
|
||||
echo "==> Seeding admin user from ENV (ADMIN_EMAIL, ADMIN_PASSWORD)..."
|
||||
/app/bin/mv eval "Mv.Release.seed_admin()"
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue