Thanks a lot for your detailed answer @mobileoverlord!
What I’m trying to do is to run migrations for the Phoenix UI. I’ve added the migration release task to shoehorn but I get this error:
** (RuntimeError) connect raised KeyError exception: key :database not found.The exception details are hidden, as they may contain sensitive data such as database credentials. You may set :show_sensitive_data_on_connection_error to true if you wish to see all of the details
(elixir) lib/keyword.ex:389: Keyword.fetch!/2
(postgrex) lib/postgrex/protocol.ex:90: Postgrex.Protocol.connect/1
(db_connection) lib/db_connection/connection.ex:66: DBConnection.Connection.connect/2
(connection) lib/connection.ex:622: Connection.enter_connect/5
(stdlib) proc_lib.erl:249: :proc_lib.init_p_do_apply/3
Last message: nil
State: Postgrex.Protocol
[error] GenServer #PID<0.649.0> terminating
I guess this is because the Ui app has not started yet.
I start the apps before running the migrations, this is the script based on the distillery recommendations:
defmodule Ui.ReleaseTasks do # More info on: https://embedded-elixir.com/post/2017-09-22-using-ecto-and-sqlite3-with-nerves/ https://github.com/bitwalker/distillery/blob/master/docs/guides/running_migrations.md
@start_apps [
:crypto,
:ssl,
:postgrex,
:ecto,
:logger,
# If using Ecto 3.0 or higher
:ecto_sql
]
@repos Application.get_env(:ui, :ecto_repos, [])
def migrate() do
repos_pids = start_services()
run_migrations()
stop_services()
end
def seed(_argv) do
start_services()
run_migrations()
run_seeds()
# Stop servicej
stop_services()
end
defp start_services do
IO.puts("Starting dependencies..")
# Start apps necessary for executing migrations
Enum.each(@start_apps, &Application.ensure_all_started/1)
# Start the Repo(s) for app
IO.puts("Starting repos..")
# Switch pool_size to 2 for ecto > 3.0
Enum.map(@repos, & &1.start_link(pool_size: 2))
end
defp stop_services do
IO.puts("Success!")
:init.stop()
IO.puts("Stopped!")
end
defp run_migrations do
Enum.each(@repos, &run_migrations_for/1)
end
defp run_migrations_for(repo) do
app = Keyword.get(repo.config(), :otp_app)
IO.puts("Running migrations for #{app}")
migrations_path = priv_path_for(repo, "migrations")
Ecto.Migrator.run(repo, migrations_path, :up, all: true)
end
defp run_seeds do
Enum.each(@repos, &run_seeds_for/1)
end
defp run_seeds_for(repo) do
# Run the seed script if it exists
seed_script = priv_path_for(repo, "seeds.exs")
if File.exists?(seed_script) do
IO.puts("Running seed script..")
Code.eval_file(seed_script)
end
end
defp priv_path_for(repo, filename) do
app = Keyword.get(repo.config(), :otp_app)
repo_underscore =
repo
|> Module.split()
|> List.last()
|> Macro.underscore()
priv_dir = "#{:code.priv_dir(app)}"
Path.join([priv_dir, repo_underscore, filename])
end
end
I think it fails because the Ui app has not started yet.
I’m not 100% sure about this, but I think the Ui is started with the app because it’s a dependency, but could I move the Ui start to soehorn instead of being done automatically? After that I could put the migrations script and remove the start_services
and stop_services
functions.
Does it make sense? Could it fail because starting up the db takes time and the migrations will run immediately?
@ConnorRigby the code I use is pretty similar to yours. Start the apps, migrate and stop.
I added to the Ui application.ex
def start(_type, _args) do
import Supervisor.Spec
# Migrate on startup
Ui.ReleaseTasks.migrate()
...
but it doesn’t work either. The migrations work but after that, the Ui won’t start:
00:00:16.706 [error] Supervisor 'Elixir.Ui.Supervisor' had child 'Elixir.Ui.Repo' started with 'Elixir.Ui.Repo':start_link() at undefined exit with reason {already_started,<0.720.0>} in context start_error
00:00:16.706 [error] CRASH REPORT Process <0.718.0> with 0 neighbours exited with reason: {{shutdown,{failed_to_start_child,'Elixir.Ui.Repo',{already_started,<0.720.0>}}},{'Elixir.Ui.Application',start,[normal,[]]}} in application_master:init/4 line 138
The repos were already started, I tried to stop them but somehow I still get this message.