envlens

module
v0.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 20, 2026 License: MIT

README

envlens

Environment Lens

CI codecov Go Report Card Go Reference

A small, zero-dependency Go CLI for the everyday dotenv hygiene tasks: compare two .env files, validate values against rules inferred from key names, and display secrets without leaking them into your terminal scrollback. Drops straight into any CI pipeline as a single static binary.

Installation

Using go install (recommended)
go install github.com/Mikadov/envlens/cmd/envlens@latest
envlens --version
Pre-built binaries (GitHub Releases)

Grab the archive for your platform from the Releases page. Each release ships with a checksums.txt you can verify against.

export VERSION=0.1.0
export OS=linux   # linux | darwin | windows
export ARCH=amd64 # amd64 | arm64

curl -LO "https://github.com/Mikadov/envlens/releases/download/v${VERSION}/envlens_${VERSION}_${OS}_${ARCH}.tar.gz"
tar -xzf "envlens_${VERSION}_${OS}_${ARCH}.tar.gz"
./envlens --version

[!TIP] Windows releases are distributed as .zip. All archives include LICENSE and README.md.

From source
git clone https://github.com/Mikadov/envlens.git
cd envlens
make build          # produces ./bin/envlens
./bin/envlens --version

Requires Go 1.22 or later.

Getting Started

Keep .env and .env.example in sync

The most common use: compare your local .env against the project's tracked .env.example to catch missing or stray keys.

$ envlens diff .env .env.example
Missing in .env (defined in .env.example):
  - STRIPE_SECRET_KEY

Extra in .env (not in .env.example):
  - OLD_API_KEY

Exit code 1 on any difference, 0 on a clean match — so this is the one command you want wired into CI. Add --quiet if you want a silent pass/fail.

Validate values, not just keys

validate infers rules from the key name. A port must be numeric; a scheme-qualified URL must also have a host; a secret must be non-empty — and so on.

$ envlens validate .env
✗ APP_URL  not a valid URL (got "http://")
✗ APP_KEY  must not be empty
✗ DB_PORT  not a valid port number (got "abc")

3 error(s) found.

Keys without a matching rule are skipped silently. Pass --strict to surface them as ? KEY no rule defined.

Inspect a .env safely

cat .env is a decent way to leak secrets into your terminal scrollback. show renders the file as a table with sensitive values masked:

$ envlens show .env
┌─────────────┬───────────┐
│ KEY         │ VALUE     │
├─────────────┼───────────┤
│ APP_NAME    │ MyApp     │
│ APP_KEY     │ ****      │
│ DB_HOST     │ 127.0.0.1 │
│ DB_PASSWORD │ ****      │
└─────────────┴───────────┘

Total: 4 keys

Sensitive keys are those whose name matches _KEY, _SECRET, _TOKEN, or _PASSWORD. --no-mask unhides everything; --json emits machine-readable output (masking still applied).

Commands

envlens diff <file1> <file2>

Compare keys between two .env files. Exit code 1 on any difference, 0 on a clean match. Add --values to also compare values; secrets remain masked as ****.

$ envlens diff .env .env.example
Missing in .env (defined in .env.example):
  - MAIL_FROM_ADDRESS
  - STRIPE_SECRET_KEY

Extra in .env (not in .env.example):
  - OLD_API_KEY

With --values, values are shown inline; sensitive keys are masked from the same rule table used by show:

$ envlens diff --values .env .env.example
Missing in .env (defined in .env.example):
  - MAIL_FROM_ADDRESS = admin@example.com
  - STRIPE_SECRET_KEY = ****

Extra in .env (not in .env.example):
  - OLD_API_KEY = ****
Flag Description
--values Include value differences (masked for sensitive keys)
--quiet Suppress all output; rely on the exit code only
--no-color Disable ANSI colour output

[!NOTE] Colour output is enabled automatically when stdout is a TTY. When piping to a file or tool (e.g., | tee), output is plain text. See Environment variables for overrides.

envlens validate <file>

Check values against rules inferred from each key's name. Keys without a matching rule are skipped silently (pass --strict to list them).

$ envlens validate .env
✗ APP_URL  not a valid URL (got "http://")
✗ APP_KEY  must not be empty
✗ DB_PORT  not a valid port number (got "abc")
✗ FROM_EMAIL  not a valid email address (got "notanemail")
✗ APP_DEBUG  must be one of true/false/1/0 (got "maybe")

