Skip to main content

Zero-config .env file comparison - catch missing keys before production

Project description

envcompare

Compare .env files in one command. Zero config. Security-first.

PyPI version Python 3.8+ License: MIT

The Problem

.env.example says your app needs 12 keys. Your staging .env has 9. Production has 7, and someone changed PORT to a string. This breaks at 3 AM.

# .env.example          # .env.production (oops)
DATABASE_URL=...        DATABASE_URL=postgres://prod/db
SECRET_KEY=...          # ← MISSING - will crash
REDIS_URL=...           # ← MISSING - will crash
PORT=8080               PORT="8080"  # ← quoted, different type

The Solution

$ uvx envcompare .env.example .env.production

❌ Missing in .env.production:
   SECRET_KEY
   REDIS_URL

🔧 Type mismatch:
   PORT: expected int (***), found string (***)

Values are masked by default. No secrets in your terminal or CI logs. Add --show-values if you need to see them.

Why envcompare?

envcompare dotenv-linter Manual grep
Config required ❌ None ✅ YAML file ❌ None
Catches missing keys
Type checking
Values masked by default
CI-ready (JSON + exit codes)
Zero install uvx

Quick Start

# No install needed
uvx envcompare .env.example .env.production

# Or install once
pipx install envcompare
envcompare .env.example .env

# Or pip
pip install envcompare
envcompare .env.example

Usage

Everyday

# Compare with default .env
envcompare .env.example

# Compare two specific files
envcompare .env.example .env.staging

# Skip type checking (only missing keys)
envcompare .env.example --missing-only

# Ignore noisy keys
envcompare .env.example --ignore NODE_ENV --ignore LOG_LEVEL

CI/CD

# Fail on missing keys, JSON output
envcompare .env.example .env.production --exit-code --format json

# Quiet mode - only missing keys, no extra warnings
envcompare .env.example .env.production --exit-code --quiet --missing-only

Security

# Default: values masked
$ envcompare .env.example .env.production
⚠️  Extra in .env.production:
   SECRET_KEY=***SENSITIVE***
   PORT=***present***

# With --show-values: values visible (be careful!)
$ envcompare .env.example .env.production --show-values
⚠️  Extra in .env.production:
   SECRET_KEY=sk-live-abc123
   PORT=8080

# JSON always shows values (for programmatic use)
$ envcompare .env.example .env.production --format json

All Flags

usage: envcompare [-h] [--version] [--exit-code] [--quiet]
               [--format {text,json}] [--ignore KEY]
               [--missing-only] [--show-values]
               example [target]

positional arguments:
  example         Example .env file (usually .env.example)
  target          Target file to compare (default: .env)

options:
  --version, -V   Show version and exit
  --exit-code     Exit 1 if issues found (for CI)
  --quiet, -q     Suppress "extra keys" warnings
  --format        text (default) or json
  --ignore KEY    Skip a key (repeatable)
  --missing-only  Only check for missing keys
  --show-values   Show actual values in output

What envcompare checks

Check What it catches Example
Missing keys Keys in example but not in target SECRET_KEY exists in .env.example but not .env
Extra keys Keys in target but not in example DEBUG=true in .env but not in template (warning only)
Type mismatches Value changed type PORT=8080 vs PORT="8080" (int vs string)

Type checking is heuristic, not semantic. It catches 8080"8080" but won't catch falsetrue (both are booleans) or localhostmalicious-host (both are strings). It's a helpful signal, not a security audit. Use --missing-only if you find it noisy.

Exit Codes

Code Meaning
0 Clean - all keys present, types match
1 Issues found - missing keys or type mismatches
2 Error - file not found or unreadable

CI Integration

GitHub Actions

- name: Verify env files
  run: |
    pip install envcompare
    envcompare .env.example .env.production --exit-code --format json

GitLab CI

check-env:
  script:
    - pip install envcompare
    - envcompare .env.example .env.staging --exit-code --quiet --missing-only

Pre-commit

repos:
  - repo: local
    hooks:
      - id: envcompare
        name: Check .env files
        entry: envcompare .env.example --exit-code
        language: system
        files: \.env$

Security

  • Values masked by default - No accidental secret exposure in terminals or CI logs
  • Sensitive keys detected - Keys containing SECRET, TOKEN, PASSWORD, KEY, etc. show ***SENSITIVE*** instead of masked values
  • JSON exposes values - --format json shows actual values. This is intentional for CI tooling that needs to parse output. Use only in secure CI environments.

Masking is heuristic, not cryptographic. Value prefixes (like sk_live_ for Stripe) may be visible. This is a terminal display tool, not a redaction engine. Don't pipe output to untrusted systems.

Known Trade-offs

Trade-off Why
Type checking is on by default Catches real config issues; use --missing-only if noisy
KEY substring matches MONKEY Over-masking is safer than under-masking
Value prefixes may be visible 4-char mask reveals sk_***ef for Stripe keys
JSON always shows values CI tooling needs real values to parse

Philosophy

  • Zero config - No .envcomparerc, no YAML, no JSON. CLI flags only.
  • Security-first defaults - Values masked. Secrets protected. Explicit opt-in to see them.
  • One thing well - Compare env files. Not a linter, not a validator, not a manager.
  • CI-native - Exit codes, JSON output, quiet mode. Works in pipelines.
  • Honest about limitations - Type checking is heuristic. Masking is best-effort. Documented, not hidden.

Development

git clone https://github.com/0xProgress/envcompare.git
cd envcompare
pip install -e ".[dev]"
pytest            # 75 tests
pytest --cov      # with coverage

License

Licensed under MIT

Contributing

Issues and PRs welcome. Before adding features:

  • Does it require a config file? → No.
  • Does it work in CI? → Must support exit codes and JSON.
  • Does it keep the tool under 500 lines? → If not, it better be worth it.
  • For more info click here

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

envcompare-0.1.0.tar.gz (19.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

envcompare-0.1.0-py3-none-any.whl (8.6 kB view details)

Uploaded Python 3

File details

Details for the file envcompare-0.1.0.tar.gz.

File metadata

  • Download URL: envcompare-0.1.0.tar.gz
  • Upload date:
  • Size: 19.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.1

File hashes

Hashes for envcompare-0.1.0.tar.gz
Algorithm Hash digest
SHA256 f4539954b18ec55a1356d626105b61e838d21f5dda9fc603f1d61033f0bba2b1
MD5 7ad364435beacf69adefeb6ed240b387
BLAKE2b-256 b698a520ba9babdf4ca5cd87d21ee77f884f3b1916ca5beecc37c2936da446f8

See more details on using hashes here.

File details

Details for the file envcompare-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: envcompare-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 8.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.1

File hashes

Hashes for envcompare-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 510206111707e37173b5e450d27ea5c5128b35d4158669e8b4f967f9c0a91362
MD5 7fb9f3ac95afdd0f3772fa05d7d2b344
BLAKE2b-256 3bc586a276bd3c5ab655656a907b03fb7d6e95494f665b03164af0f2402d7b32

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page