Skip to content

Dotnetup: Shell profile modification for PATH and DOTNET_ROOT#53447

Merged
dsplaisted merged 54 commits intodotnet:release/dnupfrom
dsplaisted:dotnetup-edit-profile
Apr 23, 2026
Merged

Dotnetup: Shell profile modification for PATH and DOTNET_ROOT#53447
dsplaisted merged 54 commits intodotnet:release/dnupfrom
dsplaisted:dotnetup-edit-profile

Conversation

@dsplaisted
Copy link
Copy Markdown
Member

@dsplaisted dsplaisted commented Mar 13, 2026

Unix shell profile integration for dotnetup

When dotnetup installs .NET on Unix, it needs to configure the user's shell so that dotnet is available on the command line by default. This PR adds and improves the shell profile modification support that makes that work — adding entries to .bashrc, .zshrc, etc. that set DOTNET_ROOT and update PATH on shell startup.

Shell profile management

  • Include dotnetup directory in PATH in generated env scripts alongside the dotnet install path
  • Implement defaultinstall admin on Unix with dotnetup-only profile entries
  • Clear shell command hash (hash -d dotnet/dotnetup for bash, rehash for zsh) in generated scripts so stale cached paths don't shadow the new installation
  • Replace existing profile entries in-place, preserving the user's ordering in their profile file
  • Skip default install prompt when the current shell is unsupported, instead of silently doing nothing after the user answers

Code cleanup

  • Remove no-op SetEnvironmentVariable calls on Unix (EnvironmentVariableTarget.User has no persistent store on Unix in .NET)
  • Consolidate shell detection into ShellDetection as single source of truth for supported shells
  • Move shell types to Microsoft.DotNet.Tools.Bootstrapper.Shell namespace — these types are used across multiple commands and don't belong in PrintEnvScript
  • Remove misleading comments from generated scripts

Documentation

  • Rewrote unix-environment-setup.md to lead with user-facing workflows (install, defaultinstall) instead of the print-env-script implementation detail
  • Updated future work section to reflect that defaultinstall admin is now implemented

@dsplaisted dsplaisted marked this pull request as ready for review March 15, 2026 20:36
@dsplaisted dsplaisted requested a review from a team as a code owner March 15, 2026 20:36
Copilot AI review requested due to automatic review settings March 15, 2026 20:36
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds Unix shell profile modification support to dotnetup so PATH/DOTNET_ROOT configuration can be persisted via .bashrc, .zshrc, etc., and consolidates shell detection/providers into a shared Microsoft.DotNet.Tools.Bootstrapper.Shell namespace.

Changes:

  • Introduces ShellProfileManager + ShellDetection and new bash/zsh/pwsh IEnvShellProvider implementations under ...Bootstrapper.Shell.
  • Updates install/defaultinstall flows to write shell-profile entries on Unix (and prints an activation command for the current terminal).
  • Adds/updates tests and rewrites Unix environment setup documentation accordingly.

Reviewed changes

