Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
49 changes: 27 additions & 22 deletions src/Installer/dnup/ArchiveDotnetInstaller.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,38 +8,39 @@
using System.IO.Compression;
using System.Linq;
using System.Runtime.InteropServices;
using Microsoft.Deployment.DotNet.Releases;

namespace Microsoft.DotNet.Tools.Bootstrapper;

internal class ArchiveDotnetInstaller : IDotnetInstaller, IDisposable
{
private readonly DotnetInstallRequest _request;
private readonly DotnetInstall _install;
private readonly ReleaseVersion _resolvedVersion;
private string scratchDownloadDirectory;
private string? _archivePath;

public ArchiveDotnetInstaller(DotnetInstallRequest request, DotnetInstall version)
public ArchiveDotnetInstaller(DotnetInstallRequest request, ReleaseVersion resolvedVersion)
{
_request = request;
_install = version;
_resolvedVersion = resolvedVersion;
scratchDownloadDirectory = Directory.CreateTempSubdirectory().FullName;
}

public void Prepare()
{
using var releaseManifest = new ReleaseManifest();
var archiveName = $"dotnet-{_install.Id}";
var archiveName = $"dotnet-{Guid.NewGuid()}";
_archivePath = Path.Combine(scratchDownloadDirectory, archiveName + DnupUtilities.GetArchiveFileExtensionForPlatform());

Spectre.Console.AnsiConsole.Progress()
.Start(ctx =>
{
var downloadTask = ctx.AddTask($"Downloading .NET SDK {_install.FullySpecifiedVersion.Value}", autoStart: true);
var reporter = new SpectreDownloadProgressReporter(downloadTask, $"Downloading .NET SDK {_install.FullySpecifiedVersion.Value}");
var downloadSuccess = releaseManifest.DownloadArchiveWithVerification(_install, _archivePath, reporter);
var downloadTask = ctx.AddTask($"Downloading .NET SDK {_resolvedVersion}", autoStart: true);
var reporter = new SpectreDownloadProgressReporter(downloadTask, $"Downloading .NET SDK {_resolvedVersion}");
var downloadSuccess = releaseManifest.DownloadArchiveWithVerification(_request, _resolvedVersion, _archivePath, reporter);
if (!downloadSuccess)
{
throw new InvalidOperationException($"Failed to download .NET archive for version {_install.FullySpecifiedVersion.Value}");
throw new InvalidOperationException($"Failed to download .NET archive for version {_resolvedVersion}");
}

downloadTask.Value = 100;
Expand Down Expand Up @@ -83,10 +84,10 @@ internal static string ConstructArchiveName(string? versionString, string rid, s

public void Commit()
{
Commit(GetExistingSdkVersions(_request.TargetDirectory));
Commit(GetExistingSdkVersions(_request.InstallRoot));
}

public void Commit(IEnumerable<DotnetVersion> existingSdkVersions)
public void Commit(IEnumerable<ReleaseVersion> existingSdkVersions)
{
if (_archivePath == null || !File.Exists(_archivePath))
{
Expand All @@ -96,10 +97,10 @@ public void Commit(IEnumerable<DotnetVersion> existingSdkVersions)
Spectre.Console.AnsiConsole.Progress()
.Start(ctx =>
{
var installTask = ctx.AddTask($"Installing .NET SDK {_install.FullySpecifiedVersion.Value}", autoStart: true);
var installTask = ctx.AddTask($"Installing .NET SDK {_resolvedVersion}", autoStart: true);

// Extract archive directly to target directory with special handling for muxer
var extractResult = ExtractArchiveDirectlyToTarget(_archivePath, _request.TargetDirectory, existingSdkVersions, installTask);
var extractResult = ExtractArchiveDirectlyToTarget(_archivePath, _request.InstallRoot.Path!, existingSdkVersions, installTask);
if (extractResult != null)
{
throw new InvalidOperationException($"Failed to install SDK: {extractResult}");
Expand All @@ -113,7 +114,7 @@ public void Commit(IEnumerable<DotnetVersion> existingSdkVersions)
* Extracts the archive directly to the target directory with special handling for muxer.
* Combines extraction and installation into a single operation.
*/
private string? ExtractArchiveDirectlyToTarget(string archivePath, string targetDir, IEnumerable<DotnetVersion> existingSdkVersions, Spectre.Console.ProgressTask? installTask)
private string? ExtractArchiveDirectlyToTarget(string archivePath, string targetDir, IEnumerable<ReleaseVersion> existingSdkVersions, Spectre.Console.ProgressTask? installTask)
{
try
{
Expand All @@ -140,14 +141,14 @@ public void Commit(IEnumerable<DotnetVersion> existingSdkVersions)
/**
* Configure muxer handling by determining if it needs to be updated.
*/
private MuxerHandlingConfig ConfigureMuxerHandling(IEnumerable<DotnetVersion> existingSdkVersions)
private MuxerHandlingConfig ConfigureMuxerHandling(IEnumerable<ReleaseVersion> existingSdkVersions)
{
DotnetVersion? existingMuxerVersion = existingSdkVersions.Any() ? existingSdkVersions.Max() : (DotnetVersion?)null;
DotnetVersion newRuntimeVersion = _install.FullySpecifiedVersion;
ReleaseVersion? existingMuxerVersion = existingSdkVersions.Any() ? existingSdkVersions.Max() : (ReleaseVersion?)null;
ReleaseVersion newRuntimeVersion = _resolvedVersion;
bool shouldUpdateMuxer = existingMuxerVersion is null || newRuntimeVersion.CompareTo(existingMuxerVersion) > 0;

string muxerName = DnupUtilities.GetDotnetExeName();
string muxerTargetPath = Path.Combine(_request.TargetDirectory, muxerName);
string muxerTargetPath = Path.Combine(_request.InstallRoot.Path!, muxerName);

return new MuxerHandlingConfig(
muxerName,
Expand Down Expand Up @@ -421,12 +422,16 @@ public void Dispose()
}
}

// TODO: InstallerOrchestratorSingleton also checks existing installs via the manifest. Which should we use and where?
// This should be cached and more sophisticated based on vscode logic in the future
private IEnumerable<DotnetVersion> GetExistingSdkVersions(string targetDirectory)
private IEnumerable<ReleaseVersion> GetExistingSdkVersions(DotnetInstallRoot installRoot)
{
var dotnetExe = Path.Combine(targetDirectory, DnupUtilities.GetDotnetExeName());
if (installRoot.Path == null)
return Enumerable.Empty<ReleaseVersion>();

var dotnetExe = Path.Combine(installRoot.Path, DnupUtilities.GetDotnetExeName());
if (!File.Exists(dotnetExe))
return Enumerable.Empty<DotnetVersion>();
return Enumerable.Empty<ReleaseVersion>();

try
{
Expand All @@ -440,14 +445,14 @@ private IEnumerable<DotnetVersion> GetExistingSdkVersions(string targetDirectory
var output = process.StandardOutput.ReadToEnd();
process.WaitForExit();

var versions = new List<DotnetVersion>();
var versions = new List<ReleaseVersion>();
foreach (var line in output.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries))
{
var parts = line.Split(' ');
if (parts.Length > 0)
{
var versionStr = parts[0];
if (DotnetVersion.TryParse(versionStr, out var version))
if (ReleaseVersion.TryParse(versionStr, out var version))
{
versions.Add(version);
}
Expand Down
34 changes: 15 additions & 19 deletions src/Installer/dnup/BootstrapperController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,17 @@ public BootstrapperController(IEnvironmentProvider? environmentProvider = null)
_environmentProvider = environmentProvider ?? new EnvironmentProvider();
}

public InstallType GetConfiguredInstallType(out string? currentInstallPath)
public DotnetInstallRoot GetConfiguredInstallType()
{
currentInstallPath = null;

string? foundDotnet = _environmentProvider.GetCommandPath("dotnet");
if (string.IsNullOrEmpty(foundDotnet))
{
return InstallType.None;
return new(null, InstallType.None, DnupUtilities.GetDefaultInstallArchitecture());
}

string installDir = Path.GetDirectoryName(foundDotnet)!;
currentInstallPath = installDir;


string? dotnetRoot = Environment.GetEnvironmentVariable("DOTNET_ROOT");
string programFiles = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
Expand All @@ -44,18 +44,18 @@ public InstallType GetConfiguredInstallType(out string? currentInstallPath)
!dotnetRoot.StartsWith(Path.Combine(programFiles, "dotnet"), StringComparison.OrdinalIgnoreCase) &&
!dotnetRoot.StartsWith(Path.Combine(programFilesX86, "dotnet"), StringComparison.OrdinalIgnoreCase))
{
return InstallType.Inconsistent;
return new(installDir, InstallType.Inconsistent, DnupUtilities.GetDefaultInstallArchitecture());
}
return InstallType.Admin;
return new(installDir, InstallType.Admin, DnupUtilities.GetDefaultInstallArchitecture());
}
else
{
// User install: DOTNET_ROOT must be set and match installDir
if (string.IsNullOrEmpty(dotnetRoot) || !DnupUtilities.PathsEqual(dotnetRoot, installDir))
{
return InstallType.Inconsistent;
return new(installDir, InstallType.Inconsistent, DnupUtilities.GetDefaultInstallArchitecture());
}
return InstallType.User;
return new(installDir, InstallType.User, DnupUtilities.GetDefaultInstallArchitecture());
}
}

Expand Down Expand Up @@ -96,35 +96,31 @@ public GlobalJsonInfo GetGlobalJsonInfo(string initialDirectory)
return null;
}

public void InstallSdks(string dotnetRoot, ProgressContext progressContext, IEnumerable<string> sdkVersions)
public void InstallSdks(DotnetInstallRoot dotnetRoot, ProgressContext progressContext, IEnumerable<string> sdkVersions)
{
foreach (var channelVersion in sdkVersions)
{
InstallSDK(dotnetRoot, progressContext, channelVersion);
InstallSDK(dotnetRoot, progressContext, new UpdateChannel(channelVersion));
}
}

private void InstallSDK(string dotnetRoot, ProgressContext progressContext, string channelVersion)
private void InstallSDK(DotnetInstallRoot dotnetRoot, ProgressContext progressContext, UpdateChannel channnel)
{
DotnetInstallRequest request = new DotnetInstallRequest(
channelVersion,
dotnetRoot,
InstallType.User,
InstallMode.SDK,
// Get current machine architecture and convert it to correct enum value
DnupUtilities.GetInstallArchitecture(RuntimeInformation.ProcessArchitecture),
new ManagementCadence(ManagementCadenceType.DNUP),
channnel,
InstallComponent.SDK,
new InstallRequestOptions()
);

DotnetInstall? newInstall = InstallerOrchestratorSingleton.Instance.Install(request);
if (newInstall == null)
{
throw new Exception($"Failed to install .NET SDK {channelVersion}");
throw new Exception($"Failed to install .NET SDK {channnel.Name}");
}
else
{
Spectre.Console.AnsiConsole.MarkupLine($"[green]Installed .NET SDK {newInstall.FullySpecifiedVersion}, available via {newInstall.MuxerDirectory}[/]");
Spectre.Console.AnsiConsole.MarkupLine($"[green]Installed .NET SDK {newInstall.Version}, available via {newInstall.InstallRoot}[/]");
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,16 @@ public string GetDefaultDotnetInstallPath()
return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "dotnet");
}

public InstallType GetConfiguredInstallType(out string? currentInstallPath)
public DotnetInstallRoot GetConfiguredInstallType()
{
var testHookDefaultInstall = Environment.GetEnvironmentVariable("DOTNET_TESTHOOK_DEFAULT_INSTALL");
InstallType returnValue = InstallType.None;
if (!Enum.TryParse<InstallType>(testHookDefaultInstall, out returnValue))
InstallType installtype = InstallType.None;
if (!Enum.TryParse<InstallType>(testHookDefaultInstall, out installtype))
{
returnValue = InstallType.None;
installtype = InstallType.None;
}
currentInstallPath = Environment.GetEnvironmentVariable("DOTNET_TESTHOOK_CURRENT_INSTALL_PATH");
return returnValue;
var installPath = Environment.GetEnvironmentVariable("DOTNET_TESTHOOK_CURRENT_INSTALL_PATH");
return new(installPath, installtype, DnupUtilities.GetDefaultInstallArchitecture());
}

public string? GetLatestInstalledAdminVersion()
Expand All @@ -49,7 +49,7 @@ public InstallType GetConfiguredInstallType(out string? currentInstallPath)
return latestAdminVersion;
}

public void InstallSdks(string dotnetRoot, ProgressContext progressContext, IEnumerable<string> sdkVersions)
public void InstallSdks(DotnetInstallRoot dotnetRoot, ProgressContext progressContext, IEnumerable<string> sdkVersions)
{
using (var httpClient = new HttpClient())
{
Expand Down
Loading