Keep your .env.example in sync with the environment variables your code actually uses.
New teammate clones the repo, copies .env.example, runs the app — and it crashes on a
KeyError for some variable nobody documented. envcheck catches that before it happens.
It statically scans your Python (it never imports or runs your code) for os.getenv /
os.environ access, compares the result to your example env file, and reports:
- Undocumented vars — used in code but missing from
.env.example(fails CI) - Possibly unused vars — in
.env.examplebut never read by any code
Zero runtime dependencies, Python 3.8+, CI-friendly.
The
examples/sampleproject deliberately drifts from its.env.exampleso you can try it immediately:envcheck examples/sample.
pip install envcheck-syncOr run from a checkout without installing:
PYTHONPATH=src python -m envcheck path/to/project# Scan the current directory (auto-detects .env.example, .env.sample, ...)
envcheck .
# Point at a specific example file
envcheck . --env-file config/.env.template
# Machine-readable output for tooling
envcheck . --format json$ envcheck examples/sample
Scanned 3 env var(s) in code against examples/sample/.env.example.
Undocumented (1 variable used in code, not in the env file):
- JWT_SECRET (e.g. examples/sample/app.py:9)
Possibly unused (1 variable in the env file, not read by code):
- LEGACY_FLAG
envcheck exits non-zero when undocumented variables are found, so it drops straight into
CI or a pre-commit hook (use --no-fail to report without failing).
Add this to your project's .pre-commit-config.yaml and envcheck runs on every commit:
repos:
- repo: https://github.com/gazzycodes/envcheck
rev: v0.1.1
hooks:
- id: envcheckThen pre-commit install once, or run it on demand with
pre-commit run envcheck --all-files.
- uses: actions/setup-python@v5
with:
python-version: "3.x"
- run: pip install envcheck-sync
- run: envcheck .The non-zero exit on undocumented variables fails the job automatically.
Static, literal-key access in these forms:
os.getenv("X")andgetenv("X")os.environ.get("X")andenviron.get("X")os.environ["X"]andenviron["X"]os.environ.setdefault("X", ...)
Dynamic keys (e.g. os.getenv(some_variable)) are intentionally ignored — envcheck only
reports what it can prove statically, so it never produces noisy false positives.
- Walk the project and parse each
.pyfile with the standard-libraryastmodule. - Collect every literal-key environment-variable access, with its file and line.
- Parse the example env file for declared variable names.
- Diff the two sets and report the gaps.
git clone https://github.com/gazzycodes/envcheck
cd envcheck
PYTHONPATH=src python -m unittest discover -s tests -vContributions are welcome — please open an issue or PR.
MIT — see LICENSE.
