lessless

lessless

Hi,

There are a few writeups describing alternative config arrangements by a topic (or an OTP app):

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, &copy_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

Showing Posts 1 to 7

D4no0

D4no0

The question is, how do you plan on enforcing correct app configuration, do you make any checks whether the correct application is configured in those .exs files?

lessless

lessless OP

How do you mean?

LostKobrakai

LostKobrakai

I‘m not a fan tbh. I don‘t want to look through N files just to figure out how my project is configured for e.g. testing or development. There might even be M files out of the N, which don‘t even have any config for a specific env. But I‘d still need to look at those just to notice that.

lessless

lessless OP

What’s the use case for looking into how a “project” is configured vs. peeking into a configuration for a specific app, an ecto repo or a phx endpoint?

LostKobrakai

LostKobrakai

I‘ve worked on a project with quite a few dependencies and nerves (multiple MIX_TARGETs) involved. Knowing how things are configured for a certain place code runs in is more important than how individual parts might be configured. Often configuration on app A kinda correlates with configuration on app B for a certain target and you loose that relation quite easily if it is scattered around a handful of individual config files, mixed in an even larger set of existing config files.

It would be a pain to build a mental model of how target A is configured over target B if you need to look through 10-30 individual files, parse all their conditionals and combine in your mind how all the individual pieces are configured to make the larger whole work. I prefer have less branches and larger chunks of „all this happens together in this branch“ over branching by individual applications and then branching those individually by all already existing criteria.

lessless

lessless OP

That project sounds like an outlier to me.

dimitarvp

dimitarvp

If you have already made up your mind then use your way, nobody is stopping you. No need to argue which case is more representative, plus you are biased in this case so it’s not an honest discussion.

If you really want to argue statistics and representation: I worked on no less than 25 Elixir projects and all of them used per-env config files, and bigger teams also employed sorting configs by app name alphabetically which made them trivial to browse and modify.

— All posts loaded —

Where Next? Top

Trending in Proposals: Ideas Top

woylie
We are seeing a lot of warning logs like this: navigate event to "https://someurl" failed because you are redirecting across live_sessio...
New

Other Trending Topics Top

mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Dmk
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
webofbits
With AI doing more of the implementation work, I’ve been wondering how much coding I should deliberately keep doing myself. My main conc...
#ai
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews