Skip to content

feat(detector): implement nvm-windows detection#8

Merged
dipto0321 merged 1 commit into
mainfrom
feat/detector/add-nvm-windows
Jun 29, 2026
Merged

feat(detector): implement nvm-windows detection#8
dipto0321 merged 1 commit into
mainfrom
feat/detector/add-nvm-windows

Conversation

@dipto0321

Copy link
Copy Markdown
Owner

Changes

  • internal/detector/nvm_windows.go (full implementation, //go:build windows):

    • Detect(): platform.LookupManagerBinary("nvm") != "" OR $NVM_HOME env OR $NVM_SYMLINK env (asdf pattern, two-env-var detection)
    • Version(): nvm version, parses the bare semver (e.g., "1.1.12") — upstream's dispatcher calls fmt.Println(NvmVersion) so output has no branding prefix and no v prefix
    • ListInstalled(): nvm list, lines * X.Y.Z (Currently using <arch>-bit executable) (current) / X.Y.Z (others), the trailing (Currently using <arch>-bit executable) suffix is stripped before semver parse; No installations recognized. sentinel filtered out
    • nvmWindowsRoot() helper: $NVM_HOME only — no home-dir fallback because the default location varies between Windows releases and nodeup should not pretend to know
    • nvmWindowsSymlink() helper: $NVM_SYMLINK only (the active-version symlink dir)
    • Mutation methods return ErrNVMWindowsNotImplemented sentinel (asdf pattern)
  • internal/detector/nvm_windows_test.go (~30 mocked tests, //go:build windows):

    • parseNVMWindowsVersion: standard output (1.1.12), v-prefixed (v1.1.12), git-describe suffix (1.1.12-4-gabc1234), trailing whitespace (incl. \r\n), empty, whitespace-only, multi-token defensive
    • parseNVMWindowsInstalled: real output (with (Currently using 64-bit executable) suffix), 32-bit marker variant, filters No installations recognized. sentinel, sentinel with leading blank lines, unsorted → sorted ascending, empty stdout, only blank lines, defensive v prefix, skips unparseable lines, \r\n line endings
    • Method tests: Name, Version (success capturing command, v-prefixed, runShell error, empty-output error), ListInstalled (success, empty stdout, runShell error)
    • Detect: no-PATH-no-env, binary-on-PATH (nvm.exe directly since file is Windows-only), honors $NVM_HOME, honors $NVM_SYMLINK, empty env falls through, whitespace-only env falls through
    • MutationMethods_NotImplemented sentinel test
    • nvmWindowsRoot helper: env-precedence, empty-when-unset, whitespace-only-env-falls-through
    • nvmWindowsSymlink helper: env, empty-when-unset, whitespace-only
  • internal/detector/registry_windows.go (refactor):

    • Removed the Phase 1 stub NVMWindows type/methods (they live in nvm_windows.go now)
    • All() continues to return NewNVMWindows() as the last entry — behavior unchanged

Design notes

  • This is the eighth and final manager in the Phase 1 rollout, completing the internal/detector surface (fnm, nvm, Volta, ASDF, mise, n, Nodenv, NVMWindows).
  • nvm-windows is the only Windows-only manager. The build-tag pattern (//go:build windows on both the implementation AND the test file) means the file is excluded from linux/macOS builds. The CI matrix exercises windows-latest so the tests run there.
  • The file is build-tagged, so the test code does NOT need a runtime.GOOS switch when constructing the stub nvm.exe binary — that's why nodenv's TestNodenv_Detect_FindsBinaryOnPath runtime.GOOS switch is absent here.
  • The detection pattern uses three independent signals: nvm.exe on PATH, $NVM_HOME, and $NVM_SYMLINK. The upstream installer sets $NVM_HOME and $NVM_SYMLINK together at install time, so they typically appear together, but either alone is sufficient — the symlink is the active-version dir, the home is the install root.
  • We deliberately do NOT fall back from $NVM_HOME to a hard-coded %APPDATA%\nvm path because that varies between Windows releases (the upstream installer lets the user pick any directory).
  • nvm list output strips the v prefix via regex upstream (regexp.MustCompile("v").ReplaceAllString) before printing — so the parser sees bare X.Y.Z. We re-strip a leading v defensively in case a fork skips that step.
  • The architecture suffix (32-bit / 64-bit) is hard-coded in upstream's format string (arm64 installs still print 64-bit). We accept both via substring-on-whitespace — the parser doesn't care which bitness the marker uses.
  • nvm version aliases (v, -v, --version, -version, --v) all dispatch to the same case in upstream, so we use version as the canonical subcommand.

Verification

  • GOOS=windows go build ./...
  • GOOS=windows go vet ./...
  • GOOS=windows go test -c -o /tmp/detector.test ./internal/detector ✅ (cross-compiles cleanly)
  • go test ./... -race (native darwin) ✅ (all 7 prior-manager test suites still pass; nvm-windows tests are excluded by build tag and will run on windows-latest in CI)

@cocogitto-bot

cocogitto-bot Bot commented Jun 29, 2026

Copy link
Copy Markdown

✔️ 2d1161e - Conventional commits check succeeded.

@dipto0321 dipto0321 merged commit 84e12d1 into main Jun 29, 2026
10 checks passed
@dipto0321 dipto0321 deleted the feat/detector/add-nvm-windows branch June 29, 2026 13:46
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