{"id":18725727,"url":"https://github.com/binaryage/env-config","last_synced_at":"2025-11-09T17:32:27.808Z","repository":{"id":57713250,"uuid":"71016777","full_name":"binaryage/env-config","owner":"binaryage","description":"A Clojure(Script) library for config map overrides via environmental variables","archived":false,"fork":false,"pushed_at":"2018-05-28T10:58:20.000Z","size":70,"stargazers_count":29,"open_issues_count":0,"forks_count":2,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-31T15:00:38.321Z","etag":null,"topics":["clojure","clojurescript","config","environment","library"],"latest_commit_sha":null,"homepage":"","language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/binaryage.png","metadata":{"files":{"readme":"readme.md","changelog":"changelog.md","contributing":null,"funding":null,"license":"license.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-10-15T22:23:42.000Z","updated_at":"2024-01-14T13:51:39.000Z","dependencies_parsed_at":"2022-09-06T02:10:56.399Z","dependency_job_id":null,"html_url":"https://github.com/binaryage/env-config","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/binaryage%2Fenv-config","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/binaryage%2Fenv-config/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/binaryage%2Fenv-config/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/binaryage%2Fenv-config/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/binaryage","download_url":"https://codeload.github.com/binaryage/env-config/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248361901,"owners_count":21091011,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["clojure","clojurescript","config","environment","library"],"created_at":"2024-11-07T14:11:37.253Z","updated_at":"2025-11-09T17:32:27.763Z","avatar_url":"https://github.com/binaryage.png","language":"Clojure","funding_links":[],"categories":[],"sub_categories":[],"readme":"# env-config\n\n[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](license.txt) \n[![Clojars Project](https://img.shields.io/clojars/v/binaryage/env-config.svg)](https://clojars.org/binaryage/env-config) \n[![Travis](https://img.shields.io/travis/binaryage/env-config.svg)](https://travis-ci.org/binaryage/env-config) \n\nThis is a Clojure(Script) library for enabling easy and consistent config map overrides via environment variables.\n\nThis is useful for library authors who want to add some flexibility how their libraries can be configured.\n\n## Intro\n\nUsually library configuration is achieved via a config map specifying keywords with individual config values.\nThis config map can be provided directly (e.g. passed via an api call), via a build configuration or by some other means. \nFor example in ClojureScript it could be passed via `:compiler \u003e :external-config`.\n\nSometimes for ad-hoc tweaks it would be preferable to be able to override config values \nby defining environment variables instead of touching build tool configuration (which is usually under source control).\n\nThis library helps you do that consistently:\n\n  1. we define a naming scheme how env variables map to config keys\n  2. we define a coercion protocol which determines how strings from env variables are converted to Clojure values\n  \n### Example\n\nWe want to support nested config maps. Let's look at example env variables with some nesting:\n \n    OOPS/COMPILER/MAX_ITERATIONS=10\n    OOPS/RUNTIME/DEBUG=true\n    OOPS/RUNTIME/WELCOME-MESSAGE=hello\n    OOPS/RUNTIME/DATA=~{:some (str \"data\" \" via\" \" read-string\")}\n    OOPS/RUNTIME/KEY=:some-keyword\n    OOPS/RUNTIME=something   \u003c= this will cause a naming conflic warning\n    \nA call to `(env-config.core/make-config \"oops\" (get-env-vars))` will return:\n \n    {:compiler {:max-iterations 10}\n     :runtime {:debug true\n               :welcome-message \"hello\"\n               :key :some-keyword\n               :data {:some \"data via read-string\"}}\n\nYou can observe several properties:\n\n  1. forward slashes are used as separators\n  2. to follow Clojure conventions, names are converted to lower-case and underscores turned into dashes\n  2. prefix \"oops\" is stripped because it was specified as a library prefix\n  3. values are naturally coerced to booleans, numbers, keywords, etc.\n  4. you can use full power of `read-string` if you prepend value with `~`\n\nAlso please note that existence of a variable name which is a prefix of another variable name will cause\nnaming conflict warning and will be ignored (`OOPS/RUNTIME` is prefix of `OOPS/RUNTIME/DEBUG` in our example above).\n\nSome shells [like Bash](http://stackoverflow.com/a/2821183/84283) do not allow slashes in variable names, you can use two underscores instead of a slash.\n\n### Integration\n\nYou probably want to merge the config coming from env-config over your standard config coming from a build tool.\n\nFor inspiration look at [the commit](https://github.com/binaryage/cljs-oops/commit/1a2a1794f59e47710b5c9e025a420ed25db4d4ed) \nwhich integrated env-config into cljs-oops library.\n\nPlease note that `make-config-with-logging` does not read environment directly. You have to pass it a map with variables.\n\nI used this simple implementation to get them:\n```\n(defn get-env-vars []\n  (-\u003e {}\n      (into (System/getenv))\n      (into (System/getProperties))))\n```\n\n### Logging\n\nI needed a way how to report issues with naming conflicts or for example problems when evaluting values via read-string.\n\nI didn't want to introduce another dependency so I decided to build internal subsystem for collecting \"reports\". It is up\nto you to inspect reports and communicate them somehow. \n\nFor convenience I have implemented a helper function which dynamically checks for availability of `clojure.tools.logging`\n and uses it for logging reports. \n \nTo get standard logging for free include dependency on `clojure.tools.logging` into your project and use `make-config-with-logging` \nto obtain your configs.\n\n### Coercion\n\nWe provide a [standard set of coercion handlers](https://github.com/binaryage/env-config/blob/master/src/lib/env_config/impl/coercers.clj). \nAs you can see from the `default-coercers` list the rules are pretty simple. You might want to provide your own handlers.\n \n#### Writing own coercion handlers\n\nCoercion handlers are asked in the order in which they were specified to `make-config`. \nEach handler is passed key path in the config map and raw string value coming from environment. \n\nThe handler should answer either: \n\n  1. `nil` which means \"I'm not interested, pass it to someone else\"\n  2. `:omit` which means \"ignore this value due to an error\"\n  3. a value wrapped in `Coerced` instance (to distinguish it from `nil` and `:omit`)\n  \nIf no handler was interested we use the raw value as-is.\n\nLook at the example of the most complex standard coercer:\n\n```clojure\n(defn code-coercer [path val]\n  (if (string-starts-with? val \"~\")\n    (let [code (.substring val 1)]\n      (try\n        (-\u003eCoerced (read-string code))\n        (catch Throwable e\n          (report/report-warning! (str \"unable to read-string from \" (make-var-description (meta path)) \", \"\n                                       \"attempted to eval code: '\" code \"', \"\n                                       \"got problem: \" (.getMessage e) \".\"))\n          :omit)))))\n```\n\nPlease note that the `path` vector has attached some metadata with original raw values which may be handy when \nreporting warnings/errors. You should use `env-config.impl.report` functionality to report errors in a standard way.\n\n### FAQ\n\n\u003e My shell does not support variable names with slashes. What now?\n\nYou can use two underscores instead of a slash. Or alternatively you might want to use `env` command to launch your command with\ndefined variables without shell naming restrictions. See [this stack overflow answer](http://unix.stackexchange.com/a/93533/188074).\n \nFor example:\n \n    env OOPS/COMPILER/MAX_ITERATIONS=10 OOPS/RUNTIME/DEBUG=true command\n \nI personally use [fish shell](https://fishshell.com) and prefer slashes to visually communicate the nested config structure.\n\n\u003e Can this be used in self-hosted mode?\n\nYes, thanks to [arichiardi](https://github.com/arichiardi). Since v0.2.0 you can use this library to configure scripts running\n under [Planck](https://github.com/mfikes/planck) or [Lumo](https://github.com/anmonteiro/lumo).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbinaryage%2Fenv-config","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbinaryage%2Fenv-config","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbinaryage%2Fenv-config/lists"}