dotenvy
dotenvy copied to clipboard
Feature request: Allow opt-out of `System.fetch_env/1` when variable not found
Hello. I ran into an issue which took me a while to debug. Note that I am not using the system environment variables anywhere in my code. Just dotenv files.
I have this line in my config/runtime.exs:
postgres_port = env!("POSTGRES_PORT", :integer, 5432)
None of my dotenv files define a default value for this, so the default should be used. Particularly since I'm not using the system environment variables (as previously mentioned).
The problem arises when we get to my containerized CI environment.
So I'm using GitLab CI, and there are a couple of "services" (other containers) involved in my CI tests. One of them is Postgres.
So when I run the tests in CI, I get this error:
** (RuntimeError) Error converting variable POSTGRES_PORT to integer: Unparsable
(dotenvy 0.8.0) lib/dotenvy/transformer.ex:182: Dotenvy.Transformer.to!/2
(dotenvy 0.8.0) lib/dotenvy.ex:111: Dotenvy.env!/3
/builds/user/my_app/config/runtime.exs:23: (file)
(stdlib 5.2.3) erl_eval.erl:750: :erl_eval.do_apply/7
(stdlib 5.2.3) erl_eval.erl:1026: :erl_eval.expr_list/7
(stdlib 5.2.3) erl_eval.erl:292: :erl_eval.expr/6
(stdlib 5.2.3) erl_eval.erl:282: :erl_eval.expr/6
Which is strange, because I haven't defined the value in any of my dotenv files. So it should be falling back to the default value I showed in my first code snippet at the top of this Issue (i.e. 5432).
The problem is that somewhere in my CI environment, the POSTGRES_PORT system environment variable is being declared. On top of that, the container defines POSTGRES_PORT as tcp://172.17.0.3:5432. This value will (unsurprisingly) not cast to integer, leading to the "Unparsable" error seen above.
In lib/dotenvy.ex, there is this code which automatically falls back to using the system environment:
defp fetch_var(variable) do
:dotenvy_vars
|> Process.get(%{})
|> Map.fetch(variable)
|> case do
{:ok, value} -> {:ok, value}
:error -> System.fetch_env(variable)
end
end
But this creates a very confusing scenario (as I have discovered) since somewhere, the system environment variable may be defined with a given value, for reasons mostly beyond my control.
If my understanding is correct, and there is no means to avoid this issue (I was not able to find one), then perhaps there could be an Application config value to override this feature (or bug, in my situation)? It could even be opt-in for backwards compatibility. For example:
defp fetch_var(variable) do
fall_back_to_system_env? = Application.get_env(:dotenvy, :fall_back_to_system_env, true)
:dotenvy_vars
|> Process.get(%{})
|> Map.fetch(variable)
|> case do
{:ok, value} -> {:ok, value}
:error -> if fall_back_to_system_env?, do: System.fetch_env(variable), else: :error
end
end
Perhaps developers could also pick-and-choose which variables they want to override with system environment variables. There could be (as options for this value) the following potential options: true, false, {:only, ["VAR_A", "VAR_B"]}, {:exclude, ["POSTGRES_HOST", "VAR_B"]}.
Please let me know your thoughts. I'm willing to put together a proof-of-concept (when I get the time) and make a pull request if you want.
Thanks for making this excellent library.