Generate .env files from a declarative YAML schema.
envgen is a small Rust CLI for keeping environment variable definitions (docs + sources + per-environment behavior) in one place, then generating .env files safely and repeatably.
- Single source of truth: variables + documentation live in YAML
- Source-agnostic: resolve values via
static,manual, or arbitrary shell commands - Multi-environment: one schema can cover local/staging/production
- One schema → one output file: run multiple times to populate multiple
.envfiles - Safe defaults: won’t overwrite existing files without
--force - Tooling-friendly: list variables, validate schemas, export a JSON Schema for editor autocomplete
Status: stable (v1.x) as of v1.0.0. Breaking changes follow semantic versioning major releases.
envgen init writes a commented sample schema (env.dev.yaml). Here’s what it looks like to use:
envgen list -c env.dev.yamlSchema: env.dev.yaml (Sample envgen schema. Replace with your project description.)
+-----------+-----------------------+--------------+
| Name | Environments | Source |
+==================================================+
| API_TOKEN | dev, local, prod, stg | manual |
|-----------+-----------------------+--------------|
| APP_NAME | dev, local, prod, stg | static |
|-----------+-----------------------+--------------|
| BUILD_ID | local, dev, stg, prod | command_name |
+-----------+-----------------------+--------------+
3 variables across 4 environments
Preview what would happen before writing anything:
envgen pull -c env.dev.yaml -e dev --dry-run --interactiveSchema: env.dev.yaml
Environment: dev
Destination: .env.dev (does not exist)
Variables to resolve:
API_TOKEN
source: manual (interactive prompt)
instructions:
Example (GitHub fine-grained personal access token):
1) Go to https://github.com and sign in.
2) Click your avatar (top-right) -> Settings.
3) Scroll to the bottom of the left sidebar -> Developer settings.
4) Personal access tokens -> Fine-grained tokens -> Generate new token.
5) Set an expiration date, limit repository access, and grant least-privilege permissions.
6) Click Generate token and copy it immediately (you won't be able to see it again).
7) Paste the token here when prompted by envgen.
Tip: Store it in a password manager and rotate it regularly.
APP_NAME
source: static
value: Envgen Dev
BUILD_ID
source: command_name
command: bash -lc 'echo envgen-demo-dev-BUILD_ID-$(date +%Y%m%d%H%M%S)'
3 variables would be written to .env.dev
1 command would be executed (2 static/manual)
brew tap smorinlabs/tap
brew install envgenThis project currently ships through the smorinlabs/tap formula stream. homebrew-core
support is a future track and is intentionally non-blocking for releases.
# From crates.io (if published):
cargo install envgen --locked
# Or from git:
cargo install --git https://github.com/smorinlabs/envgen --lockedDownload the appropriate archive from GitHub Releases and place envgen on your PATH.
cargo build --release --locked
./target/release/envgen --helpCreate a sample schema:
envgen init
# writes ./env.dev.yamlValidate it:
envgen check -c env.dev.yamlGenerate a .env file (preview first, then write):
envgen pull -c env.dev.yaml -e dev --dry-run --interactive
envgen pull -c env.dev.yaml -e dev --interactive --force
# prompts for any `source: manual` variables when `--interactive` is setList variables:
envgen list -c env.dev.yamlGenerate Markdown docs:
envgen docs -c env.dev.yamlenvgen init: write a sample schemaenvgen check: validate a schema fileenvgen list: show variables (table by default;--format jsonis available)envgen docs: generate Markdown documentation for a schemaenvgen pull: resolve variables and write the destination.envfileenvgen schema: export the embedded JSON Schema used for structural validation and editor autocompleteenvgen readme: print the embedded README.md to stdout
Useful flags:
pull:--dry-run,--force,--destination,--source-timeout,--interactive,--show-secrets,--write-on-errorpull --source-timeout <seconds>: hard timeout per source command; timed-out commands are terminatedlist:--env,--format
At a high level:
environments: defines env names and config values usable as{placeholders}sources: defines named shell command templates to fetch valuesvariables: defines each variable’s description, sensitivity, applicability, and source
Optional documentation fields:
sources.*:label,url,descriptionvariables.*.resolvers[]:label,url,description
Supported schema version:
"2": onesourceper variable, plus optional per-environmentresolvers
Placeholders available in templates:
{environment}: the selected environment name{key}: the variable’ssource_key(or the variable name)- Any key from the active
environments.<env>map (e.g.,{firebase_project})
Minimal example (schema v2):
schema_version: "2"
metadata:
description: "Frontend env vars"
destination:
local: ".env"
production: ".env.production"
environments:
local:
firebase_project: "my-app-staging"
base_url: "http://localhost:5173"
production:
firebase_project: "my-app"
base_url: "https://app.example.com"
sources:
gcloud:
command: "gcloud secrets versions access latest --secret={key} --project={firebase_project}"
label: "Google Cloud Secret Manager"
url: "https://console.cloud.google.com/security/secret-manager"
variables:
VITE_BASE_URL:
description: "Base URL for the app"
sensitive: false
source: static
values:
local: "{base_url}"
production: "{base_url}"
VITE_API_KEY:
description: "API key (prompt locally; prod comes from Secret Manager)"
source_key: API_KEY
source_instructions: |
Example (GitHub fine-grained personal access token):
1) Go to https://github.com and sign in.
2) Click your avatar (top-right) -> Settings.
3) Scroll to the bottom of the left sidebar -> Developer settings.
4) Personal access tokens -> Fine-grained tokens -> Generate new token.
5) Set an expiration date, limit repository access, and grant least-privilege permissions.
6) Click Generate token and copy it immediately (you won't be able to see it again).
7) Save it somewhere secure (you won't be able to view it again later).
environments: [local, production]
resolvers:
- environments: [local]
source: manual
label: "Local OAuth client"
url: "https://console.cloud.google.com/apis/credentials"
- environments: [production]
source: gcloud
label: "Secret Manager"Export the embedded JSON Schema (for YAML Language Server, CI, etc.). This is also the schema envgen
uses for structural validation; envgen check adds semantic validation on top. Structural
validation is implemented via the jsonschema crate (Draft 2020-12) on the YAML parsed into
serde_json::Value.
envgen schema
# writes ./envgen.schema.vA.B.C.jsonThe schema artifact version (A.B.C) is managed independently from the crate version via
SCHEMA_VERSION.
Point the YAML Language Server at it (top of your schema file):
# yaml-language-server: $schema=./envgen.schema.vA.B.C.jsonenvgen uses two independent release streams:
- Crate version stream:
- Version source:
Cargo.toml([package].version) - Release notes:
CHANGELOG.md - Tag format:
vX.Y.Z
- Version source:
- Schema artifact version stream:
- Version source:
SCHEMA_VERSION - Release notes:
SCHEMA_CHANGELOG.md - Tag format:
schema-vA.B.C
- Version source:
These streams are intentionally decoupled. Bumping the crate version does not require a schema artifact version bump, and bumping the schema artifact version does not require a crate version bump.
Common bump and tag commands:
# crate version flow
make bump-crate-patch
make tag-crate && make push-tag-crate
# schema artifact version flow
make bump-schema-patch
make tag-schema && make push-tag-schemaFull release guide: RELEASING.md
Schema artifact release notes: SCHEMA_CHANGELOG.md
- Command sources are executed via
sh -c. Treat schemas as code; don’t run untrusted schemas. envgen pullrefuses to overwrite existing files unless you pass--force.- By default,
envgen pulldoes not write the destination file when write-blocking failures occur (any command-source failure, or required static/manual failure). Use--write-on-errorto write resolved variables anyway. --dry-runmaskssensitive: truevalues unless you pass--show-secrets.
make check
make test
make fmtFor release operations, bump behavior, and tagging workflow, use RELEASING.md as the source of truth.
MIT (see LICENSE).