Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ name: CI

on:
push:
branches: [main]
branches: [main, 'feat/**', 'fix/**']
pull_request:
workflow_dispatch:

permissions:
contents: read
Expand Down Expand Up @@ -42,7 +43,7 @@ jobs:
run: go vet ./...

- name: Test
run: go test -race -count=1 ./...
run: go test -race -count=1 -timeout=120s ./...

- name: Build
run: go build ./...
Expand Down
19 changes: 18 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,19 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and

### Added

- M2 runtime, requested TCP-port and Git collectors, plus the local Docker probe needed by ENV006.
- Rules ENV001–ENV006 with deterministic evidence, manual suggestions and rule documentation.
- 54 three-OS golden cases and built-CLI checks for JSON, findings and exit codes.
- `--port` and bounded offline `--fixture-dir` diagnosis, with a committed synthetic demo.
- Runtime, Git and Docker facts in the plain-text report.
- The repository tracks CodeGraph's index ignore rules and documents it as an optional local tool
for code navigation. Nothing in the build or in CI depends on it.

### Changed

- `golang.org/x/sys` updated to v0.48.0 to remove the vulnerable v0.30.0 dependency.
- Feature and fix branches trigger the three-OS CI matrix. Snapshot schema permits absent platform facts
after a failed collector; offline errors retain degraded notices and exit code 3.
- The repository is published at https://github.com/Shellghostt/EnvDoctor and the CI workflow has
executed. The first run on 2026-11-13 passed the Windows, Ubuntu and macOS matrix, the lint job and
the vulnerability scan.
Expand All @@ -25,11 +33,20 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and

### Fixed

- M1 runner deadline now bounds a collector that ignores cancellation.
- Home redaction includes quoted PATH entries and paths embedded in collector/timing errors.
- Broken symlinks do not shadow usable runtimes; Python venvs stay distinct from their system symlink
targets; unrelated numeric directories do not become runtime versions.
- Per-user Windows Docker locations are recognized. Invalid Git config and missing macOS tools cause
degraded inspection instead of silent clean results. Git/Docker context overrides are removed.
- Port verification commands include the requested ports. Offline input rejects extra JSON documents.
- PATH permission/inspection failures are degraded rather than converted to missing-directory evidence;
malformed paths and non-directory ancestors still produce broken-entry warnings with raw text preserved.
- Four writes whose error return was discarded now say so explicitly with `_, _ =`.

### Planned

- M2: runtime, port and Git collectors, plus the first diagnostic rules with golden fixtures.
- M3: rules ENV007–ENV012, full Docker/dependency inspection and the `explain` command.

## [0.1.0] - 2026-11-13

Expand Down
5 changes: 5 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ make test # unit tests
make lint # formatting and static analysis
```

`go test -count=1 -timeout=120s ./...` also builds the CLI and runs all 54 golden cases through it.
Each new rule needs positive, negative and edge snapshots for Windows, Linux and macOS. Expected
findings are reviewed evidence, not output regenerated from the implementation. See
[the validation record](docs/testing/m1-m2-validation.md) for Windows and Linux race commands.

## Code navigation

[CodeGraph](https://github.com/colbymchenry/codegraph) builds a local symbol index of this
Expand Down
116 changes: 78 additions & 38 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@

> A cross-platform CLI that detects, diagnoses and helps resolve development environment problems.

![status](https://img.shields.io/badge/status-M1_collectors-orange)
![status](https://img.shields.io/badge/status-M2_rules-orange)
[![license](https://img.shields.io/badge/license-MIT-blue)](LICENSE)
![Go](https://img.shields.io/badge/go-1.26-00ADD8?logo=go&logoColor=white)

**Status: M1 (v0.1.0).** EnvDoctor collects platform, shell and PATH facts and prints them as text or JSON,
with a documented exit-code contract. **There are no diagnostic rules yet**, so it cannot tell you that
anything is wrong: `diagnose` lists facts and then reports zero findings. Nothing is modified on your machine.
**Status: M2 complete (unreleased).** EnvDoctor collects platform, shell, PATH, Python/Node runtime,
requested TCP-port, Git and local Docker facts. Six rules explain conflicts and suggest manual next steps.
Reports are plain text or versioned JSON. Diagnosis never applies fixes or executes interpreters found on PATH.

## Why this exists

Expand All @@ -21,18 +21,19 @@ explains the cause instead of dumping raw output.

| Capability | Milestone | Status |
|---|---|---|
| Detect conflicting Python and Node versions | M2 | Not started |
| Identify broken PATH entries | M2 | Facts reported; no rule yet |
| Detect port conflicts | M2 | Not started |
| Diagnose Docker daemon problems | M3 | Not started |
| Detect conflicting Python aliases and Node candidates | M2 | **Done**, when metadata provides the required evidence |
| Identify broken PATH entries | M2 | **Done** (raw entries preserved after home redaction) |
| Detect requested TCP port conflicts | M2 | **Done**, owner PID/name where permitted |
| Detect unreachable local Docker engine | M2 | **Done** (ENV006) |
| Inspect Docker contexts and version skew | M3 | Not started |
| Identify missing system dependencies | M3 | Not started |
| Detect incompatible package managers | M3 | Not started |
| Check Git configuration | M2 | Not started |
| Collect effective Git identity and autocrlf | M2 | **Done**; Git diagnostic rules are M3 |
| Generate diagnostic bundles | M4 | Not started |
| Human-readable reports | M4 | Basic text report (M1); polish is M4 |
| Human-readable reports | M2/M4 | **Done** plain text; interactive polish is M4 |
| JSON export | M1 | **Done** |
| Safe, opt-in automated remediation | M5 | Not started |
| Windows, Linux and macOS support | M6 | Collectors build for all three; CI matrix not yet run |
| Windows, Linux and macOS support | M2/M6 | Three-OS code and fixtures; signed release/clean-host verification is M6 |

## Architecture

Expand Down Expand Up @@ -64,34 +65,38 @@ might not exist, and no fix may run without explicit confirmation.

## Quickstart

Requires Go 1.26+.
Requires Go 1.26+. From the repository root, in PowerShell:

```bash
make build
./bin/envdoctor diagnose
```powershell
go build -o bin/envdoctor.exe ./cmd/envdoctor
.\bin\envdoctor.exe diagnose
.\bin\envdoctor.exe diagnose --port 3000,8080
```

