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:
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.
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". :)
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:
Config.Reader
that allows a warning to be emitted if an undesired module is used (for example, Mix)env/2
macro to Config
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-co...@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.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/d92115bd-a9fc-4236-b041-a80aa5fc5610%40www.fastmail.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/CAGnRm4JR65fkD_uyOKup1NqcNcJGywt8xe6ZDC-tdXtnvV_M4w%40mail.gmail.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/CACzMe7aMLEKTMXaGcENBjSFuei69V9g-VOqNNuwAqmWyo_cq2w%40mail.gmail.com.
Application.get_env/3
in the module body.--