Skip to content
/ zenv Public

An environment variable validation suite for TypeScript/Node.js monorepos. Ensures consistency between your code, schemas, and documentation.

Notifications You must be signed in to change notification settings

ModLogTV/zenv

Repository files navigation

zenv

Zod-powered environment variable validation and generation for TypeScript/Node.js projects. Written in Rust for speed. Ensures consistency between your code, Zod schemas, and .env.example documentation.

Features

  • Direct Usage Detection - Catches process.env and Bun.env usage outside validated schema files
  • Schema Coverage - Verifies env.ts Zod schemas match .env.example documentation
  • Orphan Detection - Finds env vars defined but unused, or used but undocumented
  • Naming Conventions - Enforces UPPER_SNAKE_CASE and meaningful prefixes
  • Duplicate Detection - Identifies vars sharing identical values (potential copy-paste errors)
  • Multiple Output Formats - Pretty, JSON, YAML, Standard
  • Configurable - JSON config with schema validation for IDE autocompletion
  • NEW: .env Generator - Interactive generation of .env files from .env.example with metadata annotations

Installation

npm / Bun (Recommended)

# npm
npm install -D @modlogtv/zenv

# Bun
bun add -d @modlogtv/zenv

# pnpm
pnpm add -D @modlogtv/zenv

# Yarn
yarn add -D @modlogtv/zenv

Then add to your package.json scripts:

{
	"scripts": {
		"env:validate": "zenv",
		"env:generate": "zenv generate"
	}
}

Or run directly:

npx @modlogtv/zenv
npx @modlogtv/zenv generate --dry-run

# Bun
bunx @modlogtv/zenv

Pre-built Binaries

Download from Releases.

From Source (Rust)

# Clone the repository
git clone https://github.com/ModLogTV/zenv.git
cd zenv

# Build release binary
cargo build --release

# Binary is at ./target/release/zenv

Quick Start

# Run all validation tests
zenv

# Run specific tests
zenv validate --test process-env --test env-coverage

# Output as JSON (for CI/CD)
zenv -o json

# Generate .env files interactively
zenv generate

# See all options
zenv --help

Usage

zenv [OPTIONS] [COMMAND]

Commands:
  validate    Run validation tests (default)
  generate    Generate .env files from .env.example with metadata

Options:
  -c, --config <FILE>    Config file path [default: test-env.config.json]
  -o, --output <FORMAT>  Output format: pretty, json, yaml, standard [default: pretty]
  -q, --quiet            Minimal output
  -V, --verbose          Verbose output
      --no-color         Disable colored output
  -h, --help             Print help
  -v, --version          Print version

Validate Options:
  [DIR]                  Target directory (defaults to current directory)
  -t, --test <NAME>      Run specific test (can be repeated)
  -a, --all              Run all tests [default]
      --skip <NAME>      Skip specific test (can be repeated)
      --process-env      Only run process-env test
      --env-coverage     Only run env-coverage test
      --orphaned-vars    Only run orphaned-vars test
      --naming-conventions  Only run naming-conventions test
      --duplicate-values Only run duplicate-values test

Generate Options:
  [DIR]                  Target directory (defaults to current directory)
      --dry-run          Preview without writing files
      --force            Overwrite existing .env files
      --non-interactive  Skip prompts, use defaults/generate only (for CI)

Available Tests

Test Description
process-env Detects direct process.env / Bun.env usage outside env.ts files
env-coverage Verifies env.ts Zod schemas match .env.example documentation
orphaned-vars Finds orphaned (unused) and undocumented env vars
naming-conventions Enforces UPPER_SNAKE_CASE naming and meaningful prefixes
duplicate-values Detects multiple vars with identical values

.env Generator (NEW)

The generate command creates .env files interactively from .env.example files with metadata annotations.

Metadata Annotations

Add annotations as comments above variables in your .env.example:

# @required @prompt "Database connection URL"
DATABASE_URL=

# @optional @generate 32
SESSION_SECRET=

# @required @generate 64
JWT_SECRET=

# No metadata - uses default value, required by default
DEBUG=false

Available annotations:

Annotation Description
@required Variable must have a value (default behavior)
@optional Variable can be skipped during generation
@prompt "description" Prompt user for input with description
@generate <length> Auto-generate base64 secret of specified byte length

Annotations can be combined: @required @prompt "description" or @optional @generate 32

See examples/generator/ for a comprehensive example with all annotation types.

Generate Workflow

# Interactive generation in current directory
zenv generate

# Generate in a specific directory
zenv generate ./apps/api

# Preview without writing
zenv generate --dry-run

# Force overwrite existing .env files
zenv generate --force

# Non-interactive mode (CI/CD) - only processes @generate vars
zenv generate --non-interactive

The generator will:

  1. Find all .env.example files in the project
  2. Parse metadata annotations from each file
  3. Group variables by directory
  4. Prompt for values marked with @prompt
  5. Auto-generate values marked with @generate
  6. Preview all values before writing
  7. Write .env files after confirmation

Output Formats

Pretty (default)

