Repository navigation
fix(shim): prune orphan shims on reshim - #274
Merged
Merged
Conversation
`dtvem doctor` reported shim-map cache drift that `dtvem reshim` could
never clear, so doctor kept prescribing a command that did nothing.
Rehash rebuilt the cache from the executables found in the installed
runtime versions but only ever *added* shim files, so the two sets could
only diverge. A shim whose backing executable was gone -- a pip or npm
package that was later uninstalled, a removed runtime version, or a
phantom shim from an older dtvem that trusted the provider's static
Shims() list -- stayed in shims/ forever. RemoveShim had no non-test
callers at all.
Rehash now deletes shim files absent from the freshly-scanned map, and
reports what it removed. Guards on the destructive path:
- dtvem and dtvem-shim are never pruned; installs before 0.4 kept
dtvem-shim in shims/ rather than bin/
- only files that are really shim binaries are removed, so a stray
file dropped in shims/ survives
- an empty scan skips pruning entirely -- that signals an unreadable
versions tree, not "everything was uninstalled"
- removal failures are collected and reported, not fatal; a shim
executing right now cannot be deleted on Windows
Invoking an orphan shim also leaked an internal error: mapShimToRuntime
fell back to treating the shim name as a runtime name, so `ruff --help`
died with "runtime provider not found: runtime provider 'ruff' not
found" while shadowing a real ruff deeper in PATH. It now reports
whether any runtime claims the name, and unclaimed shims hand off to the
real command in PATH with a warning naming the fix.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Resolves #273
🐛 The bug
dtvem doctorreported shim-map cache drift and told the user to rundtvem reshim. Running it changed nothing, so doctor reported the same drift forever.🔍 Root cause
Rehashrebuilt the shim-map cache from the executables found in the installed runtime versions, but only ever added shim files — it never enumeratedshims/, andRemoveShimhad zero non-test callers. The cache is rebuilt-from-scan (can shrink); the shims directory was append-only (can only grow). The two sets could only diverge, and doctor diffs exactly those two sets.Orphans came from two sources:
pytest,ruff,virtualenv— pip console scripts shimmed by a post-pip installreshim, whose backingScripts/*.exelater went away.python3— created unconditionally at install time from Python's staticShims()list, but Windows python-build-standalone ships nopython3.exe, so the disk scan never re-recorded it. (Install no longer does this as of theDiscoverShimsForVersionchange already on main; this cleans up the shims that change left behind.)Invoking an orphan shim leaked an internal error, too —
mapShimToRuntimefell back to treating the shim name as a runtime name:Worse, that dead shim sits early on PATH and shadows a real
ruffinstalled elsewhere.✅ The fix
Rehashnow prunes. After rebuilding the map it deletes shim files absent from it, and reports what it removed. Guards on the destructive path:dtvemanddtvem-shimare never pruned — installs before 0.4 keptdtvem-shiminshims/rather thanbin/, and deleting it would break every other shim.exeat the shim path on Windows; exec bit on Unix), so a stray file dropped inshims/survivesUnclaimed shims fall through to PATH.
mapShimToRuntimenow reports whether any runtime actually claims the name. Unclaimed shims hand off to the real command deeper in PATH with a warning naming the fix, instead of fabricating a provider lookup.uninstallalready callsRehash, so removing a runtime version now cleans up its shims as a side effect.🧪 Verification
End-to-end against a synthetic
DTVEM_ROOTreproducing the reported state (Python 3.13.11 providing onlypython+pip;pytest,python3,ruff,virtualenvshims on disk; cache knowing onlypython):Before — reproduces the report exactly:
After
dtvem reshim:Orphan shim invocation, both branches:
Confirmed a known runtime with no version configured still takes the existing system-PATH fallback rather than the new unknown-shim path.
11 new tests (prune keeps backed shims / removes unbacked / takes the Windows
.cmdcompanion / protects dtvem binaries / leaves non-shim files alone / empty scan keeps shims, aRehashregression test asserting cache and disk agree afterward, andmapShimToRuntimecoverage for claimed and unclaimed names)../rnr checkpasses — 26 packages, 0 lint issues.