On Linux/macOS use `go build -o bin/envdoctor ./cmd/envdoctor` and `./bin/envdoctor diagnose`.
Port inspection runs only when you provide `--port`; a listening service is not automatically a conflict.

Try the committed **synthetic** demo without inspecting your machine:

```powershell
.\bin\envdoctor.exe diagnose --fixture-dir tests/demo
.\bin\envdoctor.exe diagnose --fixture-dir tests/demo --rule ENV005
```
EnvDoctor 0.1.0 - environment diagnosis
Generated 2026-11-13T01:41:49Z

Platform windows 11 (build 26300) amd64
Elevated no
Shell powershell
PATH 26 entries, 2 missing, 0 duplicate
The demo intentionally fires all six rules and exits 2. Its port finding includes:

Missing PATH entries:
- C:\Windows\System32
- ~\AppData\Local\Programs\Antigravity\bin
```text
[ERROR] Requested TCP port is already in use

Rules 0 registered in this build
Detected:
- TCP 3000: demo-node pid 123

No findings.
Verify:
envdoctor diagnose --rule ENV005 --port 3000
```

That output is real, captured from `goreleaser build --snapshot` on Windows 11 while developing M1. The
leading space in the first entry is part of the value: it is a malformed PATH element, and that is exactly
what the collector is for.
This excerpt was produced by the CLI using `tests/demo/snapshot.json`; its process and paths are examples.
The [validation record](docs/testing/m1-m2-validation.md) also records a real Windows listener test.

Machine-readable output, for an editor or CI:

Expand All @@ -105,19 +110,53 @@ in the test suite; the snapshot inside it is described by
with `~` before anything is written.

Exit codes: `0` nothing at or above warning, `1` warnings, `2` at least one error, `3` the tool itself failed
(including a collector timeout), `4` invalid usage. Today only `0`, `3` and `4` are reachable, because zero
rules are registered.
(including a collector timeout), `4` invalid usage. Captured collector errors retain exit 3 during offline
diagnosis. A degraded report still contains the findings that could be evaluated.

`--component` and `--rule` narrow findings. Collection still covers the full snapshot. `--port` accepts
comma-separated values and repeated flags. `--fixture-dir` reads one version-1 `snapshot.json` up to 8 MiB;
it rejects unknown fields, trailing documents and unsupported versions, and invokes no collectors.

## Implemented rules

| Rule | Evidence and behavior |
|---|---|
| [ENV001](docs/rules/ENV001.md) | Known differing versions for active Python command aliases |
| [ENV002](docs/rules/ENV002.md) | Multiple Node candidates or a missing selected target |
| [ENV003](docs/rules/ENV003.md) | Missing raw PATH entries |
| [ENV004](docs/rules/ENV004.md) | A selected environment shadowed by an earlier runtime |
| [ENV005](docs/rules/ENV005.md) | A requested TCP port held by a listening process |
| [ENV006](docs/rules/ENV006.md) | Installed Docker CLI with an unreachable default local engine |

Runtime versions come from known manager paths, Windows file resources or venv metadata. Unknown versions
stay unknown; Unix system interpreters and opaque manager shims may not provide enough metadata. Expected
targets use `VIRTUAL_ENV`, `NVM_BIN` or `NVM_SYMLINK`. Git/Docker tools are found only in known installation
locations. Docker probes its default local socket rather than a selected remote context.

Home paths are redacted, including quoted paths and error notices. JSON includes Git identity; review it
before sharing. Diagnostic bundles with broader redaction remain M4.

## Testing

```powershell
$env:GOTOOLCHAIN='go1.26.11'
go test -count=1 -timeout=120s ./...
go vet ./...
go build ./...
```

