Skip to content

Generate and validate .env files from one spec - self-documenting config, consistent across environments, secrets stay out of git.

License

Notifications You must be signed in to change notification settings

smorinlabs/envgen

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

50 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

envgen

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 .env files
  • 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.

Demo

envgen init writes a commented sample schema (env.dev.yaml). Here’s what it looks like to use:

envgen list -c env.dev.yaml
Schema: 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 --interactive
Schema:      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)

Install

Homebrew (personal tap)

brew tap smorinlabs/tap
brew install envgen

This project currently ships through the smorinlabs/tap formula stream. homebrew-core support is a future track and is intentionally non-blocking for releases.

Cargo

# From crates.io (if published):
cargo install envgen --locked

# Or from git:
cargo install --git https://github.com/smorinlabs/envgen --locked

Prebuilt binaries

Download the appropriate archive from GitHub Releases and place envgen on your PATH.

From source

cargo build --release --locked
./target/release/envgen --help

Quickstart

Create a sample schema:

envgen init
# writes ./env.dev.yaml

Validate it:

envgen check -c env.dev.yaml

Generate 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 set

List variables:

envgen list -c env.dev.yaml

Generate Markdown docs:

envgen docs -c env.dev.yaml

Command summary

  • envgen init: write a sample schema
  • envgen check: validate a schema file
  • envgen list: show variables (table by default; --format json is available)
  • envgen docs: generate Markdown documentation for a schema
  • envgen pull: resolve variables and write the destination .env file
  • envgen schema: export the embedded JSON Schema used for structural validation and editor autocomplete
  • envgen readme: print the embedded README.md to stdout

Useful flags:

  • pull: --dry-run, --force, --destination, --source-timeout, --interactive, --show-secrets, --write-on-error
  • pull --source-timeout <seconds>: hard timeout per source command; timed-out commands are terminated
  • list: --env, --format

Schema format (YAML)

At a high level:

  • environments: defines env names and config values usable as {placeholders}
  • sources: defines named shell command templates to fetch values
  • variables: defines each variable’s description, sensitivity, applicability, and source

Optional documentation fields:

  • sources.*: label, url, description
  • variables.*.resolvers[]: label, url, description

Supported schema version:

  • "2": one source per variable, plus optional per-environment resolvers

Placeholders available in templates:

  • {environment}: the selected environment name
  • {key}: the variable’s source_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"

JSON Schema (editor validation)

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.json

The 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.json

Versioning and Release Model

envgen uses two independent release streams:

  • Crate version stream:
    • Version source: Cargo.toml ([package].version)
    • Release notes: CHANGELOG.md
    • Tag format: vX.Y.Z
  • Schema artifact version stream:
    • Version source: SCHEMA_VERSION
    • Release notes: SCHEMA_CHANGELOG.md
    • Tag format: schema-vA.B.C

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-schema

Full release guide: RELEASING.md
Schema artifact release notes: SCHEMA_CHANGELOG.md

Safety notes

  • Command sources are executed via sh -c. Treat schemas as code; don’t run untrusted schemas.
  • envgen pull refuses to overwrite existing files unless you pass --force.
  • By default, envgen pull does not write the destination file when write-blocking failures occur (any command-source failure, or required static/manual failure). Use --write-on-error to write resolved variables anyway.
  • --dry-run masks sensitive: true values unless you pass --show-secrets.

Development

make check
make test
make fmt

For release operations, bump behavior, and tagging workflow, use RELEASING.md as the source of truth.

License

MIT (see LICENSE).

About

Generate and validate .env files from one spec - self-documenting config, consistent across environments, secrets stay out of git.

Topics

Resources

License

Stars

Watchers

Forks

Sponsor this project

 

Packages

No packages published

Contributors 2

  •  
  •