5 error(s) found.

A clean file:

$ envlens validate .env
✓ APP_URL  OK
✓ APP_KEY  OK
✓ DB_PORT  OK
✓ FROM_EMAIL  OK
✓ APP_DEBUG  OK

No errors found.

With --strict, keys with no matching rule are surfaced as ?:

$ envlens validate --strict .env
? APP_NAME  no rule defined
✓ APP_URL  OK
? DB_HOST  no rule defined
✓ DB_PORT  OK
Flag Description
--strict Also print keys with no matching rule
--no-color Disable ANSI colour output

See Validation rules for the full suffix table.

envlens show <file>

A safer alternative to cat .env. Sensitive values are masked as **** by default; use --no-mask to unhide, or --json for machine-readable output.

$ envlens show .env
┌─────────────┬───────────┐
│ KEY         │ VALUE     │
├─────────────┼───────────┤
│ APP_NAME    │ MyApp     │
│ APP_KEY     │ ****      │
│ DB_HOST     │ 127.0.0.1 │
│ DB_PASSWORD │ ****      │
└─────────────┴───────────┘

Total: 4 keys

JSON output (masking still applied unless --no-mask):

$ envlens show --json .env
{
  "APP_NAME": "MyApp",
  "APP_KEY": "****",
  "DB_HOST": "127.0.0.1",
  "DB_PASSWORD": "****"
}
Flag Description
--no-mask Display all values without masking (use with care)
--json Emit JSON instead of a table (keys preserve file order)

[!WARNING] --no-mask prints secrets to stdout. Avoid in logs, screenshares, or CI output — anything it produces can end up in scrollback or job archives.

[!NOTE] Masking is triggered by key name, not value. A secret stored under a key that does not match a sensitive suffix (e.g., API_TOKENS_JSON) will be displayed verbatim. Rename the key or extend the rule table.

envlens --version

$ envlens --version
envlens v0.1.0

Development builds (plain go build without GoReleaser) print envlens dev.

Exit codes

Exit codes are the CI-integration contract. They are stable across versions.

Code Meaning
0 Success. No differences / no validation errors.
1 diff found differences, or validate found one or more errors.
2 Usage error (bad flag, wrong arg count) or I/O error (missing file).

[!TIP] Pair envlens diff --quiet with || exit 1 in shell scripts when you want the first failure to short-circuit, without any stdout noise.

Validation rules

Rules are matched by key name suffix, case-insensitive:

