Zero-config .env file comparison - catch missing keys before production
Project description
envcompare
Compare .env files in one command. Zero config. Security-first.
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 false→true (both are booleans) or localhost→malicious-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 jsonshows 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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f4539954b18ec55a1356d626105b61e838d21f5dda9fc603f1d61033f0bba2b1
|
|
| MD5 |
7ad364435beacf69adefeb6ed240b387
|
|
| BLAKE2b-256 |
b698a520ba9babdf4ca5cd87d21ee77f884f3b1916ca5beecc37c2936da446f8
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
510206111707e37173b5e450d27ea5c5128b35d4158669e8b4f967f9c0a91362
|
|
| MD5 |
7fb9f3ac95afdd0f3772fa05d7d2b344
|
|
| BLAKE2b-256 |
3bc586a276bd3c5ab655656a907b03fb7d6e95494f665b03164af0f2402d7b32
|