Skip to content

fix(cli): fail closed when Manager.Current() can't detect active Node#87

Merged
dipto0321 merged 1 commit into
mainfrom
fix/cli/cleanup-fail-closed
Jul 3, 2026
Merged

fix(cli): fail closed when Manager.Current() can't detect active Node#87
dipto0321 merged 1 commit into
mainfrom
fix/cli/cleanup-fail-closed

Conversation

@dipto0321

Copy link
Copy Markdown
Owner

Summary

If Manager.Current() failed during the post-upgrade cleanup step (e.g., nvm after nvm deactivate, or any transient node -p parse hiccup), the version currently powering the user's shell became an ordinary, deletable cleanup candidate. Combined with --cleanup / --yes / cfg.Cleanup.Auto, that path would auto-delete the active Node.js version.

This PR fails closed: when Current() errors, the cleanup step forces per-version y/N confirmation (downgrading every auto-confirm path), and surfaces a warning explaining why the prompt is firing.

Linked issues

Fixes #58

Type of change

  • fix — bug fix (PATCH bump)
  • feat — new feature (MINOR bump)
  • refactor — no behavior change
  • perf — performance improvement
  • docs — documentation only
  • test — tests only
  • chore — maintenance / dependency bump
  • ci — CI/CD only
  • build — build system only
  • Breaking change (MAJOR bump) — explain below

Checklist

Screenshots / output

Pre-fix behavior on a transient Current() failure with --cleanup:

$ nodeup upgrade --cleanup
Installing v22.11.0...
Installing v24.15.0...
Setting default...
Restoring packages for fnm...
Cleanup: Deleted: v18.20.4, v20.18.0, v22.10.0  ← v22.10.0 was the user's active Node
# user's shell now uses a binary that no longer exists on disk

Post-fix behavior on the same Current() failure:

$ nodeup upgrade --cleanup
Installing v22.11.0...
Installing v24.15.0...
Setting default...
Restoring packages for fnm...
Warning: could not determine the currently-active Node version (nvm: no current version set); cleanup will require per-version confirmation for safety.
Delete v18.20.4? [y/N] y
  Deleted v18.20.4
Delete v20.18.0? [y/N] n
Delete v22.10.0? [y/N] n
Cleanup: Deleted: v18.20.4, Skipped: v20.18.0, v22.10.0

--no-cleanup still wins (the strongest fail-closed stance):

$ nodeup upgrade --cleanup --no-cleanup
# no warning, no prompts, no deletes

Files touched

  • internal/cli/cleanup.go — new ForcePerVersion bool field on cleanupConfig. runCleanupPrompt applies it at step 1b (right after the NonInteractive short-circuit, before any prompt fires), setting AutoDeleteAll = false and PerVersion = true. Doc comment carries the rationale and references bug(cli): Current() failure leaves the active Node version unprotected from cleanup deletion #58.
  • internal/cli/upgrade.go — tracks currentErr from m.Current(). On error, sets cleanupCfg.ForcePerVersion = true and prints a stderr-style warning (cmd.Printf) explaining the downgrade.
  • internal/cli/cleanup_test.go — three new test functions pinning the fail-closed contract:
    • TestCleanupPrompt_ForcePerVersionDowngradesAutoDeleteAllAutoDeleteAll=true + ForcePerVersion=true requires per-version y/N (input: all-or-nothing "y" + per-version "y" + per-version "n" → 1 deleted, 1 skipped). A regression that ran with AutoDeleteAll=true would auto-delete both, never reading the per-version answers.
    • TestCleanupPrompt_ForcePerVersionIgnoresPerVersionFalsePerVersion=false + ForcePerVersion=true still requires per-version y/N, even though the user explicitly set Prompt: false in their config.
    • TestCleanupPrompt_ForcePerVersionWithNonInteractiveNonInteractive=true + ForcePerVersion=true still skips entirely (the NonInteractive short-circuit fires first). A regression that flipped the order of the two checks would print prompts even when --no-cleanup was passed.
  • docs/managers.md — the candidates section now describes the fail-closed behavior (per-version y/N when Current() fails), and the --yes row notes it's downgraded to per-version y/N in that case.
  • CHANGELOG.md### Fixed entry under [Unreleased] documenting the change with the bug(cli): Current() failure leaves the active Node version unprotected from cleanup deletion #58 reference.

The post-upgrade cleanup step silently swallowed `Current()`
errors and proceeded with the zero-value `semver.Version` for
"active" — which `cleanupCandidates` treats as "no active
version to exclude" (by design, to avoid the zero value matching
a real version).

The practical effect: a `Current()` failure (e.g. nvm after
`nvm deactivate`, or any transient `node -p` parse hiccup)
turned the version powering the user's shell into an ordinary
cleanup candidate. Combined with --cleanup / --yes /
cfg.Cleanup.Auto, that path would auto-delete the Node.js
version the user is actively running.

Fail closed instead:

- New `cleanupConfig.ForcePerVersion` flag. When set,
  `runCleanupPrompt` overrides AutoDeleteAll=false and
  PerVersion=true at step 1b (after NonInteractive wins, before
  any prompt fires), so per-version y/N is forced regardless of
  how the user configured cleanup auto-confirm.
- `runUpgrade` tracks `currentErr` from `m.Current()` and
  sets `cleanupCfg.ForcePerVersion = true` when it fails.
  A warning ("could not determine the currently-active Node
  version (...); cleanup will require per-version confirmation
  for safety") is printed so the user knows why a --cleanup /
  --yes run suddenly turned per-version.
- `cleanupCandidates` (the active-version exclusion logic) is
  intentionally unchanged — the safety net is at the prompt
  layer, not the candidate-set layer, because failing the whole
  upgrade would be too aggressive and the per-version y/N is
  the correct UX when we can't see what's active.
- New tests pin the downgrade behavior end-to-end:
  AutoDeleteAll=true + ForcePerVersion=true requires per-version
  y/N, PerVersion=false + ForcePerVersion=true still requires
  per-version y/N, and NonInteractive=true + ForcePerVersion=true
  still skips entirely (NonInteractive wins over ForcePerVersion).

docs/managers.md updated: the candidates section now describes
the fail-closed behavior, and the --yes row notes it's downgraded
to per-version y/N when Current() fails.

Refs #58
@cocogitto-bot

cocogitto-bot Bot commented Jul 3, 2026

Copy link
Copy Markdown

✔️ 561dac0 - Conventional commits check succeeded.

@dipto0321 dipto0321 merged commit 45f3cd5 into main Jul 3, 2026
10 checks passed
@dipto0321 dipto0321 deleted the fix/cli/cleanup-fail-closed branch July 3, 2026 03:29
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.

bug(cli): Current() failure leaves the active Node version unprotected from cleanup deletion

1 participant