Suffix Validation
_URL, _URI Non-empty and whitespace-free. Scheme-qualified values (scheme://…) must have a scheme; file:// URIs may have an empty host, other schemes require one. Bare values like localhost, localhost:3000, example.com, or 127.0.0.1:5432 are accepted.
_PORT Integer in range 1–65535
_KEY, _SECRET, _TOKEN, _PASSWORD Non-empty; value is also masked
_EMAIL, _ADDRESS Valid email address
_ENABLED, _DEBUG, _FLAG One of true, false, 1, 0

Keys that don't match any suffix are skipped silently by default. Pass --strict to surface them as ? KEY no rule defined.

[!NOTE] Adding a new rule is a five-line change in internal/validate/rules.go; see the "Adding a validation rule" section of CONTRIBUTING.md.

Environment variables

envlens itself reads very few env vars — it only cares about how the terminal wants to be treated:

Variable Effect
NO_COLOR Any non-empty value disables ANSI colour output (honours the NO_COLOR standard).
TERM=dumb Also disables ANSI colour output.

Colour is additionally suppressed automatically when stdout is not a TTY (e.g., when piping into tee, redirecting to a file, or running inside most CI log viewers). You can always force it off with --no-color.

CI integration

Drop envlens into any pipeline that can run a single binary. Example GitHub Actions workflow:

name: env-check
on: [push, pull_request]

jobs:
  envlens:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-go@v5
        with:
          go-version: "1.22"
      - run: go install github.com/Mikadov/envlens/cmd/envlens@latest

      - name: Keys in .env.example must match the CI env
        run: envlens diff .env.example .env.ci

      - name: Values in .env.ci must be well-formed
        run: envlens validate .env.ci

[!TIP] The exit code is the contract. You don't need any custom --format or JSON post-processing to wire envlens into a CI that understands non-zero exits — which is all of them.

Parser specification (v1)

The parser is hand-written on top of bufio + strings, with no external dependencies. It deliberately stays close to the subset of .env syntax that most dotenv dialects agree on.

Supported

# a comment line
KEY=value
KEY="double quoted value"
KEY='single quoted value'
KEY=                        # empty value (allowed)
KEY=value  # inline comment (requires whitespace before #)
export KEY=value            # export prefix is stripped

Values that contain # without preceding whitespace are treated as literal, which is usually what you want:

COLOR=#ff0000                # value = "#ff0000"
URL=http://host/page#frag    # value = "http://host/page#frag"

Duplicate keys are accepted; the last occurrence wins. A warning is printed to stderr (along with any unmatched-quote, missing-=, or invalid-key warnings) and does not affect the exit code.

Cross-platform nuances

  • UTF-8 BOM at the start of the file is stripped transparently.
  • Windows line endings (\r\n) are handled.
  • Multibyte values (CJK, emoji) are preserved; the table renderer approximates East Asian width so alignment is usually correct, with rare drift on exotic grapheme clusters.

Limitations

Deliberately out of scope for v1 — documented here so there are no surprises:

Not supported What envlens does instead
Variable interpolation (${OTHER_KEY}) Value is kept as literal text (no expansion)
Multi-line values (backslash or newline quote) Line is skipped with a warning
Nested or escaped quotes Line is skipped with a warning
.env variant resolution (.env.local chain) Compare individual files with diff
User-defined schema Use key name suffixes (rule table)

Variant resolution and custom schemas are the most commonly requested additions and are tracked for a future release.

Troubleshooting

My secret key is not being masked.

Masking is triggered by key name, not value. Only keys whose name ends with _KEY, _SECRET, _TOKEN, or _PASSWORD (case-insensitive) are masked. Rename the variable (e.g., FOO_SECRET rather than FOO_CREDENTIALS) or add a new suffix in internal/validate/rules.go.

Output has garbled \x1b[31m escape sequences.

Your terminal or log viewer is not interpreting ANSI colour codes. Either set NO_COLOR=1, TERM=dumb, or pass --no-color. CI log viewers usually strip colour correctly; this typically shows up in older Windows consoles or in saved log files.

Table columns look misaligned for CJK / emoji values.

envlens approximates East Asian width using a stdlib-only heuristic (see runeWidth in internal/display/display.go). Rare grapheme clusters and exotic emoji may drift one column. For strict alignment, pipe through column -t -s '│' or use --json.

envlens --version prints envlens dev.

You are running a development build (go build without ldflags). Release archives from GitHub Releases and binaries from go install ...@v0.1.0 print the tagged version. See .goreleaser.yaml for the build recipe.

Line 7 of my .env is being skipped with a warning.

v1 does not support multi-line values, variable interpolation, or nested quotes. The parser skips the offending line and continues — it never panics. Reshape the value (e.g., JSON-encode it onto a single line) or wait for a release that supports the construct you need.

Development

make build   # build ./bin/envlens
make test    # unit tests (internal + cmd)
make e2e     # end-to-end tests against the compiled binary
make cover   # HTML coverage report
make lint    # golangci-lint
make vuln    # govulncheck
make all     # lint + vuln + test + e2e

Further reading:

  • CONTRIBUTING.md — commit message conventions, PR checklist, and how to add a validation rule.
  • SECURITY.md — vulnerability reporting.
  • CLAUDE.md — architecture notes, test strategy, and coverage targets.

License

MIT © 2026 Mikadov

Directories

Path Synopsis
cmd
envlens command
Command envlens is a zero-dependency CLI for comparing, validating, and displaying .env files.
Command envlens is a zero-dependency CLI for comparing, validating, and displaying .env files.
internal
diff
Package diff computes key-level (and optionally value-level) differences between two parsed .env files and renders them to an io.Writer.
Package diff computes key-level (and optionally value-level) differences between two parsed .env files and renders them to an io.Writer.
display
Package display renders parsed .env entries either as a Unicode box-drawing table (default) or as JSON.
Package display renders parsed .env entries either as a Unicode box-drawing table (default) or as JSON.
parser
Package parser implements a small, stdlib-only parser for .env files.
Package parser implements a small, stdlib-only parser for .env files.
validate
Package validate infers validation rules from key name suffixes and runs them against parsed values.
Package validate infers validation rules from key name suffixes and runs them against parsed values.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL