> Since "config/releases.exs" run only during releases, if you have a
syntax error (or similar), you will find about it just way too late

We solved this problem by setting dynamic runtime configuration ONLY in
"config/releases.exs" like so
config :my_app, base_url: URI.parse(System.fetch_env!("BASE_URL"))

And "dev.exs" sets some defaults and uses the same releases.exs to
configure applications:
[{"BASE_URL", "http://localhost"}]
|> Enum.each(fn {key, value} -> System.get_env(key) || System.put_env(key,
value) end)
import_config "releases.exs"

Of course, when used like that, it wouldn't affect the already started
applications which rely on configuration during the startup (as you mention
:kernel, :stdlib, :elixir and :mix).

However we don't import "releases.exs" in other environments:

* "prod" is only used for compilation on CI, so there's no need to load
dynamic runtime configuration, and it contains only production-grade
compile-time configuration and non-configurable runtime configuration;
* "test" contains its own set of settings completely separate from the rest
but it's possible to `import "prod.exs"` if that reduces repetitions.

So I suppose our usage of "releases.exs" is similar to what you are
proposing with "runtime.exs".

However I think using it unconditionally in every environment will only
complicate things, as you mention we'd have to branch on Mix.env/Config.env
- this seems more complex than our current approach.

> In the future we will deprecate Application.get_env/3 in the module body.

Does that mean during module compilation? I guess it'll still be available
in normal runtime?

As I understand "runtime.exs" is supposed to be simply copied to the
release.
Although it looks reasonably safe, I definitely wouldn't want to see some
testing or development settings in a file included in a release especially
if they are unused.


Maybe we should come to this problem from a different side.
What if we introduce a separate "compilation" stage (using separate set of
environments - e.g. dev_compile/prod_compile) and start a clean VM for the
real runtime after Mix compiled the applications?

Kind regards,
Dmitry Belyaev


On Thu, Mar 5, 2020 at 4:20 AM José Valim <jose.va...@dashbit.co> wrote:

