Hi,
There are a few writeups describing alternative config arrangements by a topic (or an OTP app):
- Configuring Phoenix apps: Two small adjustments with big effects | bitcrowd blog
- Tips for improving your Elixir configuration
I tried it out on a hobby project and found it more convenient than a configuration per environment, and therefore, I’d like to propose changing the default.
In my case, the whole of config.exs
was just five lines long
import Config
for config_file <- Path.wildcard(Path.join([File.cwd!(), "config", "compile_time", "*.exs"])) do
import_config(config_file)
end
The longest file in the compile_time
dir, the endpoint configuration was
import Config
config :my_app, MyAppWeb.Endpoint,
url: [host: "localhost"],
render_errors: [
formats: [html: MyAppWeb.ErrorHTML, json: MyAppWeb.ErrorJSON],
layout: false
],
pubsub_server: MyApp.PubSub,
live_view: [signing_salt: "tU71HhHQ"]
case config_env() do
:dev ->
config :my_app, MyAppWeb.Endpoint,
http: [ip: {127, 0, 0, 1}, port: 4001],
check_origin: false,
code_reloader: true,
debug_errors: true,
secret_key_base: "ztwTR1eV8qJmsKqFsAHcSu49my17XV5KULr6okIBXz9aRJEc4t8L2q3okO1L+ujE",
watchers: [
esbuild: {Esbuild, :install_and_run, [:default, ~w(--sourcemap=inline --watch)]},
tailwind: {Tailwind, :install_and_run, [:default, ~w(--watch)]}
]
config :my_app, MyAppWeb.Endpoint,
live_reload: [
patterns: [
~r"priv/static/.*(js|css|png|jpeg|jpg|gif|svg)$",
~r"priv/gettext/.*(po)$",
~r"lib/my_app_web/(controllers|live|components)/.*(ex|heex)$"
]
]
:test ->
config :my_app, MyAppWeb.Endpoint,
http: [ip: {127, 0, 0, 1}, port: 4002],
secret_key_base: "oKSvXrmhGIz0TZUqB5PzlzJREnh7I1+TMCyWlvEhCR/AgN3jec4kl2s+qTkZsqTP",
server: false
_ ->
:ok
end
I also split runtime.exs
under config/runtime,
which required a bit more ceremony:
- figure out where from configs should be read (source vs release
- copying runtime configs during release assembly.
# config/runtime.exs
import Config
topic_configs_path =
if config_env() == :prod do
root_dir = :code.priv_dir(:my_app)
release_version = Application.spec(:my_app, :vsn) |> to_string()
runtime_path =
[root_dir, "..", "..", "..", "releases", release_version, "runtime"]
|> Path.join()
|> Path.expand()
Path.join([runtime_path, "*.exs"])
else
Path.join([File.cwd!(), "config", "runtime", "*.exs"])
end
for config_file <- Path.wildcard(topic_configs_path) do
Code.require_file(config_file)
end
defmodule MyApp.MixProject do
def project do
[
releases: [
sesame: [
steps: [:assemble, ©_prod_runtime_configs/1],
]
]
]
end
defp copy_prod_runtime_configs(%Mix.Release{version_path: path} = release) do
release_config_dir = "runtime"
File.mkdir!(Path.join([path, release_config_dir]))
all_configs =
[File.cwd!(), "config", "runtime", "*.exs"]
|> Path.join()
|> Path.wildcard()
for config_file <- all_configs do
dst_path = Path.join([path, release_config_dir, Path.basename(config_file)])
Logger.info("COPYING #{config_file} as #{dst_path} ")
File.cp!(
config_file,
dst_path
)
end
release
end
Btw, if anyone have suggestions on better ways to get a releases
directory than
root_dir = :code.priv_dir(:my_app)
release_version = Application.spec(:my_app, :vsn) |> to_string()
runtime_path =
[root_dir, "..", "..", "..", "releases", release_version, "runtime"]
|> Path.join()
|> Path.expand()
that would be a very welcomed change