From 107c9cc385871bdcd80ffefc9c2b2da60bdebd91 Mon Sep 17 00:00:00 2001 From: Noah Gilson Date: Wed, 21 Jan 2026 16:31:58 -0800 Subject: [PATCH 1/5] Add comparison for nvm-windows and dotnetup In this PR, I add onto the proposal from @baronfel to include a direct comparison, showing some of what I researched about `nvm` and how we thought about it when designing `dotnetup`. Some of this is pretty specific into technical detail and I want to avoid this document becoming huge and unreviewable. @baronfel's document also is a larger picture sell rather than a dive into the specifics, however I thought it would be helpful to have this information here. We can close these changes if we disagree and move it elsewhere. I tried to briefly touch on the topics without duplicating what will go into `dotnet/sdk/documentation/dotnetup` - the `hive` defaults and `path` handling alone will be their own documents. It can take several pages to explain. --- .../2026/dotnetup/cli-acquisition-tool.md | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/accepted/2026/dotnetup/cli-acquisition-tool.md b/accepted/2026/dotnetup/cli-acquisition-tool.md index 773f88e2e..37a1188ea 100644 --- a/accepted/2026/dotnetup/cli-acquisition-tool.md +++ b/accepted/2026/dotnetup/cli-acquisition-tool.md @@ -243,6 +243,65 @@ Tim Caswell created nvm (Node Version Manager) in 2011 to fundamentally solve th | Upgrade + migrate packages | `nvm install --lts --reinstall-packages-from=$(nvm current)` | | Per-project pinning | `.nvmrc` file with version, then `nvm use` | +### Comparison: `nvm` and `dotnetup` + +#### Windows + +##### `nvm` + +`nvm` chose to not support windows at all. `nvm` is a `POSIX` shell based tool. +[nvm windows statement](https://github.com/nvm-sh/nvm#:~:text=Note%3A%20nvm%20also%20supports%20Windows%20in%20some%20cases.%20It%20should%20work%20through%20WSL%20(Windows%20Subsystem%20for%20Linux)%20depending%20on%20the%20version%20of%20WSL.%20It%20should%20also%20work%20with%20GitBash%20(MSYS)%20or%20Cygwin.%20Otherwise%2C%20for%20Windows%2C%20a%20few%20alternatives%20exist%2C%20which%20are%20neither%20supported%20nor%20developed%20by%20us%3A) + +It can thus ignore Windows issues with elevation, the `PATH`, and conflicting installs. +We considered doing this with `dotnetup`, but Windows comprises a large cohort of current .NET users. + +`nvm` suggests `nvm-windows` as the first, yet unsupported/unofficial option. `nvm-windows` appears to be the most frequently used on Windows, so we investigate below. + +##### `nvm-windows` + +###### Admin Install Handling +`nvm-windows` setup dialog (UI) and user instructions on the `readme.md` suggest you uninstall all admin installations to prevent `PATH` issues. + +`nvm-windows` does not guarantee proper functionality with those installs. That is harder for `dotnetup` because `Visual Studio` is directly tied to its ecosystem. + +`nvm-windows` does, however, copy admin installations into the `nvm root`, in an attempt to not break existing tooling on the box. + +The Setup does this via a walkthrough dialog (UI), and asks for your permission to do so. The default is to do so. + +In our discussions for `dotnetup`, we decided something similar, where the walkthrough when you first launch `dotnetup` will try to install the admin-hive installs as local installs into the `dotnetup` hive. + +We cannot ignore `PATH` issues on Windows. That should be covered elsewhere - see https://github.com/dotnet/designs/tree/dnvm-e2e-experience/proposed for an older design on this. + +###### Symlink Technology Enables Shell Updates +`NVM_SYMLINK` is used because it enables to `nvm use` to apply to all console windows and can persist upon reboots. However, when running `where npm`, nothing is returned. + +This is harder for `dotnetup` because it requires `dotnet` to not be on the `PATH`, or for the `symlink` version to win over the `dotnet` on the `PATH`. +We updated the MSI installers to not modify the `PATH` upon update, but that faced issues and got rolled back. It is still worth exploring. + +Hive Locations: +The installation location of nvm is `%APPDATA%\nvm` which is a user-based hive containing multiple versions. +The default installation hive is based off of `NVM_SYMLINK`, which contains the location of the symlink file. That symlink is added to the `PATH` behind `NVM_HOME`. + +`%APPDATA%\Roaming\npm` contains globally installed tools is above on the PATH and is not part of `nvm`. + +`NVM_SYMLINK` defaults to `C:\nvm4w\node` -> `nvm use` points it to `...\AppData\Local\nvm\{version}` which contains the `nvm` folder. +`NVM_HOME` defaults to `...\AppData\Local\nvm`. Contains `nvm-windows` itself and its installs. + +Some users report confusion because the symlink cannot point to `C:\Program Files\nodejs`. + +`NVM_DIR` is `$HOME/.nvm`. + +##### Provides `nvm debug` to debug `PATH` issues. + +We've considered similar for `dotnetup`. + +##### Command Space + +`dotnetup use` makes less sense since the `dotnet` executable is a multiplexing system. +To make something similar, we would either need to write into the `global.json` file or create a separate folder with only the specific `sdk` or `runtime` designed. + +The experience for other commands such as `install` is quite similar, but instead tries to replicate the `NuGet` and `workload` existing terminology. + ### Deep Dive: rustup (Rust) **Historical Context:** From 06b34be89652ba75d89100e28ac58db2e0a8576f Mon Sep 17 00:00:00 2001 From: Noah Gilson Date: Wed, 21 Jan 2026 16:46:00 -0800 Subject: [PATCH 2/5] Unix Comparison More of this is generated text, but I've modified it and done some fact checking. --- .../2026/dotnetup/cli-acquisition-tool.md | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/accepted/2026/dotnetup/cli-acquisition-tool.md b/accepted/2026/dotnetup/cli-acquisition-tool.md index 37a1188ea..b63f22e9b 100644 --- a/accepted/2026/dotnetup/cli-acquisition-tool.md +++ b/accepted/2026/dotnetup/cli-acquisition-tool.md @@ -302,6 +302,49 @@ To make something similar, we would either need to write into the `global.json` The experience for other commands such as `install` is quite similar, but instead tries to replicate the `NuGet` and `workload` existing terminology. +#### Unix (macOS/Linux) + +##### Shell Integration + +`nvm` is implemented as a shell function sourced into the user's shell profile (`.bashrc`, `.zshrc`, etc.). This approach allows `nvm use` to modify the current shell's `PATH` directly without spawning a subshell. The tradeoff is that `nvm` must be re-sourced in each new terminal session. + +For `dotnetup`, we plan to take a similar approach: set `DOTNET_ROOT` and prepend the install location to `PATH` in the shell profile. +We can modify the profile with users consent during the initial walkthrough. + +##### Installation Location + +`nvm` installs all Node versions under `NVM_DIR` which is `$HOME/.nvm` with each version in its own subdirectory (e.g., `~/.nvm/versions/node/v20.10.0/`). + +`dotnetup` will follow a similar pattern with installs under a user-local directory. The .NET SDK's existing layout with `sdk/`, `shared/`, and `packs/` directories naturally supports multiple versions side-by-side. + +##### Version Resolution + +`nvm` resolves versions via: +1. Explicit `nvm use ` commands that modify `PATH` +2. `.nvmrc` files in the current directory (requires manual `nvm use` or shell hooks) +3. A default alias set via `nvm alias default` + +`dotnetup` benefits from .NET's existing `global.json` mechanism, which the `dotnet` muxer reads automatically. This is closer to rustup's behavior where version resolution happens transparently without explicit shell commands. + +##### Package Manager Coexistence + +On Linux, system package managers (`apt`, `dnf`, etc) may install Node.js to `/usr/bin/node`. `nvm` avoids conflicts by placing its Node versions earlier in `PATH` when activated. + +For .NET, distro packages typically install to `/usr/share/dotnet` with `/usr/bin/dotnet` as a symlink. `dotnetup` will need to handle this by: +1. Setting `DOTNET_ROOT` to the user-local hive +2. Ensuring the user-local `dotnet` appears before `/usr/bin` in `PATH` +3. Potentially warning users when a system install is detected + +If `dotnetup` is successful we want to consider working with our package manager partners. +A big win for this would be to enable acquisition of non-`100` band .NET SDKs on Linux. + +##### Self-Contained Design + +`nvm` is a collection of shell scripts that must be sourced. Updates suggest using `nvm upgrade`, (on `nvm-windows`), or officially the install script or pulling the git repository. + +`dotnetup` will be a Native AOT compiled binary, making it fully self-contained with no runtime dependencies. This simplifies distribution and enables proper self-update functionality similar to rustup. +We must ensure we build with a proper cross-gen container for older Linux versions. + ### Deep Dive: rustup (Rust) **Historical Context:** From 0325ec11a519d8410497a570f3f7772681b7cd3b Mon Sep 17 00:00:00 2001 From: Noah Gilson Date: Thu, 22 Jan 2026 10:28:23 -0800 Subject: [PATCH 3/5] fix terminology Co-authored-by: Jan Kotas --- accepted/2026/dotnetup/cli-acquisition-tool.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/accepted/2026/dotnetup/cli-acquisition-tool.md b/accepted/2026/dotnetup/cli-acquisition-tool.md index b63f22e9b..063688418 100644 --- a/accepted/2026/dotnetup/cli-acquisition-tool.md +++ b/accepted/2026/dotnetup/cli-acquisition-tool.md @@ -343,7 +343,8 @@ A big win for this would be to enable acquisition of non-`100` band .NET SDKs on `nvm` is a collection of shell scripts that must be sourced. Updates suggest using `nvm upgrade`, (on `nvm-windows`), or officially the install script or pulling the git repository. `dotnetup` will be a Native AOT compiled binary, making it fully self-contained with no runtime dependencies. This simplifies distribution and enables proper self-update functionality similar to rustup. -We must ensure we build with a proper cross-gen container for older Linux versions. +We must ensure we build with a proper cross-build container for older Linux versions. +https://github.com/dotnet/sdk/issues/51585 ### Deep Dive: rustup (Rust) From d47bb3f443cd92e8dde849f4ccf43aefd182af96 Mon Sep 17 00:00:00 2001 From: Noah Gilson Date: Fri, 23 Jan 2026 14:49:42 -0800 Subject: [PATCH 4/5] remove --- accepted/2026/dotnetup/cli-acquisition-tool.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/accepted/2026/dotnetup/cli-acquisition-tool.md b/accepted/2026/dotnetup/cli-acquisition-tool.md index 063688418..c1b40fcdd 100644 --- a/accepted/2026/dotnetup/cli-acquisition-tool.md +++ b/accepted/2026/dotnetup/cli-acquisition-tool.md @@ -276,7 +276,7 @@ We cannot ignore `PATH` issues on Windows. That should be covered elsewhere - se `NVM_SYMLINK` is used because it enables to `nvm use` to apply to all console windows and can persist upon reboots. However, when running `where npm`, nothing is returned. This is harder for `dotnetup` because it requires `dotnet` to not be on the `PATH`, or for the `symlink` version to win over the `dotnet` on the `PATH`. -We updated the MSI installers to not modify the `PATH` upon update, but that faced issues and got rolled back. It is still worth exploring. +We updated the MSI installers to not modify the `PATH` upon update, but that faced issues and got rolled back. Hive Locations: The installation location of nvm is `%APPDATA%\nvm` which is a user-based hive containing multiple versions. From 7ac055943d3fbe51b02abb38e701f59b3d9df23d Mon Sep 17 00:00:00 2001 From: Noah Gilson Date: Fri, 23 Jan 2026 14:50:31 -0800 Subject: [PATCH 5/5] improve url specificity --- accepted/2026/dotnetup/cli-acquisition-tool.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/accepted/2026/dotnetup/cli-acquisition-tool.md b/accepted/2026/dotnetup/cli-acquisition-tool.md index c1b40fcdd..dcb4255b1 100644 --- a/accepted/2026/dotnetup/cli-acquisition-tool.md +++ b/accepted/2026/dotnetup/cli-acquisition-tool.md @@ -270,7 +270,7 @@ The Setup does this via a walkthrough dialog (UI), and asks for your permission In our discussions for `dotnetup`, we decided something similar, where the walkthrough when you first launch `dotnetup` will try to install the admin-hive installs as local installs into the `dotnetup` hive. -We cannot ignore `PATH` issues on Windows. That should be covered elsewhere - see https://github.com/dotnet/designs/tree/dnvm-e2e-experience/proposed for an older design on this. +We cannot ignore `PATH` issues on Windows. That should be covered elsewhere - see https://github.com/dotnet/designs/blob/dnvm-e2e-experience/proposed/dotnet-bootstrapper/dotnet-bootstrapper-path.md for an older design on this. ###### Symlink Technology Enables Shell Updates `NVM_SYMLINK` is used because it enables to `nvm use` to apply to all console windows and can persist upon reboots. However, when running `where npm`, nothing is returned.