Copilot reviewed 19 out of 19 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
test/dotnetup.Tests/ShellProfileManagerTests.cs New tests for add/remove/backup/idempotency and provider entry formatting.
test/dotnetup.Tests/EnvShellProviderTests.cs Updates tests to new provider namespace and validates dotnetup-dir PATH behavior.
src/Installer/dotnetup/Shell/IEnvShellProvider.cs New shared shell provider interface (env script + profile integration).
src/Installer/dotnetup/Shell/BashEnvShellProvider.cs Bash env script/profile/activation implementation; profile paths selection logic.
src/Installer/dotnetup/Shell/ZshEnvShellProvider.cs Zsh env script/profile/activation implementation.
src/Installer/dotnetup/Shell/PowerShellEnvShellProvider.cs PowerShell env script/profile/activation implementation.
src/Installer/dotnetup/Shell/ShellDetection.cs Centralized supported shell list + current-shell detection.
src/Installer/dotnetup/Shell/ShellProfileManager.cs New profile file mutation logic (append/replace/remove + backups).
src/Installer/dotnetup/DotnetInstallManager.cs Switches Unix “default install” persistence from env vars to shell profiles.
src/Installer/dotnetup/Commands/Shared/InstallWalkthrough.cs Skips the default-install prompt when shell is unsupported on Unix.
src/Installer/dotnetup/Commands/Shared/InstallExecutor.cs Prints a post-install activation command on Unix.
src/Installer/dotnetup/Commands/PrintEnvScript/PrintEnvScriptCommandParser.cs Uses ShellDetection, adds --dotnetup-only, updates validation/completions.
src/Installer/dotnetup/Commands/PrintEnvScript/PrintEnvScriptCommand.cs Adds dotnetup-dir PATH injection + dotnetup-only behavior when generating scripts.
src/Installer/dotnetup/Commands/DefaultInstall/DefaultInstallCommand.cs Implements Unix defaultinstall user/admin using profile entries.
src/Installer/dotnetup/Commands/PrintEnvScript/BashEnvShellProvider.cs Removes old provider type (moved to ...Shell).
src/Installer/dotnetup/Commands/PrintEnvScript/ZshEnvShellProvider.cs Removes old provider type (moved to ...Shell).
src/Installer/dotnetup/Commands/PrintEnvScript/PowerShellEnvShellProvider.cs Removes old provider type (moved to ...Shell).
src/Installer/dotnetup/Commands/PrintEnvScript/IEnvShellProvider.cs Removes old interface (replaced by shared interface).
documentation/general/dotnetup/unix-environment-setup.md Rewrites doc around user workflows and new profile management behavior.

You can also share your feedback on Copilot code review. Take the survey.

Comment thread src/Installer/dotnetup/Shell/ShellProfileManager.cs Outdated
Comment thread documentation/general/dotnetup/unix-environment-setup.md Outdated
Comment thread src/Installer/dotnetup/DotnetInstallManager.cs Outdated
Comment thread src/Installer/dotnetup/DotnetInstallManager.cs Outdated
Comment thread src/Installer/dotnetup/Commands/Shared/InstallExecutor.cs Outdated
dsplaisted and others added 19 commits April 13, 2026 19:10
Extend IEnvShellProvider with GetProfilePaths(), GenerateProfileEntry(),
and GenerateActivationCommand() methods. Implement in all three providers:
- Bash: ~/.bashrc + login profile (~/.bash_profile or ~/.profile)
- Zsh: ~/.zshrc
- PowerShell: ~/.config/powershell/Microsoft.PowerShell_profile.ps1

Add ShellProfileManager for coordinating file I/O (add/remove entries,
backup, idempotency) and ShellDetection for resolving the current shell.

Hook profile modification into DotnetInstallManager.ConfigureInstallType()
on non-Windows so that 'sdk install --interactive' persists to profiles.

Implement DefaultInstallCommand.SetUserInstallRoot() for non-Windows,
replacing the 'not yet supported' error.

Print activation command after install so users can immediately use .NET
in their current terminal.

Closes dotnet#51582

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

# Conflicts:
#	src/Installer/dotnetup/Commands/Shared/InstallExecutor.cs
#	src/Installer/dotnetup/DotnetInstallManager.cs
GenerateEnvScript now accepts an optional dotnetupDir parameter.
When provided, the dotnetup binary's directory is prepended to PATH
alongside the dotnet install path, so both dotnet and dotnetup are
available after sourcing the script.

The print-env-script command passes Path.GetDirectoryName(Environment.ProcessPath)
automatically.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When switching to admin install on Unix, shell profile entries are
replaced with dotnetup-only versions that add dotnetup to PATH but
do not set DOTNET_ROOT or add the dotnet install path (since the
admin/system install manages dotnet).

Add --dotnetup-only flag to print-env-script command. When set, the
generated script only adds the dotnetup directory to PATH.

