Skip to content

feat: detect rust/cargo tool versions, add brew-by-name update fallback - #41

Merged
stanlyzoolo merged 2 commits into
mainfrom
feat/rust-version-update-detect
Jul 24, 2026
Merged

feat: detect rust/cargo tool versions, add brew-by-name update fallback#41
stanlyzoolo merged 2 commits into
mainfrom
feat/rust-version-update-detect

Conversation

@stanlyzoolo

Copy link
Copy Markdown
Owner

Fixes three problems with tools whose binary won't report its own version. Implements the plan in docs/plans/completed/20260724-rust-version-update-detect.md (Variant A: automatic fallbacks, no manual config).

What changed

1. rust had no updater. It ships rustc/cargo, so exec.LookPath("rust") misses and Detect returned ErrUnknownManager — the "no known updater" dead end. On a LookPath miss Detect now checks for a Cellar/<name> or Caskroom/<name> directory and plans brew upgrade <name>. The branch is self-validating (fires only when such a keg exists) and shares version.brewDirVersion's traversal guard.

2. Rust TUIs read as ✕ not found and logged an anomaly on every startup. A ratatui app ignores --version, boots its TUI and panics with no terminal. The capture carried the alt-screen signature plus ratatui-0.30.2 — which versionRe would have mis-parsed as the tool's version once the non-zero exit stopped masking it. InstalledVersion now drops such captures (isTUITakeover, its own copy since version sits below model in the import graph) and falls back to cargo install --list, which knows the version without running the binary at all.

3. installed: collapsed two different states into ✕ not found. InstalledVersion now returns (ver string, present bool) and the card renders them apart: ✓ no version (green ui.OkStyle) for a working install that won't name a version, ✕ not installed (red DangerStyle) for a genuine absence.

Source order is --version/-V → brew directory → cargo list, with the cargo probe gated on the binary existing (an absent tool can only miss, and the probe costs a subprocess).

Verification

Against a real tracker of 23 tools:

tool before after
inertia ✕ not found + a log line every startup 0.1.0 via cargo list, no log
rust [u] → "no known updater" 1.96.1 via brew dir, [u]brew upgrade rust
keeptui ✕ not found ✓ no version

Every new test was mutation-checked — confirmed to fail when the path it covers is broken:

  • remove the cargo fallback → TestInstalledVersionCargoFallback red
  • swap brew/cargo order → TestInstalledVersionBrewBeatsCargo red
  • drop the binaryExists gate → TestInstalledVersionCargoGatedOnPresence red (3/3 asserts)
  • drop info.InstalledPresent = msg.presentTestInstalledMsgCarriesPresence red
  • drop present in fetchInstalledCmdTestFetchInstalledCmd red

Two of those tests were themselves broken on the first pass and only the mutation check caught it: a marker written with touch never resolved (PATH is the fixture dir alone), and an assert on present == false was vacuous since false is the zero value.

preflight green: go build / go vet / go test -race ./... / golangci-lint run (0 issues).

Known limitations, deliberately not addressed

  • A clean start still creates a log when keeptui itself is tracked and answers keeptui dev — unparseable, and a legitimate anomaly by the log's own rule. Pre-existing, unrelated to this branch.
  • A brew cask pinned to version :latest with no PATH binary still reads ✕ not installed: brewDirVersion can't tell "no keg" from "keg with no parseable version". 0 of 15 casks on the test machine are in that state.
  • .. and . pass the formula-name traversal guard. Harmless — Stat only, no writes, and brew rejects the command — and mirrored from version.brewDirVersion, so it should be fixed in both or neither.
  • Whether keeptui offers a rust update (the marker) depends on rust-lang/rust publishing GitHub Releases. The dead end is gone either way; the data source is out of scope.

🤖 Generated with Claude Code

stanlyzoolo and others added 2 commits July 24, 2026 23:44
Plan for Variant A: automatic package-manager fallbacks symmetric to
brewDirVersion — cargo-list installed-version detection + isTUITakeover
guard in internal/version, brew-by-name update fallback in internal/updater,
plus a present/not-installed split in the "installed:" card line.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Three problems, all around tools whose binary won't name its own version.

`rust` had no updater: it ships rustc/cargo, so exec.LookPath("rust") misses
and Detect returned ErrUnknownManager — the "no known updater" dead end. On a
LookPath miss Detect now looks for a Cellar/<name> or Caskroom/<name>
directory and plans `brew upgrade <name>`. Self-validating: the branch fires
only when such a keg exists, and it shares brewDirVersion's traversal guard.

Rust TUIs showed "not found" and wrote an anomaly log every startup. A ratatui
app ignores --version, boots its TUI and panics with no terminal; the capture
carried the alt-screen signature plus "ratatui-0.30.2", which versionRe would
have mis-parsed as the tool's version once the non-zero exit stopped masking
it. InstalledVersion now drops such captures (isTUITakeover — its own copy,
version sits below model in the import graph) and falls back to
`cargo install --list`, which knows the version without running the binary.

The "installed:" line collapsed "installed, version unknown" into "not found".
InstalledVersion returns (ver string, present bool) and the card splits the
two: `✓ no version` in green ui.OkStyle vs `✕ not installed` in DangerStyle.

Fallback order is --version/-V -> brew directory -> cargo list, with the cargo
probe gated on the binary existing. Tests are mutation-checked: removing the
fallback, swapping brew/cargo order, dropping the presence gate and dropping
the present flag each turn a test red.

Docs synced (CLAUDE.md, ARCHITECTURE.md, README.md). Plan moved to
docs/plans/completed/.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@stanlyzoolo
stanlyzoolo merged commit 3176cb2 into main Jul 24, 2026
3 checks passed
@stanlyzoolo
stanlyzoolo deleted the feat/rust-version-update-detect branch July 24, 2026 21:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant