{"id":13562628,"url":"https://github.com/busyloop/envcat","last_synced_at":"2025-06-20T10:13:47.320Z","repository":{"id":66253910,"uuid":"586099755","full_name":"busyloop/envcat","owner":"busyloop","description":"Shell Environment Swiss Army Knife","archived":false,"fork":false,"pushed_at":"2024-03-26T22:37:37.000Z","size":1401,"stargazers_count":12,"open_issues_count":0,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-03T18:42:17.102Z","etag":null,"topics":["cli","docker","environment-variables","templating"],"latest_commit_sha":null,"homepage":"","language":"Crystal","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/busyloop.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"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":"2023-01-07T00:09:06.000Z","updated_at":"2024-10-27T13:19:22.000Z","dependencies_parsed_at":"2024-03-16T21:59:25.312Z","dependency_job_id":"5ef41c5b-61a9-4cf9-9cdf-258c7b1570ea","html_url":"https://github.com/busyloop/envcat","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/busyloop/envcat","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/busyloop%2Fenvcat","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/busyloop%2Fenvcat/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/busyloop%2Fenvcat/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/busyloop%2Fenvcat/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/busyloop","download_url":"https://codeload.github.com/busyloop/envcat/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/busyloop%2Fenvcat/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":260924539,"owners_count":23083525,"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":["cli","docker","environment-variables","templating"],"created_at":"2024-08-01T13:01:10.521Z","updated_at":"2025-06-20T10:13:42.231Z","avatar_url":"https://github.com/busyloop.png","language":"Crystal","funding_links":[],"categories":["Crystal"],"sub_categories":[],"readme":"\u003c!--\nDo not edit this file. Edit 'docs/templates/README.md.j2' instead and run 'make README.md'.\n--\u003e\n\n# envcat\n\n[![Build](https://github.com/busyloop/envcat/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/busyloop/envcat/actions/workflows/ci.yml?query=branch%3Amaster) [![GitHub](https://img.shields.io/github/license/busyloop/envcat)](https://en.wikipedia.org/wiki/MIT_License) [![GitHub release](https://img.shields.io/github/release/busyloop/envcat.svg)](https://github.com/busyloop/envcat/releases)\n\n\u003cimg src=\"https://github.com/busyloop/envcat/raw/master/assets/mugshot.png\" alt=\"🐟\" width=\"342\" align=\"right\" /\u003e\n\n**Your Shell Environment Swiss Army Knife.** 🇨🇭\n\n### Features\n\n* Print environment variables in JSON, YAML or other formats\n* Validate your environment variables\n* Populate a template with env-variables from stdin to stdout\n\n\u003csub\u003e\u003cb\u003eHint:\u003c/b\u003e envcat loves templating config-files in a Docker or Kubernetes environment.\u003c/sub\u003e\n\n\u003cbr\u003e\n\n## Installation\n\n#### Download static executable\n\n| OS           | Arch    | Version               |      |\n| ------------ | ------- | --------------------- | ---- |\n| macOS (Darwin) | x86_64  | 1.1.1 (latest)  | [Download](https://github.com/busyloop/envcat/releases/latest) |\n| Linux        | x86_64  | 1.1.1 (latest)  | [Download](https://github.com/busyloop/envcat/releases/latest) |\n| Linux        | aarch64 | 1.1.1 (latest)  | [Download](https://github.com/busyloop/envcat/releases/latest) |\n\n#### macOS :beer:\n\n`brew install busyloop/tap/envcat`\n\n#### Dockerfile\n\nSee the [download page](https://github.com/busyloop/envcat/releases/latest) for an example Dockerfile. :whale:\n\n\n## Usage\n\n```bash\n# Print\nenvcat '*'                           # Print all env vars in JSON-format\nenvcat -f yaml SHELL HOME            # Print $SHELL and $HOME in YAML-format\n\n# Validate\nenvcat -c ADDR:ipv4                  # Exit 1 if $ADDR is undefined or not an IPv4 address\nenvcat -c ADDR:?ipv4                 # Exit 1 if $ADDR is defined and not an IPv4 address\n\n# Template\necho \"{{HOME}}\" | envcat -f j2 '*'   # Read j2 template from stdin and render it to stdout\necho \"{{HOME}}\" | envcat -f j2 'H*'  # Same, but only vars starting with H available in the template\n\n# All of the above combined\necho \"{{BIND}}:{{PORT | default('443')}} {{NAME}}\" | envcat -f j2 -c PORT:?port -c BIND:ipv4 PORT BIND NAME\n```\n\n:bulb: See `envcat --help` for full syntax reference.\n\n\n## Templating\n\nWith `-f j2`, or when called by the name `envtpl`, envcat renders a jinja2 template from _stdin_ to _stdout_.  \nEnvironment variables are available as `{{VAR}}`.\n\nenvcat will abort with code 5 if your template references an undefined variable,  \nso make sure to provide defaults where appropriate: `{{VAR | default('xxx')}}`.\n\n\n#### Examples\n\n\n```bash\nexport FOO=a,b,c\nexport BAR=41\nunset NOPE\n\necho \"{{FOO}}\"                                          | envcat -f j2 FOO  # =\u003e a,b,c\necho \"{{NOPE | default('empty')}}\"                      | envcat -f j2 NOPE # =\u003e empty\necho \"{% for x in FOO | split(',') %}{{x}}{% endfor %}\" | envcat -f j2 FOO  # =\u003e abc\necho \"{% if FOO == 'd,e,f' %}A{% else %}B{% endif %}\"   | envtpl FOO        # =\u003e B\necho \"{% if BAR | int + 1 == 42 %}yes{% endif %}\"       | envtpl BAR        # =\u003e yes\n```\n\n\n## Template syntax\n\nEnvcat supports most jinja2 syntax and [builtin filters](https://jinja.palletsprojects.com/en/2.11.x/templates/#list-of-builtin-filters).\n\nOn top it provides the following additional filters:\n\n#### b64encode, b64encode_urlsafe\n\n```bash\nexport FOO=\"hello? world?\"\n\n# b64encode, b64encode_urlsafe\necho \"{{FOO | b64encode}}\"                              | envtpl FOO  # =\u003e aGVsbG8/IHdvcmxkPw==\necho \"{{FOO | b64encode_urlsafe}}\"                      | envtpl FOO  # =\u003e aGVsbG8_IHdvcmxkPw==\n```\n\n#### b64decode\n\n```bash\nexport B64_REGULAR=\"aGVsbG8/IHdvcmxkPw==\"\nexport B64_URLSAFE=\"aGVsbG8_IHdvcmxkPw==\"\n\necho \"{{B64_REGULAR | b64decode}}\"                      | envtpl 'B*' # =\u003e hello? world?\necho \"{{B64_URLSAFE | b64decode}}\"                      | envtpl 'B*' # =\u003e hello? world?\n```\n\n#### split\n\n\n```bash\nexport FOO=a,b,c\n\necho \"{% for x in FOO | split(',') %}{{x}}..{% endfor %}\" | envtpl FOO  # =\u003e a..b..c..\n```\n\n**Note:**  \nEnvcat uses a [Crystal implementation of the jinja2 template engine](https://straight-shoota.github.io/crinja/).  \nPython expressions are **not** supported.\n\n## Layering data from multiple sources\n\nBy default envcat reads variables only from your shell environment.  \nWith `-i` you can additionally source data from YAML, JSON or TOML files.  \nWith `-s` you can override variables directly on the command line.\n\nBoth flags can be given multiple times.\n\n**Examples:**\n\n```bash\n# Override vars with YAML file\n$ export FOO=from_env\n$ echo \"foo: from_file\" \u003edemo.yaml\n$ envcat -i env -i yaml:demo.yaml FOO\n{\"FOO\":\"from_file\"}\n\n# Override a var with `-s`\n$ envcat -i env -i yaml:demo.yaml -s FOO=from_arg FOO\n{\"FOO\":\"from_arg\"}\n\n# Layer data from foo.yaml, the environment,\n# JSON from stdin and lastly override FOO\n$ envcat -i yaml:foo.yaml -i env -i json:- -s FOO=bar [..]\n```\n\n### Input normalization\n\nenvcat flattens the structure of data sourced via `-i` as follows.\n\nGiven the following YAML:\n\n```yaml\n# demo.yaml\nemployee:\n  name: Jane Smith\n  department: HR\n  contact:\n    email: jane@example.com\n    phone: 555-123-4567\n  projects:\n    - Project A\n    - Project B\n  skills:\n    - Skill 1\n    - Skill 2\n```\n\n`envcat -f yaml -i yaml:demo.yaml '*'` produces the following output:\n\n```yaml\nEMPLOYEE_NAME: Jane Smith\nEMPLOYEE_DEPARTMENT: HR\nEMPLOYEE_CONTACT_EMAIL: jane@example.com\nEMPLOYEE_CONTACT_PHONE: 555-123-4567\nEMPLOYEE_PROJECTS_0: Project A\nEMPLOYEE_PROJECTS_1: Project B\nEMPLOYEE_SKILLS_0: Skill 1\nEMPLOYEE_SKILLS_1: Skill 2\n```\n\n\n## Checks\n\nWith `-c VAR[:SPEC]` envcat checks that $VAR meets a constraint defined by SPEC.\n\nThis flag can be given multiple times.  \nenvcat aborts with code 1 if any check fails.\n\nYou can prefix a SPEC with `?` to skip it when $VAR is undefined:\n\n```bash\nunset FOO\nenvcat -c FOO:i     # =\u003e Abort because FOO is undefined\nenvcat -c FOO:?i    # =\u003e Success because FOO is undefined (check skipped)\n\nexport FOO=x\nenvcat -c FOO:i     # =\u003e Abort because FOO is not an unsigned integer\nenvcat -c FOO:?i    # =\u003e Abort because FOO is not an unsigned integer\n\nexport FOO=1\nenvcat -c FOO:i     # =\u003e Success because FOO is an unsigned integer\nenvcat -c FOO:?i    # =\u003e Success because FOO is an unsigned integer\n```\n\nFor a full list of available SPEC constraints see below.\n\n\n## Synopsis\n\n```\nUsage: envcat [-i \u003cSOURCE\u003e ..] [-s \u003cKEY=VALUE\u003e ..] [-c \u003cVAR[:SPEC]\u003e ..] [-f etf|kv|export|j2|j2_unsafe|json|none|yaml] [GLOB[:etf] ..]\n\n  -i, --input=SOURCE      env|json:PATH|yaml:PATH|toml:PATH (default: env)\n  -s, --set=KEY=VALUE     KEY=VALUE\n  -f, --format=FORMAT     etf|export|j2|j2_unsafe|json|kv|none|yaml (default: json)\n  -c, --check=VAR[:SPEC]  Check VAR against SPEC. Omit SPEC to check only for presence.\n  -h, --help              Show this help\n      --version           Print version and exit\n\nSOURCE\n  env           - Shell environment\n  json:PATH     - JSON file at PATH\n  yaml:PATH     - YAML file at PATH\n  toml:PATH     - TOML file at PATH\n\nFORMAT\n  etf               Envcat Transport Format\n  export            Shell export format\n  j2                Render j2 template from stdin (aborts with code 5 if template references an undefined var)\n  j2_unsafe         Render j2 template from stdin (renders undefined vars as empty string)\n  json              JSON format\n  kv                Shell format\n  none              No format\n  yaml              YAML format\n\nSPEC\n  alnum             must be alphanumeric\n  b64               must be base64\n  f                 must be an unsigned float\n  fs                must be a path to an existing file or directory\n  fsd               must be a path to an existing directory\n  fsf               must be a path to an existing file\n  gt:X              must be \u003e X\n  gte:X             must be \u003e= X\n  hex               must be a hex number\n  hexcol            must be a hex color\n  i                 must be an unsigned integer\n  ip                must be an ip address\n  ipv4              must be an ipv4 address\n  ipv6              must be an ipv6 address\n  json              must be JSON\n  lc                must be all lowercase\n  len:X:Y           must be X-Y characters\n  lt:X              must be \u003c X\n  lte:X             must be \u003c= X\n  n                 must be an unsigned float or integer\n  nre:X             must not match PCRE regex: X\n  port              must be a port number (0-65535)\n  re:X              must match PCRE regex: X\n  sf                must be a float\n  si                must be an integer\n  sn                must be a float or integer\n  uc                must be all uppercase\n  uuid              must be a UUID\n  v                 must be a semantic version\n  vgt:X             must be a semantic version \u003e X\n  vgte:X            must be a semantic version \u003e= X\n  vlt:X             must be a semantic version \u003c X\n  vlte:X            must be a semantic version \u003c= X\n\n  Prefix ? to skip check when VAR is undefined.\n```\n\n## Advanced: Envcat Transport Format 🚚\n\nSometimes it can be helpful to pack multiple env vars\ninto a single string, to be unpacked elsewhere.  \nYou can do this with envcat by using the `etf` format:\n\n```bash\n$ export A=1 B=2 C=3\n\n# Export to ETF format (url-safe base64)\n$ envcat -f etf A B C\nH4sIAPPtsmMA_6tWclSyUjJU0lFyAtJGQNoZSBsr1QIActF58hkAAAA\n\n# Import from ETF format\n# The :etf suffix tells envcat to unpack $VARS_ETF from etf format.\n# The unpacked vars override any existing env vars by the same name.\n$ export VARS_ETF=H4sIAPPtsmMA_6tWclSyUjJU0lFyAtJGQNoZSBsr1QIActF58hkAAAA\n$ envcat -f export VARS_ETF:etf A B C\nexport A=1\nexport B=2\nexport C=3\n```\n\nYou can also layer multiple ETF bundles:\n\n\n```bash\n$ export BUNDLE_A=$(A=xxx envcat -f etf A)\n$ export BUNDLE_B=$(A=hello B=world envcat -f etf A B)\n\n$ envcat -f export BUNDLE_A:etf A B\nexport A=xxx\n\n$ envcat -f export BUNDLE_A:etf BUNDLE_B:etf A B\nexport A=hello\nexport B=world\n```\n\n## Exit codes\n\n| Code  |                                                                                       |\n| ----- | ------------------------------------------------------------------------------------- |\n| 0     | Success                                                                               |\n| 1     | Invalid value (`--check` constraint violation)                                        |\n| 3     | Syntax error (invalid argument or template)                                           |\n| 5     | Undefined variable access (e.g. your template contains `{{FOO}}` but $FOO is not set) |\n| 7     | I/O Error                                                                             |\n| 11    | Parsing error                                                                         |\n| 255   | Bug (unhandled exception)                                                             |\n\n## Contributing\n\n1. Fork it (\u003chttps://github.com/busyloop/envcat/fork\u003e)\n2. Create your feature branch (`git checkout -b my-new-feature`)\n3. Commit your changes (`git commit -am 'Add some feature'`)\n4. Push to the branch (`git push origin my-new-feature`)\n5. Create a new Pull Request\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbusyloop%2Fenvcat","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbusyloop%2Fenvcat","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbusyloop%2Fenvcat/lists"}