Live data from Hacker News

Use TOML for `.env` Files?

snarky.ca

61–64 of 64 posts

Re: Use TOML for `.env` Files?

#61

Earlier quoted context omitted.

One way to solve this is to be able to import into the .env file: import values from local/or/public/private/url ...values When reading the file, the environment variables will be obtained from the URL and populate the environment. This is what I had in mind when designing the import functionality for deon [1]. Being able to import also makes it easy to have a .base, a .production, a .local setup, and combine them ac…

Neat solution, sqlite gives you much more though. What happens with the .env files with inheritance eg. env.linux overridden by env.linux.local? You need one url per permutation, or call them in the correct order, now you have a config for your config. What about if you require multiple local configurations for eg. testing inside and outside docker? What if you have several developers working on the same codebase wit…

Not sure what you mean by "config for your config", seems this is what you want to obtain with an SQLite: someone would still have to manage those databases. The way I use deon for inheritance is with one directory per project, with one file to be read at startup.

file tree:

  environment/
    .env.base.deon
    .env.local.deon
    .env.production.deon
.env.local.deon file:

  import base from ./.env.base.deon

  {
    ...#base
    NEW_VALUE foo
    OVERWRITING_VALUE boo
  }
Then the node process will be started with:

  deon environment ./environment/.env.base.deon -- node build/index.js
If you want to take it a step further you could import values from a URL, even using a token from an environment variable for authentication, such as:

  import values from ./.env.base.deon
  import overwrites from https://deon-data.example with #$DEON_TOKEN

  {
    ...#values
    ...#overwrites
  }
Not sure what's the problem with several developers on the same codebase. Aren't they using their own, individual machines? Each developer can have their own environment file as they wish, or their own environment DEON_TOKEN.

If the static data changes with the environment then it's not that static, or I don't know what you mean. If I were using deon, I would split the .base file into two or more, and import accordingly.

Not sure what you mean by "including parsing quotes and handling filepaths in a uniform fashion". Have you found a bug in deon?

Anyhow, if your use case is too complex, of course you will need special tooling, deon is more of a research for my own requirements and still needs to be written in a compiled language, now it is only for the JavaScript ecosystem.

Re: Use TOML for `.env` Files?

#62
post #26

Earlier quoted context omitted.

I’ve always liked yaml but I feel the world is moving towards toml. Keen to see more of toml issues to build a fair comparison

I really like the Gura format ( https://github.com/gura-conf/gura ). Seems to combine the best of yaml, json, and toml for the use case of human created configuration files.

I've also seen the Dhall configuration language (https://dhall-lang.org/) mentioned.

Re: Use TOML for `.env` Files?

#63

I find the Twelve-Factor App's design to use environment variable for configuration ( https://12factor.net/config ) unintuitive and perhaps a bad design choice. I believe environment variables are bad for the same reasons global variables are bad. Pros listed for env vars include not committing them to the repo and not encouraging grouping them together as environments such as dev, staging or prod. I don't agree that…

> I believe environment variables are bad for the same reasons global variables are bad. Global variables are bad, but environment variables are actually more like dynamic variables: http://www.chriswarbo.net/blog/2021-04-08-env_vars.html Dynamic scope is useful for things the caller knows better than the implementor, e.g. configuration, credentials, etc. > Another alternative to consider for both env vars and config…

Hi Chris! Thanks for the link, it's an enlightening read, I learned about dynamic variable scopes today.

It did make me change my mind partially about "environment variables are bad for the same reasons global variables are bad." I concur that environment variables are more like constants than mutable globals, even in my language of choice, Python. If you only use them at process boundaries, they is fine, I admit using them that way too:

  parser = argparse.ArgumentParser()
  parser.add_argument("--foo", default=os.environ.get("FOO"))
If they are used at a boundary within a process, however:

  def foo_function():
    return foo_implementation(os.environ.get("FOO"))
Then testing foo_function() becomes a problem because os.environ isn't dynamically scoped within the process. Each test case can set os.environ["FOO"], but then the tests have mutable globals now even if the app doesn't. I know three ways to solve this, each with it's pros and cons:

- 1. Treat the script as a black box, only test the script as a whole -- or not at all. How env vars are used internally doesn't matter. Works well for smaller scripts.

- 2. Keep the code as is, test functions individually by setting and resetting the environment variables in each test setup and teardown. Don't run tests in parallel.

- 3. Push all environment variable usage to process boundaries and make all inner functions pure functions that are only affected by their explicit input parameters. If needed, I even make standard in/out/error, logger instances and other similar globals explicit parameters or class members. Requires more boilerplate, works better for more complex projects. Testing any behavior becomes easier.

I prefer to go with option #1 or #, as #2 feels dirty and makes my test cases smell of workarounds. #3 could look such with few details omitted:

  parser = argparse.ArgumentParser()
  parser.add_argument("--foo", default=os.environ.get("FOO"))
  args = parser.parse_args()

  def foo_function(foo_value):
    return foo_implementation(foo_value)

  def main():
    ...
    foo_result = foo_function(foo_value=args.foo)
    ...

  ...
  
To agree with you, it would be great if the ex-globals-turned-parameters I'm passing around during option #3 would be dynamically scoped. Not shown in the example above, but imagine that instead of printing to sys.stderr, functions receive an stderr: io.IOBase parameter or a custom dataclass that contains such a field. The point is to get rid of mutable global state in all cases.

To disagree with you, I think the correct term for "things the caller knows better than the implementor" are parameters. I'm not sure there's a benefit to preferring dynamic scope for parameters when most languages default to lexical scope.

About your last too points I somewhat agree and somewhat still disagree: "CLI args are usually passed around explicitly" -- I think this is a pro, not a con. Further, CLI arguments are strictly more flexible then environment variables, most argument parsing libraries support key-value parsing in addition to boolean flags and lists.

However, regarding your overall point that I understand as: environment variables used at process bounderies behave like dynamically scoped variables and these are fine. I agree, as long as they stay at process boundaries.

Re: Use TOML for `.env` Files?

#64

Earlier quoted context omitted.

> I believe environment variables are bad for the same reasons global variables are bad. Global variables are bad, but environment variables are actually more like dynamic variables: http://www.chriswarbo.net/blog/2021-04-08-env_vars.html Dynamic scope is useful for things the caller knows better than the implementor, e.g. configuration, credentials, etc. > Another alternative to consider for both env vars and config…

Hi Chris! Thanks for the link, it's an enlightening read, I learned about dynamic variable scopes today. It did make me change my mind partially about "environment variables are bad for the same reasons global variables are bad." I concur that environment variables are more like constants than mutable globals, even in my language of choice, Python. If you only use them at process boundaries, they is fine, I admit usi…

> "CLI args are usually passed around explicitly" -- I think this is a pro, not a con.

Sure; I never said it's a con. They have different characteristics, and are both useful in certain situations :)

> I think the correct term for "things the caller knows better than the implementor" are parameters.

True; that's also the name Racket gives to dynamically-scoped variables https://docs.racket-lang.org/guide/parameterize.html

In fact, Racket uses a parameter (dynamically-scoped variable) to store the environment. This is actually slightly annoying, since the parameter is one big hashmap of all the env vars; but I usually want to override them individually. One of my Racket projects actually defines a helper function to override individual env vars makes a copies all the other environment ( made a are contained in a parameterhttps://github.com/Warbo/theory-exploration-benchmarks/blob/...

Post reply on HN