Skip to content

sen-ltd/env-diff

Repository files navigation

env-diff

A zero-dependency TypeScript CLI that compares two .env files and tells you exactly which keys were added, removed, or changed — without ever printing the values.

.env files are secrets by default. A diff tool for them has to be default-deny: no values in the output unless you opt in with a loud flag.

How it works

For every key that exists in both files, env-diff hashes the value with SHA-256 and keeps the first 8 hex characters (32 bits). If the two truncated hashes match, the values are the same. If they differ, the values differ. The raw value never appears in stdout, stderr, or any formatter output, even when --format json — unless you explicitly pass --show-values, which prints a loud warning first.

That means you can safely pipe env-diff .env.staging .env.prod into a CI log and the log is still safe to store in Datadog, ship to Slack, or paste in a PR comment.

Install

Docker (recommended for CI)

docker run --rm -v "$PWD":/work ghcr.io/sen-ltd/env-diff \
  /work/.env.staging /work/.env.prod

From source

git clone https://github.com/sen-ltd/env-diff
cd env-diff
npm install
npm run build
node dist/main.js .env.staging .env.prod

Requires Node 20+. Runtime dependencies: zero.

Usage

env-diff <a.env> <b.env> [options]

Examples

Compare two env files and see what drifted:

$ env-diff .env.staging .env.prod
env-diff .env.staging .env.prod

~ DATABASE_URL  (2f99fe02 -> 146593f6)
~ LOG_LEVEL     (06271baf -> 0b8e9e99)
+ NEW_FEATURE_FLAG  (b5bea41b)

summary: +1 added, -0 removed, ~2 changed, =1 same, 0 excluded

Machine-readable for piping into jq:

$ env-diff a.env b.env --format json | jq '.counts'
{
  "added": 1,
  "removed": 0,
  "changed": 2,
  "same": 1,
  "excluded": 0
}

Fail a CI job when env files drift:

$ env-diff .env.staging .env.prod --fail-on-diff
# exit 1 if any add/remove/change

GitHub Actions annotations (visible on the PR "Files changed" view):

- name: check env drift
  run: env-diff .env.example .env.ci --format github

Ignore keys that are expected to differ (build timestamps, CI job IDs):

$ env-diff a.env b.env --exclude TIMESTAMP,BUILD_ID
$ env-diff a.env b.env --exclude-pattern '^CI_'

Really, truly need to see the values (local debugging only, never CI):

$ env-diff a.env b.env --show-values
WARNING: --show-values is enabled. Actual .env values will be printed. ...

Flags

Flag Effect
--format text|json|github Output format. Default text.
--show-values Print actual values. Emits a loud stderr warning first.
--exclude KEY1,KEY2 Ignore these literal keys.
--exclude-pattern REGEX Ignore keys matching this regex.
--fail-on-diff Exit 1 if anything changed.
--include-same Also show keys that are identical.
--help / --version Self-explanatory.

Exit codes

Code Meaning
0 No differences, or differences found without --fail-on-diff.
1 Differences found and --fail-on-diff was set.
2 Bad command-line arguments or IO error.

Parser support

Feature Supported
KEY=value
export KEY=value
# full-line comment
KEY=value # trailing comment ✅ (unquoted only)
KEY="double quoted" with \n \r \t \\ \" escapes
KEY='single quoted' (literal)
Empty values (KEY=, KEY="")
CRLF line endings
Variable interpolation (KEY=$OTHER) ❌ — deliberate
Multi-line values (\ continuation, heredocs) ❌ — deliberate

Malformed lines are reported to stderr with their 1-indexed line number; the rest of the file still parses.

The hash

Why SHA-256 truncated to 8 hex chars?

  • Full SHA-256 is noise in the terminal. You don't need 64 characters to compare two fingerprints at a glance; 8 is enough.
  • 32 bits is plenty for this job. For a typical env file with <100 keys, the birthday collision probability per pair is ≈ n² / 2^33, which is "you should worry about cosmic rays first" territory.
  • It's a fingerprint, not a commitment. The tool's safety property is "values are never printed". The hash is not defending against preimage attacks (which would be silly anyway — for a value like true, 8 hex chars of SHA-256 is trivially rainbow-tableable). It's just a witness.

Build from source

npm install
npm test          # 47 vitest tests
npm run build     # emit dist/main.js

License

MIT

Links

About

A zero-dependency TypeScript CLI that compares two .env files and tells you which keys were added, removed, or changed — without ever printing the values. The trick: every value is hashed with SHA-256 truncated to 8 hex characters, and the tool prints the hash instead of the value.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors