Dotenvy.source
source, go back to Dotenvy module for more information.
Specs
source(inputs :: input_source() | [input_source()], opts :: keyword()) :: {:ok, %{optional(String.t()) => String.t()}} | {:error, any()}
Like its Bash namesake command, source/2 accumulates values from the given input(s).
The accumulated values are stored via a side effect function to make them available
to the env!/2 amd env!/3 functions.
Think of source/2 as a merging operation which can accept maps (like Map.merge/2)
or paths to env files.
Inputs are processed from left to right so that values can be overridden by each
subsequent input. As with Map.merge/2, the right-most input takes precedence.
Options
:parsermodule that implementsDotenvy.parse/3callback. Default:Dotenvy.Parser:require_filesspecifies which of the givenfiles(if any) must be present. Whentrue, all the listed files must exist. Whenfalse, none of the listed files must exist. When some of the files are required and some are optional, provide a list specifying which files are required. If a file listed here is not included in the function'sfilesargument, it is ignored. Default:false:side_effectan arity 1 function called after the successful parsing of each of the given files. The default is an internal function that stores the values inside the application process dictionary. You may also set this value tofalseto disable a side-effect altogether, but this will effectively neutralize theenv!/2function.
Examples
The simplest implementation is to parse a single file by including its path:
iex> Dotenvy.source(".env")
{:ok, %{
"TIMEOUT" => "5000",
"DATABASE_URL" => "postgres://postgres:postgres@localhost/myapp",
# ...etc...
}
}More commonly, you will source multiple files (often based on the config env) and you will defer to pre-existing system variables. The most common pattern would do something like the following:
iex> Dotenvy.source([
"#{config_env()}.env",
"#{config_env()}.override.env",
System.get_env()
])In the above example, the prod.env, dev.env, and test.env files would be version-controlled,
but the *.override.env variants would be ignored, giving developers the ability to override
the values defined in the version-controlled files.
Give Precedence to System Envs! {: .warning}
Don't forget to include
System.get_env()as the final input tosource/2so that system environment variables take precedence over values sourced from.envfiles.When you run a shell command like
LOG_LEVEL=debug mix run, your expectation is probably that theLOG_LEVELvariable would be set todebug, overriding whatever may have been defined in your sourced.envfiles. Similarly, you may export env vars in your Bash profile. This doesn't happen automatically: you must explicitly includeSystem.get_env()as the final input tosource/2.
If your env files are making use of variable substitution based on system env vars,
e.g. ${PWD}, then you would need to include System.get_env() before your inputs.
For example, if your .env references the system HOME variable:
# .env
CACHE_DIR=${HOME}/cachethen your source/2 command would need to make the system env vars available
to the parser by including them as one of the inputs, e.g.
iex> Dotenvy.source([System.get_env(), ".env"])Including the System.get_env() before your files means that your files have final
say over the values, potentially overriding any pre-existing system env vars. In
some cases, you may wish to reference the system vars both before and after your own
.env files, e.g.
iex> Dotenvy.source([System.get_env(), ".env", System.get_env()])or you may wish to cherry-pick which variables you need to make available for variable substitution:
iex> Dotenvy.source([
%{"HOME" => System.get_env("HOME")},
".env",
System.get_env()
])This syntax favors explicitness so there is no confusion over what might have been "automagically" accumulated.