From 248ef72dae897406084e9430f216545e96619086 Mon Sep 17 00:00:00 2001 From: Benjamin Piouffle Date: Thu, 30 Apr 2026 10:35:39 +0200 Subject: [PATCH] chore: release improvements --- Dockerfile | 6 ++---- apps/db/lib/db/application.ex | 15 ++++++++++++++- apps/db/lib/db/release_tasks.ex | 30 +++++++++++++++++++++++++++--- apps/db/lib/db/repo.ex | 31 +++++++++++++++++++++++++++++++ apps/db/lib/db/seeds.ex | 12 ++++++++++-- 5 files changed, 84 insertions(+), 10 deletions(-) diff --git a/Dockerfile b/Dockerfile index 854e7483..c1e1c2d8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,7 +6,7 @@ ARG MIX_ENV=prod ENV HOME=/opt/app/ SHELL=/bin/bash MIX_ENV=$MIX_ENV WORKDIR /opt/build -# Cache dependencies +# Build & cache dependencies COPY mix.exs mix.lock ./ COPY apps/cf/mix.exs ./apps/cf/ COPY apps/cf_atom_feed/mix.exs ./apps/cf_atom_feed/ @@ -18,12 +18,10 @@ COPY apps/db/mix.exs ./apps/db/ RUN mix local.hex --force RUN mix local.rebar --force RUN HEX_HTTP_CONCURRENCY=4 HEX_HTTP_TIMEOUT=180 mix deps.get --only $MIX_ENV - -# Build dependencies -COPY . . RUN mix deps.compile # Build app +COPY . . RUN mix compile RUN mix release diff --git a/apps/db/lib/db/application.ex b/apps/db/lib/db/application.ex index aa5060b2..bc681b21 100644 --- a/apps/db/lib/db/application.ex +++ b/apps/db/lib/db/application.ex @@ -5,7 +5,20 @@ defmodule DB.Application do require Logger def start(_type, _args) do - # Define workers and child supervisors to be supervised + case DB.Repo.ensure_storage_created() do + :ok -> + start_repo_and_migrate() + + {:error, reason} -> + Logger.error( + "Could not create or reach Postgres database #{inspect(DB.Repo.config()[:database])}: #{reason}" + ) + + {:error, {:could_not_create_database, reason}} + end + end + + defp start_repo_and_migrate do children = [ # Starts a worker by calling: DB.Worker.start_link(arg1, arg2, arg3) # {DB.Worker, [arg1, arg2, arg3]}, diff --git a/apps/db/lib/db/release_tasks.ex b/apps/db/lib/db/release_tasks.ex index 5020d12e..f5a21067 100644 --- a/apps/db/lib/db/release_tasks.ex +++ b/apps/db/lib/db/release_tasks.ex @@ -12,6 +12,7 @@ defmodule DB.ReleaseTasks do :ssl, :postgrex, :ecto, + :ecto_sql, :logger ] @@ -27,6 +28,17 @@ defmodule DB.ReleaseTasks do :init.stop() end + @doc """ + Loads dev seed data (Cypress video + admin user) via `DB.Seeds.seed_dev_data/0`. + For local integration tests against a prod release — not for production containers. + """ + def seed_dev_data_release do + init() + {:ok, _} = Application.ensure_all_started(:bcrypt_elixir) + DB.Seeds.seed_dev_data() + :init.stop() + end + def seed do init() @@ -68,17 +80,29 @@ defmodule DB.ReleaseTasks do def priv_dir(app), do: "#{:code.priv_dir(app)}" defp init do - # Load the code, but don't start it - :ok = Application.load(:db) + # Load the code, but don't start it (eval may already have :db loaded) + case Application.load(:db) do + :ok -> :ok + {:error, {:already_loaded, :db}} -> :ok + {:error, reason} -> raise "Application.load(:db) failed: #{inspect(reason)}" + end # Start apps necessary for executing migrations Enum.each(@start_apps, &Application.ensure_all_started/1) Logger.info("Dependencies started, loading runtime configuration...") + case DB.Repo.ensure_storage_created() do + :ok -> + :ok + + {:error, reason} -> + raise "Could not ensure Postgres database exists: #{reason}" + end + # Start the Repo(s) for myapp Logger.info("Starting repos..") - Enum.each(@repos, & &1.start_link(pool_size: 1)) + Enum.each(@repos, & &1.start_link(pool_size: 2)) end defp run_migrations_for(app) do diff --git a/apps/db/lib/db/repo.ex b/apps/db/lib/db/repo.ex index 8f7776ed..bf2e4772 100644 --- a/apps/db/lib/db/repo.ex +++ b/apps/db/lib/db/repo.ex @@ -1,4 +1,35 @@ defmodule DB.Repo do use Ecto.Repo, otp_app: :db, adapter: Ecto.Adapters.Postgres use Scrivener, page_size: 10 + + require Logger + + @doc """ + Ensures the configured database exists on Postgres (runs `CREATE DATABASE` via + the adapter). Uses `maintenance_database` (default `"postgres"`). + + Safe to call when the DB already exists (`:already_up`). + + Call **before** `start_link`; returns `:ok` or `{:error, reason}`. + """ + @spec ensure_storage_created() :: :ok | {:error, term()} + def ensure_storage_created do + adapter = __adapter__() + opts = config() + + case adapter.storage_up(opts) do + :ok -> + Logger.info("Created Postgres database #{inspect(opts[:database])}") + :ok + + {:error, :already_up} -> + :ok + + {:error, reason} -> + {:error, format_storage_error(reason)} + end + end + + defp format_storage_error(reason) when is_binary(reason), do: reason + defp format_storage_error(reason), do: inspect(reason) end diff --git a/apps/db/lib/db/seeds.ex b/apps/db/lib/db/seeds.ex index 05b5ab45..02b93def 100644 --- a/apps/db/lib/db/seeds.ex +++ b/apps/db/lib/db/seeds.ex @@ -17,8 +17,16 @@ defmodule DB.Seeds do before the admin user so the video keeps `id == 1` and `hash_id == "Jzqg"`. """ def seed_dev_data(repo \\ Repo) do - seed_cypress_video_and_statement(repo) - seed_dev_admin_user(repo) + env = Application.get_env(:db, :env) + + if env != :dev and env != :test do + Logger.warning("API is running in non-dev mode. Skipping dev seeds.") + :ok + else + seed_cypress_video_and_statement(repo) + seed_dev_admin_user(repo) + end + :ok end