bitwalker / distillery

Simplify deployments in Elixir with OTP releases!
MIT License
2.97k stars 398 forks source link

`Code.require_file/2` not working when running mix release #635

Closed i-n-g-m-a-r closed 5 years ago

i-n-g-m-a-r commented 5 years ago

Steps to reproduce

Implement a script (myapp/config/settings.exs) that loads settings, like so:

defmodule Settings do
  @fields [:foo, :bar]
  @enforce_keys @fields
  defstruct @fields
  def new do
    struct(__MODULE__, %{foo: "load stuff", bar: "from somewhere"})
  end
end

Call settings script from myapp/config/config.exs like so:

load_cfg = fn ->
  with [{settings, _}] <- Code.require_file("settings.exs", "./config") do
    settings.new
  else
    _noop -> IO.inspect "not working!?"
  end
end

cfg = load_cfg.()

config :myapp, MyApp.MyModule,
  foo: cfg.foo,
  bar: cfg.bar

Verbose Logs

Generated myapp app
==> Assembling release..
==> Building release myapp:0.0.1 using environment prod
"not working!?"

==> Release failed: argument error
    :erlang.apply("not working!?", :foo, [])
    (stdlib) erl_eval.erl:680: :erl_eval.do_apply/6
    (stdlib) erl_eval.erl:888: :erl_eval.expr_list/6
    (stdlib) erl_eval.erl:240: :erl_eval.expr/5
    (stdlib) erl_eval.erl:232: :erl_eval.expr/5
    (stdlib) erl_eval.erl:233: :erl_eval.expr/5
    (stdlib) erl_eval.erl:888: :erl_eval.expr_list/6
    (stdlib) erl_eval.erl:240: :erl_eval.expr/5
    (stdlib) erl_eval.erl:232: :erl_eval.expr/5
    (stdlib) erl_eval.erl:888: :erl_eval.expr_list/6
    (stdlib) erl_eval.erl:411: :erl_eval.expr/5
    (stdlib) erl_eval.erl:126: :erl_eval.exprs/5
    (elixir) src/elixir.erl:258: :elixir.eval_forms/4
    (elixir) lib/code.ex:232: Code.eval_string/3
    (mix) lib/mix/config.ex:220: Mix.Config.eval!/2
    (distillery) lib/mix/lib/releases/config/providers/elixir.ex:80: Mix.Releases.Config.Providers.Elixir.eval!/2
    (distillery) lib/mix/lib/releases/assembler.ex:663: Mix.Releases.Assembler.generate_base_config/2
    (distillery) lib/mix/lib/releases/assembler.ex:619: Mix.Releases.Assembler.generate_sys_config/1
    (distillery) lib/mix/lib/releases/assembler.ex:262: Mix.Releases.Assembler.write_release_scripts/1
    (distillery) lib/mix/lib/releases/assembler.ex:50: Mix.Releases.Assembler.assemble/1

Description of issue

The cfg variable is a Settings struct with settings.

distillery-2.0.12

Ubuntu 18.04.2 LTS Erlang/OTP 21 [erts-10.2.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [hipe] Elixir 1.8.1 (compiled with Erlang/OTP 20)

probably not interesting, standard stuff.

didn't find anything in particular.

it's definitely a runtime configuration issue, please see the steps to reproduce. when running my app with mix phx.server and so on, there's no problem. the problem happens right after: ==> Building release myapp:0.0.1 using environment prod

i-n-g-m-a-r commented 5 years ago

It turns out Code.require_file/2 works just fine when running mix release. The issue is that the Settings module already exists in memory when Distillery loads ./config/config.exs.

So the solution is to check whether module Settings already exists:

load_cfg = fn ->
  with false <- Code.ensure_compiled?(Settings) do
    with [{settings, _}] <- Code.require_file("settings.exs", "./config") do
      settings.new
    else
      _noop -> :noop
    end
  else
    _available -> Settings.new
  end
end

cfg = load_cfg.()