Skip to main content

Root-cause diagnosis for 'passes locally, fails in CI' — ranks the likely cause of environment drift and prints a plain-English fix.

Project description

EnvFixer

Root-cause diagnosis for "passes locally, fails in CI."

EnvFixer captures a privacy-safe fingerprint of a Python environment (interpreter, OS/arch/libc, installed packages, and environment-variable names), compares two of them, and ranks the most likely cause of an environment-drift failure — then prints a plain-English fix for each.

Every other tool in this space stops at "here's the diff." EnvFixer tells you which difference is probably the cause, and how to fix it.

$ envfixer diff local.json ci.json

EnvFixer — diagnosis
=====================

3 candidate cause(s), most likely first:

1. [HIGH  ]  82/100 ████████████████····  package_missing: pydantic
      working: '2.4.2'   failing: None
      why: A package present where the code works is entirely absent where it
           fails; a missing import is the single most common CI failure.
      fix: Install 'pydantic' in the failing environment — add
           'pydantic==2.4.2' to your requirements/lockfile so CI installs it.

2. [MEDIUM]  58/100 ████████████········  python_version: python
      working: '3.11.5'   failing: '3.9.18'
      ...
  • Zero runtime dependencies. The core is pure standard library, so it runs inside any minimal CI container.
  • Privacy-safe by construction. It records environment-variable names only — never values. The fingerprint is plain JSON you can eyeball.
  • A CI guardrail, not just a report. Exit codes and --fail-on let it gate a build when environment drift is the likely culprit.

Install

pip install envfixer      # once published to PyPI

Quick start

Capture a fingerprint inside the environment you want to snapshot (run it in the target virtualenv, e.g. python -m envfixer capture, so you fingerprint the right interpreter):

# on your machine
python -m envfixer capture -o local.json

# in CI
python -m envfixer capture -o ci.json

Diagnose the difference (first argument = where it works, second = where it fails):

envfixer diff local.json ci.json

Have the failing traceback? Pass it — differences it names are boosted to near-certainty:

envfixer diff local.json ci.json --traceback ci-error.log

Health-check a single environment (no second fingerprint needed):

envfixer check

Commands

Command What it does
capture [-o FILE] Write this environment's fingerprint (stdout by default).
diff WORKING FAILING [--traceback FILE] Rank the likely causes of the failure.
check [FINGERPRINT] Surface broken dependency chains in one environment.

Shared reporting flags on diff/check:

  • --format {human,json,markdown,sarif}markdown for PR comments, sarif for inline GitHub code-scanning annotations.
  • --fail-on {none,low,medium,high} — exit non-zero when a cause of at least this confidence is found (default high).
  • --top N — show only the top N results.

Use in CI (GitHub Actions)

- uses: actions/checkout@v4
- uses: actions/setup-python@v5
  with: { python-version: '3.11' }
- run: pip install -e . && pip install envfixer
- run: python -m envfixer capture -o ci.json
# compare against a committed baseline captured locally
- run: envfixer diff local.json ci.json --format markdown --fail-on high

A reusable composite action lives in .github/actions/envfixer.

How the ranking works

The ranking is a transparent rule system, not a black box — every score comes with a human-readable rationale. In brief:

  • Missing package (present where it works, absent where it fails) scores highest — it's the most common real cause.
  • Version mismatches are scored by blast radius (major > minor > patch).
  • Python / libc / arch differences are scored by how often they actually break code (e.g. glibc-vs-musl is weighted heavily).
  • Env-var names are filtered by significance: DATABASE_URL, *_TOKEN, AWS_*, PYTHONPATH score high; shell noise like SHLVL/TERM is dropped.
  • A provided traceback strongly boosts any difference it names.

See envfixer/rank.py for the exact rules.

Evaluation

Because "ranks by likely cause" is an empirical claim, EnvFixer ships a benchmark that measures it against both a synthetic fault injector and a curated real-world corpus (missing test plugins, urllib3 v2, Python 3.12 dropping distutils, Alpine/musl wheel gaps, NumPy 2.0 ABI breaks, …). On the current corpus (18 labelled cases): Top-1 100%, MRR 1.000, 0 false positives. See EVALUATION.md.

python -m evaluation.benchmark

Privacy

EnvFixer never reads environment-variable values, only their names. The fingerprint is human-readable JSON; inspect it before sharing.

Contributing

Issues and PRs welcome — see CONTRIBUTING.md. Good first contributions: new fault scenarios in evaluation/generate.py and new remedies in envfixer/fixes.py.

License

MIT — see LICENSE.

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

envfixer-0.1.0.tar.gz (23.2 kB view details)

Uploaded Source

Built Distribution

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

envfixer-0.1.0-py3-none-any.whl (20.6 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: envfixer-0.1.0.tar.gz
  • Upload date:
  • Size: 23.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for envfixer-0.1.0.tar.gz
Algorithm Hash digest
SHA256 51c68a313ff913c1db6c8912f7882e11076f3cb373a42bbc6ffa1bfcb4e6f4f5
MD5 0e7af9c70404558c6ea9d74f194f30bb
BLAKE2b-256 d5aad0e76595aa0fecaf6951f863fdfe18cf180e87813e035e897f0d96c2d809

See more details on using hashes here.

Provenance

The following attestation bundles were made for envfixer-0.1.0.tar.gz:

Publisher: release.yml on hemanthrayuduu/envdoctor

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

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

File metadata

  • Download URL: envfixer-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 20.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for envfixer-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ffd31f5dd11982c2ea36d6492b506a79709b069b9247fd0f8eb45dca5733ba46
MD5 3eabefaa266efc7b89cb1ee03bbc0dcc
BLAKE2b-256 772ad3b325b0fbb93b98015e429dc17d35471dc6a60ff620246ce44302da3280

See more details on using hashes here.

Provenance

The following attestation bundles were made for envfixer-0.1.0-py3-none-any.whl:

Publisher: release.yml on hemanthrayuduu/envdoctor

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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