Colored, human-readable terminal output with icons.

Standard

Plain text without colors (for logs).

JSON

Structured JSON for programmatic consumption.

{
  "version": "2.0.0",
  "timestamp": "2026-01-10T12:00:00+00:00",
  "summary": {
    "errors": 0,
    "warnings": 2,
    "status": "pass"
  },
  "tests": [...],
  "errors": [],
  "warnings": [...]
}

YAML

Structured YAML output.

Configuration

Create a test-env.config.json file to customize behavior:

{
	"$schema": "./test-env.config.schema.json",
	"description": "My project env validation config",

	"processEnvCheck": {
		"enabled": true,
		"excludeDirs": ["apps/legacy"],
		"excludeFiles": ["scripts/*.ts"]
	},

	"envCoverageCheck": {
		"enabled": true,
		"excludeVars": ["PORT"]
	},

	"orphanedEnvVarsCheck": {
		"enabled": true,
		"excludeVars": ["VERCEL_URL", "TURBO_INVOCATION_ID"]
	},

	"undocumentedEnvVarsCheck": {
		"enabled": true,
		"excludeVars": ["NODE_ENV"]
	},

	"namingConventionCheck": {
		"enabled": true,
		"allowGenericNames": ["PORT", "HOST"]
	},

	"duplicateValueCheck": {
		"enabled": true,
		"excludeVars": ["DATABASE_URL", "READ_DATABASE_URL"]
	},

	"generator": {
		"excludeDirs": [],
		"excludeFiles": [],
		"defaultSecretLength": 32
	}
}

Project Structure

The tool works with any TypeScript/Node.js project structure. It recursively searches for .env.example files and env.ts Zod schemas.

Monorepo Example

your-project/
├── .env                    # Actual environment values (gitignored)
├── .env.example            # Root-level documented env vars
├── apps/
│   ├── api/
│   │   ├── .env.example    # App-specific env vars
│   │   └── src/
│   │       └── env.ts      # Zod schema for this app
│   └── web/
│       ├── .env.example
│       └── src/
│           └── env.ts
└── packages/
    └── shared/
        └── src/
            └── env.ts

Single Package Example

your-project/
├── .env
├── .env.example
└── src/
    └── env.ts

env.ts Pattern

The tool looks for Zod schema definitions like:

// apps/api/src/env.ts
import { z } from "zod";

const envSchema = z.object({
	NODE_ENV: z.enum(["development", "production"]).default("development"),
	PORT: z.string().default("3000"),
	DATABASE_URL: z.string().url(),
	API_KEY: z.string().min(1),
});

export const env = envSchema.parse(process.env);

CI/CD Integration

GitHub Actions

- name: Validate environment variables
  run: |
      zenv -o json > env-report.json
      if [ $? -ne 0 ]; then
        echo "Environment validation failed"
        cat env-report.json
        exit 1
      fi

- name: Generate .env for CI
  run: |
      zenv generate --non-interactive --force

Pre-commit Hook

#!/bin/bash
# .git/hooks/pre-commit

zenv -q
if [ $? -ne 0 ]; then
  echo "Environment validation failed. Run 'zenv' for details."
  exit 1
fi

Exit Codes

Code Meaning
0 All tests passed (warnings are OK)
1 One or more tests failed with errors

Examples

See the examples/ directory for complete working examples:

# Run validator example (will show errors and warnings)
zenv validate examples/validator

# Run generator example (dry run, non-interactive)
zenv generate --dry-run --non-interactive examples/generator

Common Commands

# Run all tests with pretty output
zenv

# Run only process-env and naming checks
zenv validate --process-env --naming-conventions

# Skip duplicate check
zenv validate --skip duplicate-values

# Use custom config
zenv -c ./custom-config.json

# CI mode: JSON output, fail on errors
zenv -o json && echo "OK" || echo "FAIL"

# Generate .env files interactively
zenv generate

# Generate for a specific directory
zenv generate ./apps/api

# Preview generation without writing files
zenv generate --dry-run

Development

# Build debug
cargo build

# Build release
cargo build --release

# Run tests
cargo test

# Run with verbose output
cargo run -- -V validate

Publishing to npm

The package is distributed via npm with platform-specific binaries:

# Build for your current platform
./scripts/build-npm.sh local

# Test locally
cd npm/zenv
npm link
zenv --help

# Publish (after building for all platforms)
# See .github/workflows/release.yml for automated publishing

To trigger a release, push a version tag:

git tag v2.0.1
git push origin v2.0.1

The GitHub Actions workflow will:

  1. Build binaries for all platforms (macOS, Linux, Windows)
  2. Publish platform-specific packages (@modlogtv/zenv-linux-x64, etc.)
  3. Publish the main zenv package
  4. Create a GitHub release with downloadable binaries

License

MIT License - see LICENSE for details.

About

An environment variable validation suite for TypeScript/Node.js monorepos. Ensures consistency between your code, schemas, and documentation.

Resources

Stars

Watchers

Forks

Packages

No packages published

Contributors 2

  •  
  •