Add ShellProfileManager.ReplaceProfileEntries() for switching between
user and admin profile entries.

Add includeDotnet parameter to GenerateEnvScript and dotnetupOnly
parameter to GenerateProfileEntry/GenerateActivationCommand on
IEnvShellProvider.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

# Conflicts:
#	src/Installer/dotnetup/Commands/DefaultInstall/DefaultInstallCommand.cs
#	src/Installer/dotnetup/Commands/PrintEnvScript/PrintEnvScriptCommand.cs
#	src/Installer/dotnetup/DotnetInstallManager.cs
Restructure the document so that install and defaultinstall are presented
as the primary ways environment setup happens, with print-env-script
described as the underlying building block and standalone utility.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
EnvironmentVariableTarget.User has no persistent store on Unix in .NET,
so the SetEnvironmentVariable calls for DOTNET_ROOT and PATH were
effectively process-scoped and had no lasting effect. Shell profile
entries are the sole persistence mechanism on Unix.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

# Conflicts:
#	src/Installer/dotnetup/DotnetInstallManager.cs
The shell providers generate eval-based commands, so the documentation
examples should match. Also use dot-source (.) instead of source for
POSIX compatibility.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ripts

The output of print-env-script is consumed via eval, not sourced as a
file. The 'source this script' instructions were misleading. Removed
from all three shell providers and the documentation examples.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The generated scripts add both the dotnetup binary directory and the
dotnet install path to PATH. The documentation examples were missing
the dotnetup directory.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The command works on Unix by replacing profile entries with
dotnetup-only entries. The remaining gap is system-wide /etc/profile.d/
configuration, so reword the item to reflect that.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
On non-Windows, configuring the default install requires modifying
shell profile files. If the current shell cannot be detected or is not
supported, the profile modification would silently do nothing. Instead,
detect this up-front and skip the prompt with a warning message, so the
user knows why the default install setup was skipped.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

# Conflicts:
#	src/Installer/dotnetup/Commands/Shared/InstallWalkthrough.cs
Move the supported shells array and shell map from
PrintEnvScriptCommandParser into ShellDetection, eliminating the
duplicate dictionary and the duplicate LookupShellFromEnvironment
method. All callers now go through ShellDetection for shell lookup.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

# Conflicts:
#	src/Installer/dotnetup/Commands/Shared/InstallWalkthrough.cs
AddProfileEntries now replaces existing entries in-place when found,
preserving the user's ordering in their profile file. This eliminates
the need for a separate ReplaceProfileEntries method and fixes the
case where AddProfileEntries would silently skip stale entries.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

# Conflicts:
#	src/Installer/dotnetup/DotnetInstallManager.cs
Remove duplicate '# dotnetup' constants from BashEnvShellProvider,
ZshEnvShellProvider, and PowerShellEnvShellProvider. All three now
reference ShellProfileManager.MarkerComment, ensuring the marker
used to generate entries always matches the one used to find them.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add 'hash -d dotnet' (bash) and 'rehash' (zsh) to the generated
scripts so a stale cached dotnet path is cleared when the environment
is configured. This replaces the misleading comments that claimed
dotnetup would handle it automatically.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Move the validator and completion source setup from the static
constructor into the ShellOption object initializer using collection
initializer syntax. Remove the now-unnecessary helper methods.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Move IEnvShellProvider, BashEnvShellProvider, ZshEnvShellProvider,
PowerShellEnvShellProvider, ShellDetection, and ShellProfileManager
into a new Shell/ directory and namespace. These types are used across
multiple commands and don't belong in the PrintEnvScript namespace.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

