Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 35 additions & 2 deletions src/Installer/dotnetup/Commands/Sdk/Install/SdkInstallCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ internal class SdkInstallCommand(ParseResult result) : CommandBase(result)

private readonly IDotnetInstallManager _dotnetInstaller = new DotnetInstallManager();
private readonly ChannelVersionResolver _channelVersionResolver = new ChannelVersionResolver();
private InstallRootManager? _installRootManager;
private InstallRootManager InstallRootManager => _installRootManager ??= new InstallRootManager(_dotnetInstaller);

public override int Execute()
{
Expand Down Expand Up @@ -126,6 +128,7 @@ public override int Execute()
}

bool? resolvedSetDefaultInstall = _setDefaultInstall;
UserInstallRootChanges? userInstallRootChanges = null;
Copy link
Member

Choose a reason for hiding this comment

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

Don't declare this here. Only declare it later when it is first used.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done in commit ef7728b. Moved the declaration to line 158 where it's first used.


if (resolvedSetDefaultInstall == null)
{
Expand All @@ -142,7 +145,15 @@ public override int Execute()
{
if (DotnetupUtilities.PathsEqual(resolvedInstallPath, currentDotnetInstallRoot.Path))
{
// No need to prompt here, the default install is already set up.
// If the current install is fully configured and matches the resolved path, skip the prompt
if (currentDotnetInstallRoot.IsFullyConfigured)
{
// Default install is already set up correctly, no need to prompt
resolvedSetDefaultInstall = false;
}
// If not fully configured, we leave resolvedSetDefaultInstall as null (no prompt)
// since the install path is already on PATH. The user can run "dotnetup defaultinstall"
// if they want to fully configure it.
Copy link
Member

Choose a reason for hiding this comment

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

Update this so that if the user install is not fully configured, it should display the things which need to be configured and prompt whether they should be fixed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done in commit dba18fc. When user install is not fully configured, the command now displays what needs to be configured (remove admin paths, add to user PATH, set DOTNET_ROOT) and prompts whether to apply these changes.

}
else
{
Expand Down Expand Up @@ -249,7 +260,29 @@ public override int Execute()

if (resolvedSetDefaultInstall == true)
{
_dotnetInstaller.ConfigureInstallType(InstallType.User, resolvedInstallPath);
if (OperatingSystem.IsWindows())
Copy link
Member

Choose a reason for hiding this comment

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

Refactor this so that it calls _dotnetInstaller.ConfigureInstallType on all operating systems, but the implementation on Windows uses InstallRootManager.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done in commit dba18fc. Refactored ConfigureInstallType to work on all platforms. On Windows, it now uses InstallRootManager internally. SdkInstallCommand now calls _dotnetInstaller.ConfigureInstallType(InstallType.User, resolvedInstallPath) for all platforms.

{
// Use InstallRootManager to apply the user install root configuration
if (userInstallRootChanges == null)
{
userInstallRootChanges = InstallRootManager.GetUserInstallRootChanges();
}

bool succeeded = InstallRootManager.ApplyUserInstallRoot(
userInstallRootChanges,
msg => SpectreAnsiConsole.WriteLine(msg),
msg => SpectreAnsiConsole.MarkupLine($"[red]{msg}[/]"));

if (!succeeded)
{
SpectreAnsiConsole.MarkupLine("[yellow]Warning: Failed to configure default install root. You can configure it later with the \"dotnetup defaultinstall\" command.[/]");
}
}
else
{
// For non-Windows platforms, use the existing ConfigureInstallType method
_dotnetInstaller.ConfigureInstallType(InstallType.User, resolvedInstallPath);
}
}

if (resolvedUpdateGlobalJson == true)
Expand Down
57 changes: 46 additions & 11 deletions src/Installer/dotnetup/DotnetInstallManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,27 +22,62 @@ public DotnetInstallManager(IEnvironmentProvider? environmentProvider = null)

public DotnetInstallRootConfiguration? GetConfiguredInstallType()
{

string? foundDotnet = _environmentProvider.GetCommandPath("dotnet");
if (string.IsNullOrEmpty(foundDotnet))
{
return null;
}

string installDir = Path.GetDirectoryName(foundDotnet)!;


string? dotnetRoot = Environment.GetEnvironmentVariable("DOTNET_ROOT");
string programFiles = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
string programFilesX86 = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86);
bool isAdminInstall = installDir.StartsWith(Path.Combine(programFiles, "dotnet"), StringComparison.OrdinalIgnoreCase) ||
installDir.StartsWith(Path.Combine(programFilesX86, "dotnet"), StringComparison.OrdinalIgnoreCase); // TODO: This should be improved to not be windows-specific https://github.com/dotnet/sdk/issues/51601

var installRoot = new DotnetInstallRoot(installDir, InstallerUtilities.GetDefaultInstallArchitecture());

bool isSetAsDotnetRoot = DotnetupUtilities.PathsEqual(dotnetRoot, installDir);
// Use InstallRootManager to determine if the install is fully configured
if (OperatingSystem.IsWindows())
{
var installRootManager = new InstallRootManager(this);

// Check if user install root is fully configured
var userChanges = installRootManager.GetUserInstallRootChanges();
if (!userChanges.NeedsChange() && DotnetupUtilities.PathsEqual(installDir, userChanges.UserDotnetPath))
{
return new(installRoot, InstallType.User, IsFullyConfigured: true);
}

return new(installRoot, isAdminInstall ? InstallType.Admin : InstallType.User, IsOnPath: true, isSetAsDotnetRoot);
// Check if admin install root is fully configured
var adminChanges = installRootManager.GetAdminInstallRootChanges();
if (!adminChanges.NeedsChange())
{
string programFiles = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
string programFilesX86 = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86);
bool isAdminInstall = installDir.StartsWith(Path.Combine(programFiles, "dotnet"), StringComparison.OrdinalIgnoreCase) ||
installDir.StartsWith(Path.Combine(programFilesX86, "dotnet"), StringComparison.OrdinalIgnoreCase);
Copy link
Member

Choose a reason for hiding this comment

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

We can skip this logic here. If we don't need any changes to switch to admin mode, then we can return that the admin mode is fully configured.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done in commit dba18fc. Removed the unnecessary logic - if GetAdminInstallRootChanges() reports no changes needed, we return Admin type as fully configured regardless of path location.

if (isAdminInstall)
{
return new(installRoot, InstallType.Admin, IsFullyConfigured: true);
}
}

// Not fully configured, but PATH resolves to dotnet
// Determine type based on location
string programFilesPath = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
string programFilesX86Path = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86);
bool isAdminPath = installDir.StartsWith(Path.Combine(programFilesPath, "dotnet"), StringComparison.OrdinalIgnoreCase) ||
installDir.StartsWith(Path.Combine(programFilesX86Path, "dotnet"), StringComparison.OrdinalIgnoreCase);

return new(installRoot, isAdminPath ? InstallType.Admin : InstallType.User, IsFullyConfigured: false);
}
else
{
// For non-Windows platforms, determine based on path location
// TODO: This should be improved to not be windows-specific https://github.com/dotnet/sdk/issues/51601
string programFiles = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
string programFilesX86 = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86);
bool isAdminInstall = installDir.StartsWith(Path.Combine(programFiles, "dotnet"), StringComparison.OrdinalIgnoreCase) ||
installDir.StartsWith(Path.Combine(programFilesX86, "dotnet"), StringComparison.OrdinalIgnoreCase);

// For now, we consider it fully configured if it's on PATH
return new(installRoot, isAdminInstall ? InstallType.Admin : InstallType.User, IsFullyConfigured: true);
}
}

public string GetDefaultDotnetInstallPath()
Expand Down
4 changes: 1 addition & 3 deletions src/Installer/dotnetup/IDotnetInstallManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,7 @@ public string? SdkPath
public record DotnetInstallRootConfiguration(
DotnetInstallRoot InstallRoot,
InstallType InstallType,
bool IsOnPath,
// We may also need additional information to handle the case of whether DOTNET_ROOT is not set or whether it's set to a different path
bool IsSetAsDotnetRoot)
bool IsFullyConfigured)
{
public string Path => InstallRoot.Path;
}