`--component` and `--rule` narrow which rules are evaluated. With no rules, either flag can only produce an
empty result or an "unknown rule" error.
The suite includes 54 exact golden cases: six rules × three OS snapshots × positive/negative/edge cases.
It also builds the CLI and checks those cases through real subprocesses, validating JSON and exit codes.
CI runs the full race-enabled suite on Windows, Ubuntu and macOS. Local Linux race commands and measured
results are in [docs/testing/m1-m2-validation.md](docs/testing/m1-m2-validation.md).

A local release build of all six targets:

```bash
goreleaser build --snapshot --clean
```

`goreleaser check` additionally wants a git remote, so it only passes once the repository is pushed.
`goreleaser check` and the local six-target snapshot build passed. A snapshot build does not publish a release.

## Repository layout

Expand All @@ -126,22 +165,23 @@ goreleaser build --snapshot --clean
| `cmd/envdoctor` | CLI entry point |
| `internal/cli` | Command wiring, flags, exit codes |
| `internal/config` | Resolved options, schema version, collector timeout |
| `internal/collector` | Collector interface, registry, parallel runner |
| `internal/collector/{windows,linux,darwin}` | Reserved for build-tagged collectors (empty in M1) |
| `internal/collector` | Parallel runner and platform/PATH/runtime/port/Git/local-Docker collectors |
| `internal/diagnostic` | Rule engine, severity model, fixture harness |
| `internal/report` | Terminal renderer, JSON renderer |
| `internal/fix` | Remediation plans, dry-run by default (empty until M5) |
| `internal/system` | Thin wrappers over OS facilities, the only place syscalls live |
| `docs/schema` | Committed JSON schemas, enforced by tests |
| `tests/golden` | Snapshot-to-diagnosis fixtures, one per rule |
| `tests/golden` | 54 snapshot-to-diagnosis fixtures |
| `tests/integration` | Built CLI tests against all golden cases |
| `tests/demo` | Synthetic offline snapshot demonstrating all six rules |
| `deploy` | Release and packaging notes |

## Roadmap

| Milestone | Deliverable | Status |
|---|---|---|
| M1 | CLI foundation, collector interface, system information | **Done** (v0.1.0) |
| M2 | Cross-platform collectors: runtimes, PATH, ports, Git | Not started |
| M2 | Cross-platform collectors and rules ENV001–ENV006 | **Done (unreleased)** |
| M3 | Rules ENV007–ENV012, Docker and dependency inspectors, `explain` | Not started |
| M4 | Polished terminal output, Markdown export, diagnostic bundles | Not started |
| M5 | Safe, confirmed automated remediation | Not started |
Expand Down
29 changes: 25 additions & 4 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# EnvDoctor Architecture

**Status: partially implemented (M1).** The collector pipeline, platform, shell and PATH collection, and
the JSON contract exist. There are no diagnostic rules yet, so EnvDoctor reports no findings on any machine.
This document describes intended structure, not delivered behaviour. The authoritative specification is
[PLAN.md](PLAN.md), and section 0 there is the current truth.
**Status: M2 complete, unreleased.** Seven collectors feed six pure rules and a plain-text/JSON report.
Dependency inspection, broader Docker rules, bundles, automatic fixes and interactive output remain planned.
The authoritative specification is [PLAN.md](PLAN.md); [ADR 0004](adr/0004-m2-collectors-and-rules.md)
records the implemented evidence and inspection boundaries.

## Context

Expand Down Expand Up @@ -39,6 +39,27 @@ flowchart TD
See [PLAN.md](PLAN.md) section 6 for the responsibility of each box, the request and event flows, and the
failure behaviour of each stage.

The live path runs platform, shell, PATH, runtime, requested-port, Git and local-Docker collectors in
parallel with a two-second deadline. Each returns a typed part; results merge in registration order.
Failed or late results contribute an error rather than partial facts. The runner's wait is bounded even
when a kernel filesystem operation ignores cancellation.

Runtime discovery reads metadata without executing interpreters or shims. Windows uses TCP table and
query-only process APIs, Linux uses `/proc`, and macOS uses a known lsof installation. No port inspection
runs without `--port`. Git and Docker use known absolute CLI locations with fixed arguments, bounded
output and sanitized context overrides. Docker contacts only its default local engine.

After home redaction, rules ENV001–ENV006 use only the snapshot. Rule/component filters affect evaluation,
not collection. Findings are validated, sorted by severity and rendered with evidence, possible cause,
manual next steps and verification commands. Failed collectors disable dependent rules and give exit 3.

The offline path decodes a bounded versioned snapshot and skips collectors, then uses the same redaction,
engine and report. Captured errors retain degraded notices. Both paths write exactly one JSON document
under `--json`. Platform fields may be absent after a failed collector; no facts are invented to fill them.

Home redaction is not an identity/secret scrubber: Git identity is part of the JSON snapshot. The future
bundle feature must define its own sharing policy. No current command applies a fix.

## What is deliberately absent

See [PLAN.md](PLAN.md) section 4 (non-goals). If a component is not on that diagram, it is not part of the
Expand Down
Loading
Loading