{"id":26164717,"url":"https://github.com/optum/envvars","last_synced_at":"2026-05-20T16:39:19.575Z","repository":{"id":279646947,"uuid":"917752020","full_name":"Optum/envvars","owner":"Optum","description":"A library to provide the heavy-lifting for implementing your own environment variable management system.","archived":false,"fork":false,"pushed_at":"2025-02-26T16:36:24.000Z","size":92,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-02-26T17:27:19.249Z","etag":null,"topics":["environment-variables","java"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Optum.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2025-01-16T15:26:32.000Z","updated_at":"2025-02-26T16:26:00.000Z","dependencies_parsed_at":"2025-02-26T17:27:54.887Z","dependency_job_id":"c57f42c4-c0f0-40af-95d2-770ffa20dfbf","html_url":"https://github.com/Optum/envvars","commit_stats":null,"previous_names":["optum/envvars"],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Optum%2Fenvvars","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Optum%2Fenvvars/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Optum%2Fenvvars/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Optum%2Fenvvars/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Optum","download_url":"https://codeload.github.com/Optum/envvars/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243061003,"owners_count":20229903,"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":["environment-variables","java"],"created_at":"2025-03-11T15:39:54.708Z","updated_at":"2026-05-20T16:39:19.554Z","avatar_url":"https://github.com/Optum.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv id=\"top\"\u003e\u003c/div\u003e\n\n# envvars\n\nThe **envvars** library is meant to provide the heavy-lifting for implementing your own environment\nvariable management system.\n\nIt is based on the philosophy of a separation of duties between \"configuration producers\" and \"configuration consumers\" with consideration and support for varying contexts and enforced constraints.\n\n\u003e Where your code runs is complicated. Your configuration shouldn't be.\n\n## Motivating Use Case\nConfiguration typically varies.  It varies by environment, it varies by release, it varies by\nplatform type, it varies by lots of things.  Different people know how different parts vary.  What\nwould be nice is to have those different variation domains isolated and managed independently; then somehow blended together into a complete configuration.\n\nThe **envvars** library provides features to help you do that.\n\n## Envvars Doesn't Actually Create Environment Variables\n\u003e The **envvars** library... *is a library*.\n\nIt doesn't do anything on its own right out of the box.  It is intended to be the environment variable kernel of a larger application configuration management system.\n\nIt is meant to be embedded in your CICD pipeline.  Are you creating property files? deployment configurations? config maps? distributed maps?  Well, these binding are all great, but **envvars** does not natively support any of them.\n\nThat's the work left for you to do.\n\n## Library Module Design\nThe core **envvars** library module (envvars-lib) is only dependent on com.optum.templ:templ-lib.  This should make using envvars-lib easy to adopt into any project without dealing with \\[too many\\] transitive dependencies or security vulnerabilities.\n\nBut envvars-lib doesn't implement file processing/parsing.  For that you can choose JSON or YAML, and the transitive dependencies of Jackson or Snake respectively.  The most complete support is provided by the YAML library, as stand-alone **envvars** files are implemented in the YAML library.  But JSON is also supported, to a limited degree.\n\n\u003e JSON isn't perfect.  Neither is YAML.  But YAML has comments.\n\nThe envvars-cli includes sample code to kickstart your project.\n\n## Notes About Types\nThe **envvars** library is untyped, or more specifically: all keys are considered Strings (not surprising) and also all values are considered Strings.\n\nWhen using YAML with **envvars**, be careful and remember that YAML is typed.  YAML infers types in ways that may not be obvious.  When in doubt (or always to be safe) put your YAML values in quotes so YAML treats them as Strings!  On the plus side, **envvars** has no problem being passed multi-line Strings from YAML.  It may have dire and unexpected consequences when you export data *out* of **envvars**.  But the **envvars** engine couldn't care less.\n\n## Key Features\n\n### Producers and Consumers\nA piece of configuration is typically produced by one group (a DBA says \"The transaction records database in the performance environment is hosted on transrecdb.perf.ourcompany.com port 3306\") and consumed by another group (an application developer says \"I need the host and port of the transaction records database.\")\n\nIdeally we want a decoupling between the configuration producer and the consumer.\n\nThe philosophy and terminology in envvars for this decoupling is that \"producers define\" and \"consumers inject.\"\n\n\u003eThe \"meeting in the middle\" is effectively done with `environment variables`; not shell environment variables, but rather variables in the **envvars** engine that look and behave kind of like shell environment variables - so let's just call them environment variables and remember the \"environment\" they are in is the **envvars** engine, not the container runtime or operating system or shell.\n\nSo conceptually:\n#### DBA Produces\n```\ndefine:\n  HOST: \"transrecdb.perf.ourcompany.com\"\n  PORT: \"3306\"\n```\n#### Developer Consumes\n```\ninject:\n  - HOST\n  - PORT\n```\n\n### Templates\nA simple templating language is supported (com.optum.templ) that is tuned to typical environment variable composition use cases.\n\nSuppose the developer wants to inject a URL instead of HOST and PORT?  Our configuration changes to this:\n\n#### DBA Produces\n```\ndefine:\n  HOST: \"transrecdb.perf.ourcompany.com\"\n  PORT: \"3306\"\n  URL: \"jdbc:mysql://{{HOST}}:{{PORT}}/db\"\n```\n#### Developer Consumes\n```\ninject:\n  - URL\n```\n\nThis becomes more valuable when you realize you can separate the \"meta-definition\" of URL (which is universal) from the \"instance-definitions\" of HOST and PORT (which varies by environment).\n\nConsider this scenario where three environments are defined:\n\n#### DBA Produces\n```\nenvironments:\n  default:\n    define:\n      URL: \"jdbc:mysql://{{HOST}}:{{PORT}}/db\"\n  development:\n    define:\n      HOST: \"transrecdb.dev.ourcompany.com\"\n      PORT: \"3306\"\n  performance:\n    define:\n      HOST: \"transrecdb.perf.ourcompany.com\"\n      PORT: \"3306\"\n  production:\n    define:\n      HOST: \"transrecdb.ourcompany.com\"\n      PORT: \"3306\"\n  \n```\n#### Developer Consumes\n```\ninject:\n  - URL\n```\n\nUpon further consideration, we realize it shouldn't be the DBA who needs to understand that our application is using JDBC and needs our URL formatted in that way.  Let's refactor the URL meta-definition into the domain of the developer:\n\n#### DBA Produces\n```\nenvironments:\n  development:\n    define:\n      HOST: \"transrecdb.dev.ourcompany.com\"\n      PORT: \"3306\"\n  performance:\n    define:\n      HOST: \"transrecdb.perf.ourcompany.com\"\n      PORT: \"3306\"\n  production:\n    define:\n      HOST: \"transrecdb.ourcompany.com\"\n      PORT: \"3306\"\n  \n```\n#### Developer Consumes\n```\nenvironments:\n  default:\n    define:\n      URL: \"jdbc:mysql://{{HOST}}:{{PORT}}/db\"\n\ninject:\n  - URL\n```\n\nNow what happens when the DBA decides to migrate our two non-prod databases onto the same host but different ports?  The DBA updates the defines, and the developers don't care:\n\n#### DBA Produces\n```\nenvironments:\n  development:\n    define:\n      HOST: \"transrecdb.nonprod.ourcompany.com\"\n      PORT: \"33060\"\n  performance:\n    define:\n      HOST: \"transrecdb.nonprod.ourcompany.com\"\n      PORT: \"33070\"\n  production:\n    define:\n      HOST: \"transrecdb.ourcompany.com\"\n      PORT: \"3306\"\n  \n```\n#### Developer Consumes\n```\nenvironments:\n  default:\n    define:\n      URL: \"jdbc:mysql://{{HOST}}:{{PORT}}/db\"\n\ninject:\n  - URL\n```\n\n### Template handling of \"missing\" data.\n\nThe com.optum.templ library has a convenient feature for handling construction delimiters in the case of \"missing\" data.\n\nThe JDBC MySql driver will use port 3306 as default if it is not specified.  Our DBA wants to be efficient and stop specifying the port when it is the default port.\n\n*But this breaks our URL template!*\n\nLibrary templ to the rescue!\n\nSpecifying `{{:PORT}}` instead of `:{{PORT}}` will cause the `:` to only be used if the value of `PORT` is not blank.\n\nSo with these definitions:\n#### DBA Produces\n```\nenvironments:\n  development:\n    define:\n      HOST: \"transrecdb.nonprod.ourcompany.com\"\n      PORT: \"33060\"\n  performance:\n    define:\n      HOST: \"transrecdb.nonprod.ourcompany.com\"\n      PORT: \"33070\"\n  production:\n    define:\n      HOST: \"transrecdb.ourcompany.com\"\n  \n```\n#### Developer Consumes\n```\nenvironments:\n  default:\n    define:\n      BADURL: \"jdbc:mysql://{{HOST}}:{{PORT}}/db\"\n      GOODURL: \"jdbc:mysql://{{HOST}}{{:PORT}}/db\"\n      \ninject:\n  - BADURL\n  - GOODURL\n```\n\nThe values of `BADURL` and `GOODURL` in development and performance will be the same.\nBut in production:\n```\nBADURL=jdbc:mysql://transrecdb.nonprod.ourcompany.com:\nGOODURL=jdbc:mysql://transrecdb.nonprod.ourcompany.com\n```\n\n### Templates help make optional data simple\nDevelopers might want connection properties\n\n#### DBA Produces\n```\nenvironments:\n  development:\n    define:\n      HOST: \"transrecdb.nonprod.ourcompany.com\"\n      PORT: \"33060\"\n  performance:\n    define:\n      HOST: \"transrecdb.nonprod.ourcompany.com\"\n      PORT: \"33070\"\n  production:\n    define:\n      HOST: \"transrecdb.ourcompany.com\"\n  \n```\n#### Developer Consumes\n```\nenvironments:\n  default:\n    define:\n      URL: \"jdbc:mysql://{{HOST}}{{:PORT}}/db{{?PROPERTIES}}\"\n  production:\n    define:\n      PROPERTIES: \"connectTimeout=3600\u0026socketTimeout=36000\"    \n\ninject:\n  - URL\n```\n\nOur results will be:\n\nIn development:\n```\nURL=jdbc:mysql://transrecdb.nonprod.ourcompany.com:33060\n```\n\nIn performance:\n```\nURL=jdbc:mysql://transrecdb.nonprod.ourcompany.com:33070\n```\n\nIn production:\n```\nURL=jdbc:mysql://transrecdb.ourcompany.com?connectTimeout=3600\u0026socketTimeout=36000\n```\n\nNow we see how both DBAs and Developers are able to independently manage specific pieces of data that get merged together using consistent, universal template definitions.\n\n## Nested Template Processing - Indirection\nThe concept of *which* captures the power of indirection.  The selection of *which* can be done by either the producer of consumer - with pros and cons likely in each case; so use indirection with the consideration \"who should be deciding which?\"\n\nIndirection works by a feature in **templ** where template variables themselves are built from templates.\n\nSetting up an example takes a few extra pieces (the layer of indirection); but this should still be simple enough to follow.\n\n\u003e I have frequently adopted a `T_` prefix for variables that are intended to be used exclusively for template processing and not expected to be used directly.  This is especially useful when paired with schema restrictions on `T_` variables.  You may or may not find value in similar patterns.\n\n#### Network Admin Produces\n```\nenvironments:\n  default:\n    define:\n      T_SERVICE_LDAP_EAST: \"ldap.east.ourcompany.com\"\n      T_SERVICE_SMTP_EAST: \"smtp.east.ourcompany.com\"\n      T_SERVICE_LDAP_WEST: \"ldap.west.ourcompany.com\"\n      T_SERVICE_SMTP_WEST: \"smtp.west.ourcompany.com\"\n      SERVICE_LDAP: \"{{T_SERVICE_LDAP_{{^T_ZONE}}}}\"\n      SERVICE_SMTP: \"{{T_SERVICE_SMTP_{{^T_ZONE}}}}\"\n  development:\n    define:\n      T_ZONE: \"east\"\n  performance:\n    define:\n      T_ZONE: \"east\"\n  production:\n    define:\n      T_ZONE: \"west\"\n```\n#### Developer Consumes\n```\ninject:\n  - SERVICE_LDAP\n  - SERVICE_SMTP\n```\n\n\u003e Note the use of the `{{^VAR}}` **templ** syntax to upper-case the templated variable, changing T_ZONE \"east\" to \"EAST\".\n\nOne critically-important feature in this pattern is the atomicity of selecting a ZONE.  By an environment \"Selecting Which\" using a single variable (T_ZONE) we have the power to atomically configure multiple variables.  There is no opportunity for a half-configured environment - which seems to happen over and over with copy/paste/edit bugs each time a new environment is created: one of the variables doesn't get updated.  With nested template indirection you guarantee all of the variables based on T_ZONE are configured correctly.\n\n## Inject Sets\nWhat if you want to make sure an application gets all of the variables from some necessary set of variables?  Inject Sets provides support for this pattern.\n\nLet's adjust the above example to show injection sets at work:\n\n#### Network Admin Produces\n```\nenvironments:\n  default:\n    define:\n      T_SERVICE_LDAP_EAST: \"ldap.east.ourcompany.com\"\n      T_SERVICE_SMTP_EAST: \"smtp.east.ourcompany.com\"\n      T_SERVICE_LDAP_WEST: \"ldap.west.ourcompany.com\"\n      T_SERVICE_SMTP_WEST: \"smtp.west.ourcompany.com\"\n      SERVICE_LDAP: \"{{T_SERVICE_LDAP_{{^T_ZONE}}}}\"\n      SERVICE_SMTP: \"{{T_SERVICE_SMTP_{{^T_ZONE}}}}\"\n  development:\n    define:\n      T_ZONE: \"east\"\n  performance:\n    define:\n      T_ZONE: \"east\"\n  production:\n    define:\n      T_ZONE: \"west\"\n\ninject_sets:\n  Services:\n    - SERVICE_LDAP\n    - SERVICE_SMTP\n```\n#### Developer Consumes\n```\ninject:\n  - \"*Services\"\n```\n\nNow we see that the developer simply says \"Give me all the variables defined in the Services set\" and whatever they are, they get.\n\n## Define Sets and Declare\nWe saw that indirection was fantastic to guarantee all of the configuration pieces of an environment correctly \"map\" together.  But what do you do if they don't all switch the same, but you want to make sure they are all defined correctly?\n\nDefine Sets (and Declare) makes this possible.\n\nAgain we will adjust the example above, this time constructing the definitions so that our network administator can independently adjust the location of our LDAP and SMTP servers, but in a way that they can't forget one or the other.\n\n#### Network Admin Produces\n```\ndefine_sets:\n  EnvironmentServices:\n    SERVICE_LDAP: \"ldap.{{$1}}.ourcompany.com\"\n    SERVICE_SMTP: \"smtp.{{$2}}.ourcompany.com\"\n\nenvironments:\n  development:\n    declare:\n      - \"EnvironmentServices\u003c east , east \u003e\"\n  performance:\n    declare:\n      - \"EnvironmentServices\u003c east , west \u003e\"\n  production:\n    declare:\n      - \"EnvironmentServices\u003c west , west \u003e\"\n\ninject_sets:\n  Services:\n    - SERVICE_LDAP\n    - SERVICE_SMTP\n```\n#### Developer Consumes\n```\ninject:\n  - \"*Services\"\n```\n\nWe now know that the performace environment is split zoned, with LDAP pointing to the east server, and SMTP pointing to the west server.  This pattern avoids intermediate variables, but requires that the arguments are syntactically correct - which may be too much to ask.\n\nA hybrid system is possible using both template indirection and define sets.  It seems convoluted and obtuse for these simple examples; but trust me that it can be a lifesaver with complicated configurations.\n\n#### Network Admin Produces\n```\ndefine_sets:\n  EnvironmentServices:\n    T_ZONE_A: \"{{$1}}\"\n    T_ZONE_B: \"{{$2}}\"\n\nenvironments:\n  default:\n    define:\n      T_SERVICE_LDAP_EAST: \"ldap.east.ourcompany.com\"\n      T_SERVICE_SMTP_EAST: \"smtp.east.ourcompany.com\"\n      T_SERVICE_LDAP_WEST: \"ldap.west.ourcompany.com\"\n      T_SERVICE_SMTP_WEST: \"smtp.west.ourcompany.com\"\n      SERVICE_LDAP: \"{{T_SERVICE_LDAP_{{^T_ZONE_A}}}}\"\n      SERVICE_SMTP: \"{{T_SERVICE_SMTP_{{^T_ZONE_B}}}}\"\n\n  development:\n    declare:\n      - \"EnvironmentServices\u003c east , east \u003e\"\n  performance:\n    declare:\n      - \"EnvironmentServices\u003c east , west \u003e\"\n  production:\n    declare:\n      - \"EnvironmentServices\u003c west , west \u003e\"\n\ninject_sets:\n  Services:\n    - SERVICE_LDAP\n    - SERVICE_SMTP\n```\n#### Developer Consumes\n```\ninject:\n  - \"*Services\"\n```\n\n## Define Secrets\nThe **envvars** library has special support for secrets.\n\n\u003e The **envvars** library cannot protect secrets!\n\nLet me say that again a different way: \n\n\u003e Do not put actual secrets in **envvars** data, or let the **envvars** library process your actual secrets!\n\nRather, secrets in **envvars** are meant to only be named references to secretIDs that somehow map to a secret management system at your runtime or deploytime.\n\nSo back to a simple example, it might look something like this:\n#### DBA Produces\n```\ndefine:\n  HOST: \"transrecdb.perf.ourcompany.com\"\n  PORT: \"3306\"\n\ndefineSecrets:\n  USERNAME: \"vaultKey_transrecdb_perf_uname\"\n  PASSWORD: \"vaultKey_transrecdb_perf_pass\"\n  \n```\n#### Developer Consumes\n```\ninject:\n  - HOST\n  - PORT\n  - USERNAME\n  - PASSWORD\n```\n\nThe important thing to realize:\n\u003e A variables \"secretness\" is defined when it is produced (defined), not when it is consumed (injected).\n\nConsumers should not be directly aware whether a variable is protected or not.  Some policies treat a username as a secret, other policies do not.  The application developer shouldn't need to care if the username is protected or not - it just needs the username.\n\n\u003e But your CICD code that uses the **envvars** library will need to specially map the secret data as part of its output generation.\n\n## Define References\n\u003e Work in Progress\n\nSecrets are private variables having values that are expected to actually be indirect references to protected data stored somewhere else.\n\nReferences are public variables having values that are expected to actually be indirect references to public data stores somewhere else.\n\n## Allowing Missing Declarations\nIs a missing declaration a defect?  Opinions vary.  The **envvars* library lets you choose your own side on the \"missing means error\" vs \"missing means default\" argument.\n\nThe **envvars** library is opinionated to the extent that by default, **envvars** treats missing data as an error.\n\nIf you wish for missing data to be allowed (for **envvars** to not complain if you try to inject a variable that is undefined) you must explicitly tell the **envvars** engine to skip injecting that variable if it is not defined.\n\n#### DBA Produces\n```\nenvironment:\n  development:\n    define:\n      HOST: \"transrecdb.dev.ourcompany.com\"\n      PORT: \"3306\"\n  performance:\n    define:\n      HOST: \"transrecdb.perf.ourcompany.com\"\n  production:\n    define:\n      HOST: \"transrecdb.ourcompany.com\"\n      PORT: \"3306\"\n```\n#### Developer Consumes\n```\ninject:\n  - HOST\n  - PORT\n```\n\nThe example above will error in *performance* because `PORT` is not defined.\n\nSome variables \"activate\" by their existence - regardless of value, and it's better (or required) to not inject them in *false* cases.\n#### Operations Produces\n```\nenvironment:\n  development:\n    define:\n      DOCKER_TLS_VERIFY: \"0\"\n  performance:\n    define:\n      DOCKER_TLS_VERIFY: \"0\"\n  production:\n    define:\n      DOCKER_TLS_VERIFY: \"1\"\n```\n#### Developer Consumes\n```\ninject:\n  - DOCKER_TLS_VERIFY\n```\n\nThe code above works, but some folks prefer to not inject DOCKER_TLS_VERIFY if it shouldn't be activated.  How do you conditionally inject based on the defined status?\n\nNever define the \"0\" cases, and then have your code tell the engine SkipInjectIfNotDefined(\"DOCKER_TLS_VERIFY\").\n\n#### Operations Produces\n```\nenvironment:\n  development:\n    define:\n  performance:\n    define:\n  production:\n    define:\n      DOCKER_TLS_VERIFY: \"1\"\n```\n#### Developer Consumes\n```\ninject:\n  - DOCKER_TLS_VERIFY\n```\n\n## Built-in SkipInjectIfNotDefined Support\nThere *is* one built-in way that **envvars** supports the \"skip inject if not defined\" feature.  There is a special syntax when using inject_sets with a `?` prefix to indicate that the variable to inject can be skipped if not defined.\n\n\u003e Why isn't `?` supported by normal inject? It is a design choice by **envvars** to reserve this \\[potentially dangerous\\] feature to the \\[presumably fewer and more experienced\\] persons that manage inject_sets.\n\nSo if we wanted the above to work without needing to write special code to interface directly with the EnvVarsEngine, we could do this:\n\n#### Operations Produces\n```\nenvironment:\n  development:\n    define:\n  performance:\n    define:\n  production:\n    define:\n      DOCKER_TLS_VERIFY: \"1\"\n\ninject_sets:\n  DockerVariables:\n   - \"?DOCKER_TLS_VERIFY\"\n```\n#### Developer Consumes\n```\ninject:\n  - \"*DockerVariables\"\n```\n\n## Optional Define Shortcut\nThe \"define:\" block of a Node is inferred if there are no other blocks or no other nestd blocks.\n\nMeaning this:\n\n```\nenvironment:\n  default:\n    define:\n      URL: \"jdbc:mysql://{{HOST}}:{{PORT}}/db\"\n  development:\n    define:\n      HOST: \"transrecdb.dev.ourcompany.com\"\n      PORT: \"3306\"\n  performance:\n    define:\n      HOST: \"transrecdb.perf.ourcompany.com\"\n      PORT: \"3306\"\n  production:\n    define:\n      HOST: \"transrecdb.ourcompany.com\"\n      PORT: \"3306\"\n```\n\ncan be reduced to this:\n```\nenvironment:\n  default:\n    URL: \"jdbc:mysql://{{HOST}}:{{PORT}}/db\"\n  development:\n    HOST: \"transrecdb.dev.ourcompany.com\"\n    PORT: \"3306\"\n  performance:\n    HOST: \"transrecdb.perf.ourcompany.com\"\n    PORT: \"3306\"\n  production:\n    HOST: \"transrecdb.ourcompany.com\"\n    PORT: \"3306\"\n```\n\n\n## Composable Configuration\nConfiguration (defines and injects) are composable, allowing different data domains to exist in different files or even different formats (YAML/JSON).\n\n## Nested Configuration\nConfiguration (defines and injects) are nestable, allowing for a hierarchical configuration definition.\n\n## Configuration Features Can Be Constrained\nConstraints (in the form of a NodeSectionsPolicy) determines which features are allowed for a given Node context.\n\nThe constrained features for each node are:\n* Declare\n* Define\n* Inject\n* Remap\n* SkipInjectIfNotDefined\n* DefineSecrets\n* DefineReferences\n\n\n## Schema Variable Constraints\nNative **envvars** YAML files support a `schema` for variable name standards validation and confirmation.\n\nSchemas use regular expressions to define definitions, and those definitions can be required or forbidden on a file-by-file basis.\n\n## Variable Injection from a Different Definition\nWhen trying to standardize naming conventions by domain in order to secure definition access by schema (aka: \"Only DBA should have access to define database values\"), it often creates cases where data is defined using a variable that is not the variables needed for injection (aka: \"The DBAs define MYSQL_HOST but I need HOST\").\n\nThe **envvars** library supports *inject from*, which is effectively an inline-rename operation.\n\nFirst consider this scenario without rename:\n\n#### Network Admin Produces\n```\ndefine:\n  SERVICE_LDAP: \"ldap.ourcompany.com\"\n```\n#### Developer Consumes\n```\ndefine:\n  LDAP_HOST: \"{{SERVICE_LDAP}}\"\ninject:\n  - LDAP_HOST\n```\n\nThis works *fine* except if, for some reason, `SERVICE_LDAP` was not defined.  This happens if, let's say, you accidentally type `{{SRV_LDAP}}`, or if the network admin hasn't yet defined the host in a new environment.  Since variables are expected to be blank, there is no way *in template proccessing* to error on a missing (blank) variable.\n\n*Plus there's the whole philosophical issue of not wanting (or even not *allowing*) developers to define variables.\n\nFrom to the rescue.  Here is how you can inject `LDAP_HOST` without defining `LDAP_HOST`:\n\n#### Network Admin Produces\n```\ndefine:\n  SERVICE_LDAP: \"ldap.ourcompany.com\"\n```\n#### Developer Consumes\n```\ninject:\n  - \"LDAP_HOST from SERVICE_LDAP\"\n```\n\nElegant.\n\nAnd now you will get an error if MYSQL_HOST isn't defined.\n\nThis also works great when you want/need to refactor global definitions, but individual apps are slow to adopt the new variable names.  They can individually map the new names to the old names.  Problem solved!\n\n\n## Helpful Support for Key Files\nWhen defining variables by environment, it becomes quickly important to know \"What are the valid environments?\"  Code could infer the list of environments as it detects new ones in the configuration, but that makes it impossible to detect errors when someone mistakenly types `prf` instead of `perf`.\n\nYou can certainly define an `enum` in your code to list your valid environments, but wouldn't it be nicer to keep data in a data file and out of your code?\n\nSo, **envvars** supports Key Files.\n\n```\nenvironments:\n  - dev\n  - perf\n  - production\n```\n\nNow you can build key validation so that variables defined mistakenly for `prf` throws an error.\n\n\n## Node Types\n\u003e Work in progress.\n\nNode types is meant for hierarchical configuration nodes to be definable in metadata files rather that in code.\n\n## Peeking At Envvars Engine Internals - Bridge Data\nThe **envvars** library is intended to be embedded in some larger configuration management CICD system.  There can be situations when that larger system needs \\[or thinks it needs\\] to *peek* into the running state of variables in the **envvars** engine.  Support for such behavior is provided by **bridge data** methods of the **EnvVarsEngine**.  Bridge Data can be completely generated by generateBridgeData() or generated-on-demand-and-cached by generateSparseBridgeData().\n\nIn either case, this is *not* how **envvars** processing is *supposed* to work, as it only reports on the \"Producer Side\" of the world, ignoring Inject processing.  Why does this matter?  Recall, Inject using the \"from\" syntax renames variables.  The bridge methods do not consider the from renaming of injects.  So: yes, you get a truthful state of the engine, but it might not be an accurate picture of what eventually is consumed by an application.\n\n\u003e Use Bridge Data with caution, as it may be an incomplete (or even inaccurate) picture of the variables your final consumer will see after calling EnvVarsEngine.process().\n\n## Administrivia - Pronunciation\nThe **envvars** libary is spelled with two *v* letters; but phonetically, the first *v* is slurred so much into the second *v* as to be effectively silent.  Think \"En Vars\" not \"Env Vars.\"  You, of course, can pronounce it as you like.  I pronounce it \"En Vars.\"\n\n\u003c!-- LICENSE --\u003e\n## License\n\nDistributed under the Apache 2.0 License. See `LICENSE.txt` for more information.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foptum%2Fenvvars","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Foptum%2Fenvvars","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foptum%2Fenvvars/lists"}