fix: stop the plugin update guard firing on npm install artifacts - #267
Conversation
`plugin install` runs `npm install` in the checkout, so a plugin repo without a .gitignore reports node_modules/ and package-lock.json as untracked. The dirty-checkout guard then refused every update — blaming the user for work webcmd itself created, and leaving such plugins permanently un-updatable short of --force. getDirtyFiles now drops those paths (at any depth, covering monorepo sub-plugin installs) before the guard decides. Anything else untracked is still real user work and still blocks. Fixes the plugin-management E2E. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
🟢 No documentation gap found — medium confidenceThe automated review found no documentation gap in the supplied changes. This review is advisory and does not block merging. |
Maintainer review: changes requestedThe underlying concern is valid. The current filter weakens the data-loss guard too far, though. Blocking behavior
Lines 571 to 591 in 4eb9c54 That means all of these can be silently removed from the dirty set:
The new test currently codifies the unsafe behavior: it supplies Required behaviorPlease ignore only proven install-created untracked entries:
In other words, decide from the two status columns before path filtering. Avoid trimming away the leading status column before interpreting it. Tracked/staged/deleted/unmerged/renamed entries must never be exempted merely because their path looks generated. Required testsPlease retain the reproduction for: and add separate assertions that each still blocks: At least one tracked path under Scope and deploymentThis is local plugin-update behavior only. Hosted plugin installation/update routes through Cloud and does not call this local replacement guard, so no Cloud deployment or package-pin change is needed. Once the exemption is limited to |
Read the two porcelain status columns before any path filtering, so only `??` entries at node_modules/package-lock.json paths are treated as install output. Tracked, staged, deleted, renamed and unmerged entries at those same paths are user work updatePlugin would destroy, and keep blocking the update. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Thanks — you're right that the filter was too broad. Pushed Only Tests. The reproduction is retained, with the Added a table of tracked-status cases that each must still block, covering everything you listed plus a tracked path under
Agreed on scope — local plugin-update behavior only, no Cloud deployment or package pin involved. |
No issue filed — found while investigating a failing test on #266, and unrelated to that PR, so it is on its own branch.
The problem
webcmd plugin updaterefuses to run on a plugin that the user never touched.plugin installrunsnpm install --omit=dev --ignore-scriptsinside the cloned plugin (src/plugin.ts). If that plugin repo has no.gitignore, git then reportsnode_modules/andpackage-lock.jsonas untracked changes, and the "don't destroy uncommitted work" guard blocks the update:The guard is firing on webcmd's own output and blaming the user for work they never did. Such a plugin is permanently un-updatable short of
--force, which is documented as "discard uncommitted changes" — an alarming flag to need on a clean checkout.This is also what fails
tests/e2e/plugin-management.test.ts > plugin update succeeds on an installed pluginonmain.What I changed
src/plugin.ts—getDirtyFilesnow ignoresnode_modules/andpackage-lock.jsonat any depth. Depth matters because monorepo installs runnpm installat the repo root and in each sub-plugin.src/plugin.ts— pulled the porcelain path parsing into onedirtyEntryPathhelper, now shared withdescribeDirtyEntry.node_modules_notes.md) still blocks — there is a test for that.Deliberately not filtered: the
.jsfilestranspilePluginTsemits next to.tssources. Those are indistinguishable from hand-written files, so failing closed is the right default.Proof
npx vitest run→ 405 files, 4854 passed, 1 skipped, 0 failed — the E2E test now passes.npm run typecheckclean.🤖 Generated with Claude Code