Deployment configurations are loaded by cascading overrides. Overrides are inputed in order of:
- Configuration files placed in a configurable folder location
- ENV variable mapping
- Function calls
The syntax for accessing configurations:
using Configs
password = getconfig("database.credentials.password")
username = getconfig("database.credentials.username")
# OR
credentials = getconfig("database.credentials")
username = credentials.username
password = credentials.passwordAccessing non-existent configurations will throw an error, so:
using Configs
if hasconfig("optional.setting")
setting = getconfig("optional.setting")
endSetting configurations from external sources:
using Configs
port = myexternalcall(...)
setconfig!("database.connection.port", port)Immutability:
After the first call to getconfig or hasconfig, the configuration is immutable. Thus, you can not call setconfig! after calling getconfig or hasconfig. It will throw an error.
Conversely stated, you must complete all your setconfig! calls before accessing with getconfig or hasconfig.
$> cd my/project/rootdir
$> julia --project=.
julia> ]add ConfigsCreate a configs directory in the project root.
The default configs folder is expected to be at <project rootdir>/configs.
Configs will throw an error if no folder is found at the default path or a custom path is not explicitly provided.
$> cd my/project/rootdir
$> mkdir configsOR..
$> cd my/project/rootdir
$> export DEPLOYMENT_KEY=MY_ENV
$> export CONFIGS_DIRECTORY="/opt/configs/myproject"
$> julia --project=. src/project.jlOR...
using Configs
initconfig(; deployment_key="MY_ENV", configs_directory="customdir")WHERE:
CONFIGS_DIRECTORY / configs_directory
defines a custom path to the configs directory. This can be input as absolute path or relative to the project root. The default is <project root>/configs
DEPLOYMENT_KEY / deployment_key
defines which ENV key you intend to use to state the deployment environment [development, staging, production, etc...]. The default is ENV["DEPLOYMENT"].
Then manipulate and access your configs:
using Configs
value = myexternalcall(...)
setconfig!("path.to.new", value)
setconfig!("path.to.override", value)
newvalue = getconfig("path.to.new")
overriddenvalue = getconfig("path.to.override")
connection = getconfig("database.connection")
port = connection.port
# OR
conf = getconfig()
connection = conf.database.connection
port = connection.port
url = connection.url
if hasconfig("optional.setting")
option = getconfig("optional.setting")
end
# After the first call to getconfig or hasconfig, configs are immutable, so:
setconfig!("database.connection.port", 8000) # Throws an error if called hereConfigurations can be independantly defined in any of the following file formats:
- JSON
.json - YAML
.yml - Julia
.jl
These provide cascading overrides in the order shown below:
Define public configs. This is suitable for eg. storing in a public code repository.
timestamp: 2020-09-03T14:18:45.633
database:
connection:
url: "http://localhost"
port: 3600
credentials:
username: "guest"
password: "guestuserdefault"
otherstuff:
defaultmessage: "Hello new user"Configs does not support multi-doc yaml files.
Typically, would be:
- development.jl
- staging.jl
- production.jl
- testing.jl
Define semi private, deployment specific overrides. This would typically have a .gitignore exclusion, or be stored in a private repository only.
The file is named in lowercase to correspond with any ENV["DEPLOYMENT"] found at runtime. Thus, running:
$> export DEPLOYMENT=PrOdUcTiOn
$> julia --project=. src/myproject.jlwould merge the configuration defined in production.jl
(
timestamp = now(),
database = (
connection = (
url = "https://secureserver.me/staging",
port = 3601,
),
credentials = (
username = "stagingadmin",
pasword = "",
)
)
)For .jl configuration files, any valid Julia collections [ Array, Tuple, NamedTuple, Dict ] may be used in any combination.
Valid Dates methods may be used in the configuration file.
Define private overrides. This maps ENV variables to configuration variables.
{
"database": {
"credentials": {
"password": "DATABASE_PASSWORD"
}
}
}Private variables are thus passed in explicitly by, for example, defining the environment variable in BASH.
$> export DATABASE_PASSWORD=mysupersecretpasword
$> julia --project=. src/myproject.jlusing Configs
password = getconfig("database.credentials.password")
# password === "mysupersecretpasword"setconfig! has flexible input parameter types:
- Bool, Number, String
- JSON String
- Dict, Array
- Tuple, NamedTuple
(with any depth / combination of nesting)
using Configs
# Thus
setconfig!("project", """{
"credentials": {
"username": "user",
"password": "userpass"
},
"pages": [1, 2, 3]
}""")
# Is the same as
setconfig!("project", (
credentials= (
username="user",
password="userpass",
),
pages=(1,2,3)
))
# Is the same as
setconfig!("project", Dict(
:credentials => Dict(
:username => "user",
:password => "userpass"
),
:pages => [1, 2, 3]
))
# Is the same as
setconfig!("project.credentials.username", "user")
setconfig!("project.credentials.password", "userpass")
setconfig!("project.pages", [1, 2, 3])This is a deployment methodology cloned from the excellent node.js config package.