# Conflicts:
#	src/Installer/dotnetup/Commands/Shared/InstallExecutor.cs
#	src/Installer/dotnetup/Commands/Shared/InstallWalkthrough.cs
#	src/Installer/dotnetup/DotnetInstallManager.cs
The test was checking that the first PATH entry containing 'dotnet' was
the install path. Now that the dotnetup directory is also added to PATH,
the dotnetup binary path (which contains 'dotnet' as a substring) can
appear first. Changed to simply verify the install path is contained in
PATH entries.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@dsplaisted dsplaisted force-pushed the dotnetup-edit-profile branch from b3afd4e to b2176bf Compare April 14, 2026 11:46
dsplaisted and others added 3 commits April 14, 2026 23:29
Thread the selected install root through generated shell profile entries and activation commands, and align the Unix environment docs with the current API behavior.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@dsplaisted dsplaisted force-pushed the dotnetup-edit-profile branch from a1eb785 to 2e20591 Compare April 15, 2026 03:30
dsplaisted and others added 11 commits April 19, 2026 11:24
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Suppress the explicit install-path argument when the effective path is the environment default, and keep the helper naming aligned with its return value.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy link
Copy Markdown
Member

@nagilson nagilson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for responding to all of my feedback 🥳

I will look at the tests in a bit and do a final pass, but these are the remaining nits/thoughts I had. I don't consider any of the comments here blocking except I'm double checking on the security focused issue(s)

{
if (File.Exists(tempPath))
{
File.Delete(tempPath);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: the exists check here and in the restoreoriginal file are redundant - delete should not fail to my knowledge here. The one around move however is necessary.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has been rewritten now, I'm not 100% sure whether this specific concern has been addressed.

Comment thread src/Installer/dotnetup/Shell/ShellProfileManager.cs
Comment thread src/Installer/dotnetup/Shell/ShellProfileManager.cs Outdated
Comment thread src/Installer/dotnetup/Shell/ShellProfileManager.cs Outdated
Comment thread src/Installer/dotnetup/Shell/PowerShellEnvShellProvider.cs Outdated
return
$"""
#!/usr/bin/env bash
# This script configures the environment for .NET installed at {dotnetInstallPath}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Asking for a 2nd opinion on this to make sure there's nothing we didn't think of

Comment thread documentation/general/dotnetup/unix-environment-setup.md Outdated
dsplaisted and others added 2 commits April 21, 2026 16:26
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy link
Copy Markdown
Member

@nagilson nagilson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Finished looking at the tests, the remaining feedback is about polish on the PR but a few I think are more important, such as the interop change. Let me know what you think!

Comment thread documentation/general/dotnetup/unix-environment-setup.md Outdated
Comment thread documentation/general/dotnetup/unix-environment-setup.md Outdated
Comment thread documentation/general/dotnetup/unix-environment-setup.md
Comment thread src/Installer/dotnetup/Commands/DefaultInstall/DefaultInstallCommand.cs Outdated
[Fact]
public void ResolveCurrentInstallRootPath_UsesRealDirectoryWhenParentDirectoryIsSymlinked()
{
if (OperatingSystem.IsWindows())
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we use the native method interop replacement API I think we can remove this condition.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should probably stick to realpath and I don't think we are concerned with this behavior on Windows.

Comment thread test/dotnetup.Tests/ParserTests.cs
Comment thread test/dotnetup.Tests/ShellProfileManagerTests.cs
Comment thread test/dotnetup.Tests/ShellProfileManagerTests.cs
Comment thread test/dotnetup.Tests/ShellProfileManagerTests.cs
Copy link
Copy Markdown
Member

@nagilson nagilson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for adding those tests, the follow ups, and remaining improvements!

@dsplaisted dsplaisted enabled auto-merge April 22, 2026 19:32
@dsplaisted dsplaisted disabled auto-merge April 23, 2026 00:24
@dsplaisted
Copy link
Copy Markdown
Member Author

/ba-g .NET SDK tests failing in dotnetup branch

@dsplaisted dsplaisted merged commit 32c1f94 into dotnet:release/dnup Apr 23, 2026
25 of 29 checks passed
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.

4 participants