> Over the last releases we have been making improvements to the
> configuration system. However, there is still one large gap to be
> addressed. Today config files are treated as compile-time configuration by
> default. This behaviour, alongside overuse from libraries, made using the
> application environment via config files more rigid and error prone than
> they have to be.
>
> Today, Mix will load "config/config.exs" before any of the dependencies
> are compiled and on every command. This makes basic scenarios like the ones
> below impossible:
>
>    -
>
>    If your project requires some environment variables in development,
>    you cannot enforce them. Otherwise, basic commands such as mix help or
>    editor integration will stop working
>
>
>    -
>
>    If you need to configure one application based on another application,
>    that is impossible to do as well, as no applications are available at this
>    point
>
> Also, because configuration files run during compilation by default,
> library authors often accidentally rely on compile-time configuration.
> Luckily, Elixir v1.10 has added Application.compile_env/2. In the future
> we will deprecate Application.get_env/3 in the module body, which will
> help steer people to the correct direction in their own applications.
>
> On the opposite side, we have releases, where the configuration works at
> runtime only. Given there is no way to execute configuration at runtime in
> Mix, we end-up with two completely disjoint approaches. This introduces a
> couple issues of their own:
>
>    -
>
>    Since "config/releases.exs" run only during releases, if you have a
>    syntax error (or similar), you will find about it just way too late
>
>
>    -
>
>    There is no way for frameworks like Phoenix to provide a configuration
>    file that works for both Mix and release deployments
>
> Our goal is to address these issues. However, it is important to consider
> that a complex project today may already have many configuration files:
>
>    - config/config.exs (compile time) - shared configuration settings
>    - config/{dev,test,prod}.exs (compile time) - per env configuration
>    settings
>    - config/releases.exs (runtime) - release specific configuration
>    settings
>
> Therefore, we would like to propose a new configuration file that can
> address the problem above while replacing the need to use
> "config/releases.exs" in 99% of the cases.
> Proposal: introduce config/runtime.exs
>
> Our proposal is simple: we will introduce "config/runtime.exs".
> "config/runtime.exs" will be loaded both by Mix and Releases, closing the
> gap between them.
>
> For Mix, "config/runtime.exs" will load after the code is compiled and
> before the application starts, this allows "config/runtime.exs" to rely on
> code from dependencies, as long as you keep in mind that any application
> that is started during "config/runtime.exs" cannot be configured by
> "config/runtime.exs" itself. Furthermore, given "config/runtime.exs" works
> at runtime, changing it won't require the whole application to be
> recompiled.
>
> For Releases, it will work precisely the same as "config/releases.exs". If
> both are available, "config/runtime.exs" is executed first, followed by
> "config/releases.exs".
>
> There are a couple pitfalls to be aware though:
>
>    -
>
>    Since "config/runtime.exs" is used by both Mix and releases, it cannot
>    configure :kernel, :stdlib, :elixir, and :mix themselves. Attempting
>    to configure those will emit an error. For those rare scenarios, you will
>    need to use "config/releases.exs" - but "config/releases.exs" will remain
>    simple, which will reduce the odds of syntax errors.
>
>
>    -
>
>    Since "config/runtime.exs" is used by both Mix and releases, it cannot
>    invoke "Mix" directly. Therefore, for conditional environment compilation,
>    we will add a env/2 macro to Config that will be available for all
>    config files. For example, instead of a "config/runtime.prod.exs", one will
>    have to:
>
>    import Config
>
>    env :prod do
>      config :my_app, :secret_key, System.get_env!("SECRET_KEY")end
>
>
> One may argue that "config/runtime.exs" should eventually replace
> "config/config.exs" as the default file for application configuration. We
> will certainly evaluate this option in the future but it is important to
> take baby steps. And the first step is to support "config/runtime.exs". :)
> Implementation considerations
>
> This section covers implementation details. It is not part of the proposal
> per se. Although the feature is relatively small, it requires many
> improvements to Mix and the underlying config engine. The tasks are:
>
>    - Load config/runtime.exs inside Mix
>    - Copy config/runtime.exs inside escripts and load them when the
>    escript runs
>    - Copy config/runtime.exs inside releases (similar to
>    config/releases.exs)
>    - Add a feature to Config.Reader that allows a warning to be emitted
>    if an undesired module is used (for example, Mix)
>    - Add the env/2 macro to Config
>    - Raise if "import_config" is used in "config/runtime.exs" and
>    "config/releases.exs" - providing proper guidance to users
>
> One aspect to consider is exactly when runtime config should be loaded
> inside Mix. We need to choose between doing it after the "compile" task or
> before "app.start". The issue is that many projects have tasks that only
> need the application to be compiled but not started. For example, Ecto Repo
> management tasks or Phoenix routes tasks. Those tasks today simply run
> Mix.Task.run("compile"). However, if we were to introduce
> "config/runtime.exs" and load it before "app.start", those tasks will now
> run without "config/runtime.exs" and behave incorrectly.
>
> Therefore there is an argument to be made to load the runtime
> configuration right after the code is compiled - even though this is a bit
> unintuitive. The other option is to ask users to always run "app.start" as
> the entry point and pass the "--no-start" if they actually don't want to
> start their apps, which is also a bit counter intuitive. Unfortunately, the
> second option means projects will behave incorrectly until they are updated.
>
> --
> You received this message because you are subscribed to the Google Groups
> "elixir-lang-core" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to elixir-lang-core+unsubscr...@googlegroups.com.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/elixir-lang-core/CAGnRm4%2BS7nefgLCXCb9OWQGj9d9eE1TrDstq%3Dkm%3D%2B2a7iT9DAw%40mail.gmail.com
> <https://groups.google.com/d/msgid/elixir-lang-core/CAGnRm4%2BS7nefgLCXCb9OWQGj9d9eE1TrDstq%3Dkm%3D%2B2a7iT9DAw%40mail.gmail.com?utm_medium=email&utm_source=footer>
> .
>

-- 
You received this message because you are subscribed to the Google Groups 
"elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to elixir-lang-core+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/elixir-lang-core/CAEY66iOe713gMzAkOLQGYv9h%2B4%3DG8T2Oa8fMAR%2B04VkAykLZfQ%40mail.gmail.com.

Reply via email to