diff --git a/src/Cli/Microsoft.DotNet.Configurer/CliFolderPathCalculator.cs b/src/Cli/Microsoft.DotNet.Configurer/CliFolderPathCalculator.cs index a4d1d81b06ba..f667113ec253 100644 --- a/src/Cli/Microsoft.DotNet.Configurer/CliFolderPathCalculator.cs +++ b/src/Cli/Microsoft.DotNet.Configurer/CliFolderPathCalculator.cs @@ -11,8 +11,8 @@ namespace Microsoft.DotNet.Configurer { public static class CliFolderPathCalculator { - public const string DotnetHomeVariableName = "DOTNET_CLI_HOME"; - private const string DotnetProfileDirectoryName = ".dotnet"; + public const string DotnetHomeVariableName = CliFolderPathCalculatorCore.DotnetHomeVariableName; + private const string DotnetProfileDirectoryName = CliFolderPathCalculatorCore.DotnetProfileDirectoryName; private const string ToolsShimFolderName = "tools"; private const string ToolsResolverCacheFolderName = "toolResolverCache"; @@ -45,28 +45,18 @@ public static string WindowsNonExpandedToolsShimPath public static string ToolsResolverCachePath => Path.Combine(DotnetUserProfileFolderPath, ToolsResolverCacheFolderName); - public static string PlatformHomeVariableName => - OperatingSystem.IsWindows() ? "USERPROFILE" : "HOME"; + public static string PlatformHomeVariableName => CliFolderPathCalculatorCore.PlatformHomeVariableName; public static string DotnetHomePath { get { - var home = Environment.GetEnvironmentVariable(DotnetHomeVariableName); - if (string.IsNullOrEmpty(home)) - { - home = Environment.GetEnvironmentVariable(PlatformHomeVariableName); - if (string.IsNullOrEmpty(home)) - { - throw new ConfigurationException( - string.Format( - LocalizableStrings.FailedToDetermineUserHomeDirectory, - DotnetHomeVariableName)) - .DisplayAsError(); - } - } - - return home; + return CliFolderPathCalculatorCore.GetDotnetHomePath() + ?? throw new ConfigurationException( + string.Format( + LocalizableStrings.FailedToDetermineUserHomeDirectory, + DotnetHomeVariableName)) + .DisplayAsError(); } } diff --git a/src/Cli/Microsoft.DotNet.Configurer/Microsoft.DotNet.Configurer.csproj b/src/Cli/Microsoft.DotNet.Configurer/Microsoft.DotNet.Configurer.csproj index e9cdbfb81324..203d4f273e3a 100644 --- a/src/Cli/Microsoft.DotNet.Configurer/Microsoft.DotNet.Configurer.csproj +++ b/src/Cli/Microsoft.DotNet.Configurer/Microsoft.DotNet.Configurer.csproj @@ -24,4 +24,8 @@ + + + + diff --git a/src/Cli/dotnet/commands/RestoringCommand.cs b/src/Cli/dotnet/commands/RestoringCommand.cs index dc5f94f5edcc..9a69bddaa707 100644 --- a/src/Cli/dotnet/commands/RestoringCommand.cs +++ b/src/Cli/dotnet/commands/RestoringCommand.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Microsoft.DotNet.Configurer; using Microsoft.DotNet.Tools.MSBuild; using Microsoft.DotNet.Tools.Restore; using Microsoft.DotNet.Workloads.Workload.Install; @@ -18,10 +19,12 @@ public class RestoringCommand : MSBuildForwardingApp public RestoringCommand( IEnumerable msbuildArgs, bool noRestore, - string msbuildPath = null) + string msbuildPath = null, + string userProfileDir = null) : base(GetCommandArguments(msbuildArgs, noRestore), msbuildPath) { - Task.Run(() => WorkloadManifestUpdater.BackgroundUpdateAdvertisingManifestsAsync()); + userProfileDir = CliFolderPathCalculator.DotnetUserProfileFolderPath; + Task.Run(() => WorkloadManifestUpdater.BackgroundUpdateAdvertisingManifestsAsync(userProfileDir)); SeparateRestoreCommand = GetSeparateRestoreCommand(msbuildArgs, noRestore, msbuildPath); } diff --git a/src/Cli/dotnet/commands/dotnet-new/OptionalWorkloadProvider.cs b/src/Cli/dotnet/commands/dotnet-new/OptionalWorkloadProvider.cs index 28252a167be5..0e370a1d0cd8 100644 --- a/src/Cli/dotnet/commands/dotnet-new/OptionalWorkloadProvider.cs +++ b/src/Cli/dotnet/commands/dotnet-new/OptionalWorkloadProvider.cs @@ -1,4 +1,5 @@ using Microsoft.DotNet.Cli; +using Microsoft.DotNet.Configurer; using Microsoft.TemplateEngine.Abstractions; using Microsoft.TemplateEngine.Abstractions.TemplatePackage; using System; @@ -35,8 +36,9 @@ public Task> GetAllTemplatePackagesAsync(Cancell var sdkDirectory = Path.GetDirectoryName(typeof(DotnetFiles).Assembly.Location); var sdkVersion = Path.GetFileName(sdkDirectory); var dotnetRootPath = Path.GetDirectoryName(Path.GetDirectoryName(sdkDirectory)); + string userProfileDir = CliFolderPathCalculator.DotnetUserProfileFolderPath; - var packages = optionalWorkloadLocator.GetDotnetSdkTemplatePackages(sdkVersion, dotnetRootPath); + var packages = optionalWorkloadLocator.GetDotnetSdkTemplatePackages(sdkVersion, dotnetRootPath, userProfileDir); var fileSystem = _environmentSettings.Host.FileSystem; foreach (var packageInfo in packages) { diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/NetSdkManagedInstaller.cs b/src/Cli/dotnet/commands/dotnet-workload/install/NetSdkManagedInstaller.cs index 320a4482e5e5..30dd78018ee4 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/install/NetSdkManagedInstaller.cs +++ b/src/Cli/dotnet/commands/dotnet-workload/install/NetSdkManagedInstaller.cs @@ -8,6 +8,7 @@ using Microsoft.DotNet.Cli; using Microsoft.DotNet.Cli.NuGetPackageDownloader; using Microsoft.DotNet.Cli.Utils; +using Microsoft.DotNet.Configurer; using Microsoft.DotNet.ToolPackage; using Microsoft.Extensions.EnvironmentAbstractions; using Microsoft.NET.Sdk.WorkloadManifestReader; @@ -23,8 +24,9 @@ internal class NetSdkManagedInstaller : IWorkloadPackInstaller { private readonly IReporter _reporter; private readonly string _workloadMetadataDir; - private readonly string _installedPacksDir = "InstalledPacks"; + private const string InstalledPacksDir = "InstalledPacks"; protected readonly string _dotnetDir; + protected readonly string _userProfileDir; protected readonly DirectoryPath _tempPackagesDir; private readonly INuGetPackageDownloader _nugetPackageDownloader; private readonly IWorkloadResolver _workloadResolver; @@ -38,6 +40,7 @@ internal class NetSdkManagedInstaller : IWorkloadPackInstaller public NetSdkManagedInstaller(IReporter reporter, SdkFeatureBand sdkFeatureBand, IWorkloadResolver workloadResolver, + string userProfileDir, INuGetPackageDownloader nugetPackageDownloader = null, string dotnetDir = null, string tempDirPath = null, @@ -45,6 +48,7 @@ public NetSdkManagedInstaller(IReporter reporter, PackageSourceLocation packageSourceLocation = null, RestoreActionConfig restoreActionConfig = null) { + _userProfileDir = userProfileDir; _dotnetDir = dotnetDir ?? Path.GetDirectoryName(Environment.ProcessPath); _tempPackagesDir = new DirectoryPath(tempDirPath ?? Path.GetTempPath()); ILogger logger = verbosity.VerbosityIsDetailedOrDiagnostic() ? new NuGetConsoleLogger() : new NullLogger(); @@ -53,11 +57,12 @@ public NetSdkManagedInstaller(IReporter reporter, new NuGetPackageDownloader(_tempPackagesDir, filePermissionSetter: null, new FirstPartyNuGetPackageSigningVerifier(_tempPackagesDir), logger, restoreActionConfig: _restoreActionConfig); - _workloadMetadataDir = Path.Combine(_dotnetDir, "metadata", "workloads"); + bool userLocal = WorkloadFileBasedInstall.IsUserLocal(_dotnetDir, sdkFeatureBand.ToString()); + _workloadMetadataDir = Path.Combine(userLocal ? _userProfileDir : _dotnetDir, "metadata", "workloads"); _reporter = reporter; _sdkFeatureBand = sdkFeatureBand; _workloadResolver = workloadResolver; - _installationRecordRepository = new NetSdkManagedInstallationRecordRepository(_dotnetDir); + _installationRecordRepository = new NetSdkManagedInstallationRecordRepository(_workloadMetadataDir); _packageSourceLocation = packageSourceLocation; } @@ -190,7 +195,8 @@ public void InstallWorkloadManifest(ManifestId manifestId, ManifestVersion manif string packagePath = null; string tempExtractionDir = null; string tempBackupDir = null; - var manifestPath = Path.Combine(_dotnetDir, "sdk-manifests", sdkFeatureBand.ToString(), manifestId.ToString()); + string rootInstallDir = WorkloadFileBasedInstall.IsUserLocal(_dotnetDir, sdkFeatureBand.ToString()) ? _userProfileDir : _dotnetDir; + var manifestPath = Path.Combine(rootInstallDir, "sdk-manifests", sdkFeatureBand.ToString(), manifestId.ToString()); _reporter.WriteLine(string.Format(LocalizableStrings.InstallingWorkloadManifest, manifestId, manifestVersion)); @@ -287,10 +293,10 @@ public void DownloadToOfflineCache(PackInfo packInfo, DirectoryPath cachePath, b public void GarbageCollectInstalledWorkloadPacks(DirectoryPath? offlineCache = null) { - var installedPacksDir = Path.Combine(_workloadMetadataDir, _installedPacksDir, "v1"); var installedSdkFeatureBands = _installationRecordRepository.GetFeatureBandsWithInstallationRecords(); _reporter.WriteLine(string.Format(LocalizableStrings.GarbageCollectingSdkFeatureBandsMessage, string.Join(" ", installedSdkFeatureBands))); var currentBandInstallRecords = GetExpectedPackInstallRecords(_sdkFeatureBand); + string installedPacksDir = Path.Combine(_workloadMetadataDir, InstalledPacksDir, "v1"); if (!Directory.Exists(installedPacksDir)) { @@ -346,7 +352,7 @@ public void GarbageCollectInstalledWorkloadPacks(DirectoryPath? offlineCache = n public IEnumerable<(WorkloadPackId, string)> GetInstalledPacks(SdkFeatureBand sdkFeatureBand) { - var installedPacksDir = Path.Combine(_workloadMetadataDir, _installedPacksDir, "v1"); + var installedPacksDir = Path.Combine(_workloadMetadataDir, InstalledPacksDir, "v1"); if (!Directory.Exists(installedPacksDir)) { return Enumerable.Empty<(WorkloadPackId, string)>(); @@ -429,7 +435,7 @@ private void DeletePack(PackInfo packInfo) } private string GetPackInstallRecordPath(PackInfo packInfo, SdkFeatureBand featureBand) => - Path.Combine(_workloadMetadataDir, _installedPacksDir, "v1", packInfo.Id, packInfo.Version, featureBand.ToString()); + Path.Combine(_workloadMetadataDir, InstalledPacksDir, "v1", packInfo.Id, packInfo.Version, featureBand.ToString()); private void WritePackInstallationRecord(PackInfo packInfo, SdkFeatureBand featureBand) { @@ -465,7 +471,7 @@ private void DeletePackInstallationRecord(PackInfo packInfo, SdkFeatureBand feat private bool PackHasInstallRecords(PackInfo packInfo) { - var packInstallRecordDir = Path.Combine(_workloadMetadataDir, _installedPacksDir, "v1", packInfo.Id, packInfo.Version); + var packInstallRecordDir = Path.Combine(_workloadMetadataDir, InstalledPacksDir, "v1", packInfo.Id, packInfo.Version); return Directory.Exists(packInstallRecordDir) && Directory.GetFiles(packInstallRecordDir).Any(); } diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadInstallCommand.cs b/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadInstallCommand.cs index 80168c082522..6276c60ec1f1 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadInstallCommand.cs +++ b/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadInstallCommand.cs @@ -42,7 +42,7 @@ internal class WorkloadInstallCommand : CommandBase private readonly IWorkloadManifestUpdater _workloadManifestUpdater; private readonly ReleaseVersion _sdkVersion; private readonly SdkFeatureBand _sdkFeatureBand; - private readonly string _userHome; + private readonly string _userProfileDir; private readonly string _tempDirPath; private readonly string _dotnetPath; @@ -54,7 +54,7 @@ public WorkloadInstallCommand( INuGetPackageDownloader nugetPackageDownloader = null, IWorkloadManifestUpdater workloadManifestUpdater = null, string dotnetDir = null, - string userHome = null, + string userProfileDir = null, string tempDirPath = null, string version = null, IReadOnlyCollection workloadIds = null) @@ -69,7 +69,8 @@ public WorkloadInstallCommand( _workloadIds = workloadIds ?? parseResult.ValueForArgument>(WorkloadInstallCommandParser.WorkloadIdArgument).ToList().AsReadOnly(); _verbosity = parseResult.ValueForOption(WorkloadInstallCommandParser.VerbosityOption); _dotnetPath = dotnetDir ?? Path.GetDirectoryName(Environment.ProcessPath); - _sdkVersion = WorkloadOptionsExtensions.GetValidatedSdkVersion(parseResult.ValueForOption(WorkloadInstallCommandParser.VersionOption), version, _dotnetPath); + _userProfileDir = userProfileDir ?? CliFolderPathCalculator.DotnetUserProfileFolderPath; + _sdkVersion = WorkloadOptionsExtensions.GetValidatedSdkVersion(parseResult.ValueForOption(WorkloadInstallCommandParser.VersionOption), version, _dotnetPath, _userProfileDir); _sdkFeatureBand = new SdkFeatureBand(string.Join('.', _sdkVersion.Major, _sdkVersion.Minor, _sdkVersion.SdkFeatureBand)); _tempDirPath = tempDirPath ?? (string.IsNullOrWhiteSpace(parseResult.ValueForOption(WorkloadInstallCommandParser.TempDirOption)) ? Path.GetTempPath() : @@ -80,8 +81,8 @@ public WorkloadInstallCommand( _packageSourceLocation = string.IsNullOrEmpty(configOption) && (sourceOption == null || !sourceOption.Any()) ? null : new PackageSourceLocation(string.IsNullOrEmpty(configOption) ? null : new FilePath(configOption), sourceFeedOverrides: sourceOption); - var sdkWorkloadManifestProvider = new SdkDirectoryWorkloadManifestProvider(_dotnetPath, _sdkVersion.ToString()); - _workloadResolver = workloadResolver ?? WorkloadResolver.Create(sdkWorkloadManifestProvider, _dotnetPath, _sdkVersion.ToString()); + var sdkWorkloadManifestProvider = new SdkDirectoryWorkloadManifestProvider(_dotnetPath, _sdkVersion.ToString(), userProfileDir); + _workloadResolver = workloadResolver ?? WorkloadResolver.Create(sdkWorkloadManifestProvider, _dotnetPath, _sdkVersion.ToString(), _userProfileDir); var sdkFeatureBand = new SdkFeatureBand(_sdkVersion); var tempPackagesDir = new DirectoryPath(Path.Combine(_tempDirPath, "dotnet-sdk-advertising-temp")); var restoreActionConfig = _parseResult.ToRestoreActionConfig(); @@ -92,10 +93,9 @@ public WorkloadInstallCommand( _verbosity.VerbosityIsDetailedOrDiagnostic() ? new NuGetConsoleLogger() : new NullLogger(), restoreActionConfig: restoreActionConfig); _workloadInstaller = workloadInstaller ?? WorkloadInstallerFactory.GetWorkloadInstaller(_reporter, sdkFeatureBand, - _workloadResolver, _verbosity, _nugetPackageDownloader, _dotnetPath, _tempDirPath, + _workloadResolver, _verbosity, _userProfileDir, _nugetPackageDownloader, _dotnetPath, _tempDirPath, _packageSourceLocation, restoreActionConfig, elevationRequired: !_printDownloadLinkOnly && string.IsNullOrWhiteSpace(_downloadToCacheOption)); - _userHome = userHome ?? CliFolderPathCalculator.DotnetHomePath; - _workloadManifestUpdater = workloadManifestUpdater ?? new WorkloadManifestUpdater(_reporter, _workloadResolver, _nugetPackageDownloader, _userHome, _tempDirPath, + _workloadManifestUpdater = workloadManifestUpdater ?? new WorkloadManifestUpdater(_reporter, _workloadResolver, _nugetPackageDownloader, _userProfileDir, _tempDirPath, _workloadInstaller.GetWorkloadInstallationRecordRepository(), _packageSourceLocation); ValidateWorkloadIdsInput(); diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadInstallRecords/NetSdkManagedInstallationRecordInstaller.cs b/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadInstallRecords/NetSdkManagedInstallationRecordInstaller.cs index b34fce388dc6..518bf698affe 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadInstallRecords/NetSdkManagedInstallationRecordInstaller.cs +++ b/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadInstallRecords/NetSdkManagedInstallationRecordInstaller.cs @@ -12,11 +12,11 @@ namespace Microsoft.DotNet.Workloads.Workload.Install.InstallRecord internal class NetSdkManagedInstallationRecordRepository : IWorkloadInstallationRecordRepository { private readonly string _workloadMetadataDir; - private readonly string _installedWorkloadDir = "InstalledWorkloads"; + private const string InstalledWorkloadDir = "InstalledWorkloads"; - public NetSdkManagedInstallationRecordRepository(string dotnetDir) + public NetSdkManagedInstallationRecordRepository(string workloadMetadataDir) { - _workloadMetadataDir = Path.Combine(dotnetDir, "metadata", "workloads"); + _workloadMetadataDir = workloadMetadataDir; } public IEnumerable GetFeatureBandsWithInstallationRecords() @@ -25,7 +25,7 @@ public IEnumerable GetFeatureBandsWithInstallationRecords() { var bands = Directory.EnumerateDirectories(_workloadMetadataDir); return bands - .Where(band => Directory.Exists(Path.Combine(band, _installedWorkloadDir)) && Directory.GetFiles(Path.Combine(band, _installedWorkloadDir)).Any()) + .Where(band => Directory.Exists(Path.Combine(band, InstalledWorkloadDir)) && Directory.GetFiles(Path.Combine(band, InstalledWorkloadDir)).Any()) .Select(path => new SdkFeatureBand(Path.GetFileName(path))); } else @@ -36,7 +36,7 @@ public IEnumerable GetFeatureBandsWithInstallationRecords() public IEnumerable GetInstalledWorkloads(SdkFeatureBand featureBand) { - var path = Path.Combine(_workloadMetadataDir, featureBand.ToString(), _installedWorkloadDir); + var path = Path.Combine(_workloadMetadataDir, featureBand.ToString(), InstalledWorkloadDir); if (Directory.Exists(path)) { return Directory.EnumerateFiles(path) @@ -50,7 +50,7 @@ public IEnumerable GetInstalledWorkloads(SdkFeatureBand featureBand) public void WriteWorkloadInstallationRecord(WorkloadId workloadId, SdkFeatureBand featureBand) { - var path = Path.Combine(_workloadMetadataDir, featureBand.ToString(), _installedWorkloadDir, workloadId.ToString()); + var path = Path.Combine(_workloadMetadataDir, featureBand.ToString(), InstalledWorkloadDir, workloadId.ToString()); if (!File.Exists(path)) { var pathDir = Path.GetDirectoryName(path); @@ -64,7 +64,7 @@ public void WriteWorkloadInstallationRecord(WorkloadId workloadId, SdkFeatureBan public void DeleteWorkloadInstallationRecord(WorkloadId workloadId, SdkFeatureBand featureBand) { - var path = Path.Combine(_workloadMetadataDir, featureBand.ToString(), _installedWorkloadDir, workloadId.ToString()); + var path = Path.Combine(_workloadMetadataDir, featureBand.ToString(), InstalledWorkloadDir, workloadId.ToString()); if (File.Exists(path)) { File.Delete(path); diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadInstallerFactory.cs b/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadInstallerFactory.cs index 431e4e823ea4..a0557684ee16 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadInstallerFactory.cs +++ b/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadInstallerFactory.cs @@ -8,6 +8,7 @@ using Microsoft.DotNet.Workloads.Workload.Install.InstallRecord; using Microsoft.DotNet.Cli; using Microsoft.DotNet.Cli.NuGetPackageDownloader; +using Microsoft.DotNet.Configurer; namespace Microsoft.DotNet.Workloads.Workload.Install { @@ -18,6 +19,7 @@ public static IInstaller GetWorkloadInstaller( SdkFeatureBand sdkFeatureBand, IWorkloadResolver workloadResolver, VerbosityOptions verbosity, + string userProfileDir, INuGetPackageDownloader nugetPackageDownloader = null, string dotnetDir = null, string tempDirPath = null, @@ -40,14 +42,17 @@ public static IInstaller GetWorkloadInstaller( nugetPackageDownloader, verbosity, packageSourceLocation, reporter, tempDirPath); } - if (elevationRequired && !CanWriteToDotnetRoot(dotnetDir)) + if (elevationRequired && !WorkloadFileBasedInstall.IsUserLocal(dotnetDir, sdkFeatureBand.ToString()) && !CanWriteToDotnetRoot(dotnetDir)) { throw new GracefulException(LocalizableStrings.InadequatePermissions, isUserError: false); } + userProfileDir ??= CliFolderPathCalculator.DotnetUserProfileFolderPath; + return new NetSdkManagedInstaller(reporter, sdkFeatureBand, workloadResolver, + userProfileDir, nugetPackageDownloader, dotnetDir: dotnetDir, tempDirPath: tempDirPath, diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadManifestUpdater.cs b/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadManifestUpdater.cs index 3fba464eb6b9..8b0bcb0f4a4e 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadManifestUpdater.cs +++ b/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadManifestUpdater.cs @@ -27,7 +27,7 @@ internal class WorkloadManifestUpdater : IWorkloadManifestUpdater private readonly IWorkloadResolver _workloadResolver; private readonly INuGetPackageDownloader _nugetPackageDownloader; private readonly SdkFeatureBand _sdkFeatureBand; - private readonly string _userHome; + private readonly string _userProfileDir; private readonly string _tempDirPath; private readonly PackageSourceLocation _packageSourceLocation; Func _getEnvironmentVariable; @@ -36,7 +36,7 @@ internal class WorkloadManifestUpdater : IWorkloadManifestUpdater public WorkloadManifestUpdater(IReporter reporter, IWorkloadResolver workloadResolver, INuGetPackageDownloader nugetPackageDownloader, - string userHome, + string userProfileDir, string tempDirPath, IWorkloadInstallationRecordRepository workloadRecordRepo, PackageSourceLocation packageSourceLocation = null, @@ -44,7 +44,7 @@ public WorkloadManifestUpdater(IReporter reporter, { _reporter = reporter; _workloadResolver = workloadResolver; - _userHome = userHome; + _userProfileDir = userProfileDir; _tempDirPath = tempDirPath; _nugetPackageDownloader = nugetPackageDownloader; _sdkFeatureBand = new SdkFeatureBand(_workloadResolver.GetSdkFeatureBand()); @@ -53,24 +53,23 @@ public WorkloadManifestUpdater(IReporter reporter, _workloadRecordRepo = workloadRecordRepo; } - private static WorkloadManifestUpdater GetInstance() + private static WorkloadManifestUpdater GetInstance(string userProfileDir) { var reporter = new NullReporter(); var dotnetPath = Path.GetDirectoryName(Environment.ProcessPath); var sdkVersion = Product.Version; - var workloadManifestProvider = new SdkDirectoryWorkloadManifestProvider(dotnetPath, sdkVersion); - var workloadResolver = WorkloadResolver.Create(workloadManifestProvider, dotnetPath, sdkVersion); + var workloadManifestProvider = new SdkDirectoryWorkloadManifestProvider(dotnetPath, sdkVersion, userProfileDir); + var workloadResolver = WorkloadResolver.Create(workloadManifestProvider, dotnetPath, sdkVersion, userProfileDir); var tempPackagesDir = new DirectoryPath(Path.Combine(Path.GetTempPath(), "dotnet-sdk-advertising-temp")); var nugetPackageDownloader = new NuGetPackageDownloader(tempPackagesDir, filePermissionSetter: null, new FirstPartyNuGetPackageSigningVerifier(tempPackagesDir, new NullLogger()), new NullLogger(), reporter); - var userHome = CliFolderPathCalculator.DotnetHomePath; - var workloadRecordRepo = WorkloadInstallerFactory.GetWorkloadInstaller(reporter, new SdkFeatureBand(sdkVersion), workloadResolver, Cli.VerbosityOptions.normal) + var workloadRecordRepo = WorkloadInstallerFactory.GetWorkloadInstaller(reporter, new SdkFeatureBand(sdkVersion), workloadResolver, Cli.VerbosityOptions.normal, userProfileDir) .GetWorkloadInstallationRecordRepository(); - return new WorkloadManifestUpdater(reporter, workloadResolver, nugetPackageDownloader, userHome, tempPackagesDir.Value, workloadRecordRepo); + return new WorkloadManifestUpdater(reporter, workloadResolver, nugetPackageDownloader, userProfileDir, tempPackagesDir.Value, workloadRecordRepo); } public async Task UpdateAdvertisingManifestsAsync(bool includePreviews, DirectoryPath? offlineCache = null) @@ -81,11 +80,11 @@ await Task.WhenAll(manifests.Select(manifest => UpdateAdvertisingManifestAsync(m WriteUpdatableWorkloadsFile(); } - public async static Task BackgroundUpdateAdvertisingManifestsAsync() + public async static Task BackgroundUpdateAdvertisingManifestsAsync(string userProfileDir) { try { - var manifestUpdater = WorkloadManifestUpdater.GetInstance(); + var manifestUpdater = WorkloadManifestUpdater.GetInstance(userProfileDir); await manifestUpdater.BackgroundUpdateAdvertisingManifestsWhenRequiredAsync(); } catch (Exception) @@ -140,7 +139,7 @@ public static void AdvertiseWorkloadUpdates() try { var backgroundUpdatesDisabled = bool.TryParse(Environment.GetEnvironmentVariable(EnvironmentVariableNames.WORKLOAD_UPDATE_NOTIFY_DISABLE), out var disableEnvVar) && disableEnvVar; - var adUpdatesFile = GetAdvertisingWorkloadsFilePath(CliFolderPathCalculator.DotnetHomePath); + var adUpdatesFile = GetAdvertisingWorkloadsFilePath(CliFolderPathCalculator.DotnetUserProfileFolderPath); if (!backgroundUpdatesDisabled && File.Exists(adUpdatesFile)) { var updatableWorkloads = JsonSerializer.Deserialize(File.ReadAllText(adUpdatesFile)); @@ -192,7 +191,7 @@ public IEnumerable GetUpdatableWorkloadsToAdvertise(IEnumerable NewerManifestPackageExists(ManifestId manifest) private bool BackgroundUpdatesAreDisabled() => bool.TryParse(_getEnvironmentVariable(EnvironmentVariableNames.WORKLOAD_UPDATE_NOTIFY_DISABLE), out var disableEnvVar) && disableEnvVar; - private string GetAdvertisingManifestSentinalPath() => Path.Combine(_userHome, ".dotnet", ".workloadAdvertisingManifestSentinal"); + private string GetAdvertisingManifestSentinalPath() => Path.Combine(_userProfileDir, ".workloadAdvertisingManifestSentinal"); - private string GetAdvertisingWorkloadsFilePath() => GetAdvertisingWorkloadsFilePath(_userHome); + private string GetAdvertisingWorkloadsFilePath() => GetAdvertisingWorkloadsFilePath(_userProfileDir); - private static string GetAdvertisingWorkloadsFilePath(string userHome) => Path.Combine(userHome, ".dotnet", ".workloadAdvertisingUpdates"); + private static string GetAdvertisingWorkloadsFilePath(string userProfileDir) => Path.Combine(userProfileDir, ".workloadAdvertisingUpdates"); private string GetAdvertisingManifestPath(SdkFeatureBand featureBand, ManifestId manifestId) => - Path.Combine(_userHome, ".dotnet", "sdk-advertising", featureBand.ToString(), manifestId.ToString()); + Path.Combine(_userProfileDir, "sdk-advertising", featureBand.ToString(), manifestId.ToString()); internal static PackageId GetManifestPackageId(SdkFeatureBand featureBand, ManifestId manifestId) => GetManifestPackageId(featureBand, manifestId, InstallType.FileBased); diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadOptionsExtensions.cs b/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadOptionsExtensions.cs index 4596e388d910..1584f8c7634d 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadOptionsExtensions.cs +++ b/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadOptionsExtensions.cs @@ -17,7 +17,7 @@ namespace Microsoft.DotNet.Workloads.Workload.Install { internal class WorkloadOptionsExtensions { - internal static ReleaseVersion GetValidatedSdkVersion(string versionOption, string providedVersion, string dotnetPath) + internal static ReleaseVersion GetValidatedSdkVersion(string versionOption, string providedVersion, string dotnetPath, string userProfileDir) { if (string.IsNullOrEmpty(versionOption)) @@ -26,7 +26,7 @@ internal static ReleaseVersion GetValidatedSdkVersion(string versionOption, stri } else { - var manifests = new SdkDirectoryWorkloadManifestProvider(dotnetPath, versionOption).GetManifests(); + var manifests = new SdkDirectoryWorkloadManifestProvider(dotnetPath, versionOption, userProfileDir).GetManifests(); if (!manifests.Any()) { throw new GracefulException(string.Format(LocalizableStrings.NoManifestsExistForFeatureBand, versionOption), isUserError: false); diff --git a/src/Cli/dotnet/commands/dotnet-workload/list/WorkloadListCommand.cs b/src/Cli/dotnet/commands/dotnet-workload/list/WorkloadListCommand.cs index 44d0a864b07c..109a49d1a7c7 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/list/WorkloadListCommand.cs +++ b/src/Cli/dotnet/commands/dotnet-workload/list/WorkloadListCommand.cs @@ -31,7 +31,7 @@ internal class WorkloadListCommand : CommandBase private readonly IReporter _reporter; private readonly string _targetSdkVersion; private readonly string _tempDirPath; - private readonly string _userHome; + private readonly string _userProfileDir; private readonly VerbosityOptions _verbosity; private readonly IWorkloadManifestUpdater _workloadManifestUpdater; private readonly IWorkloadInstallationRecordRepository _workloadRecordRepo; @@ -42,7 +42,7 @@ public WorkloadListCommand( IWorkloadInstallationRecordRepository workloadRecordRepo = null, string currentSdkVersion = null, string dotnetDir = null, - string userHome = null, + string userProfileDir = null, string tempDirPath = null, INuGetPackageDownloader nugetPackageDownloader = null, IWorkloadManifestUpdater workloadManifestUpdater = null, @@ -64,28 +64,29 @@ public WorkloadListCommand( ? Path.GetTempPath() : result.ValueForOption(WorkloadListCommandParser.TempDirOption)); _targetSdkVersion = result.ValueForOption(WorkloadListCommandParser.VersionOption); + _userProfileDir = userProfileDir ?? CliFolderPathCalculator.DotnetUserProfileFolderPath; var workloadManifestProvider = new SdkDirectoryWorkloadManifestProvider(_dotnetPath, string.IsNullOrWhiteSpace(_targetSdkVersion) ? currentSdkReleaseVersion.ToString() - : _targetSdkVersion); - _userHome = userHome ?? CliFolderPathCalculator.DotnetHomePath; + : _targetSdkVersion, + _userProfileDir); DirectoryPath tempPackagesDir = - new(Path.Combine(_userHome, ".dotnet", "sdk-advertising-temp")); + new(Path.Combine(_userProfileDir, "sdk-advertising-temp")); NullLogger nullLogger = new NullLogger(); _nugetPackageDownloader = nugetPackageDownloader ?? new NuGetPackageDownloader(tempPackagesDir, null, new FirstPartyNuGetPackageSigningVerifier(tempPackagesDir, nullLogger), verboseLogger: nullLogger, restoreActionConfig: _parseResult.ToRestoreActionConfig()); - workloadResolver ??= WorkloadResolver.Create(workloadManifestProvider, _dotnetPath, currentSdkReleaseVersion.ToString()); + workloadResolver ??= WorkloadResolver.Create(workloadManifestProvider, _dotnetPath, currentSdkReleaseVersion.ToString(), _userProfileDir); _workloadRecordRepo = workloadRecordRepo ?? - WorkloadInstallerFactory.GetWorkloadInstaller(reporter, _currentSdkFeatureBand, workloadResolver, _verbosity, + WorkloadInstallerFactory.GetWorkloadInstaller(reporter, _currentSdkFeatureBand, workloadResolver, _verbosity, _userProfileDir, elevationRequired: false).GetWorkloadInstallationRecordRepository(); _workloadManifestUpdater = workloadManifestUpdater ?? new WorkloadManifestUpdater(_reporter, - workloadResolver, _nugetPackageDownloader, _userHome, _tempDirPath, _workloadRecordRepo); + workloadResolver, _nugetPackageDownloader, _userProfileDir, _tempDirPath, _workloadRecordRepo); } public override int Execute() diff --git a/src/Cli/dotnet/commands/dotnet-workload/repair/WorkloadRepairCommand.cs b/src/Cli/dotnet/commands/dotnet-workload/repair/WorkloadRepairCommand.cs index d2655a75a548..f6d90a13d902 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/repair/WorkloadRepairCommand.cs +++ b/src/Cli/dotnet/commands/dotnet-workload/repair/WorkloadRepairCommand.cs @@ -7,6 +7,7 @@ using Microsoft.Deployment.DotNet.Releases; using Microsoft.DotNet.Cli; using Microsoft.DotNet.Cli.Utils; +using Microsoft.DotNet.Configurer; using Microsoft.NET.Sdk.WorkloadManifestReader; using System.IO; using System.Linq; @@ -36,21 +37,23 @@ public WorkloadRepairCommand( INuGetPackageDownloader nugetPackageDownloader = null, string dotnetDir = null, string tempDirPath = null, - string version = null) + string version = null, + string userProfileDir = null) : base(parseResult) { _reporter = reporter ?? Reporter.Output; _verbosity = parseResult.ValueForOption(WorkloadRepairCommandParser.VerbosityOption); _dotnetPath = dotnetDir ?? Path.GetDirectoryName(Environment.ProcessPath); - _sdkVersion = WorkloadOptionsExtensions.GetValidatedSdkVersion(parseResult.ValueForOption(WorkloadRepairCommandParser.VersionOption), version, _dotnetPath); + userProfileDir ??= CliFolderPathCalculator.DotnetUserProfileFolderPath; + _sdkVersion = WorkloadOptionsExtensions.GetValidatedSdkVersion(parseResult.ValueForOption(WorkloadRepairCommandParser.VersionOption), version, _dotnetPath, userProfileDir); var configOption = parseResult.ValueForOption(WorkloadRepairCommandParser.ConfigOption); var sourceOption = parseResult.ValueForOption(WorkloadRepairCommandParser.SourceOption); _packageSourceLocation = string.IsNullOrEmpty(configOption) && (sourceOption == null || !sourceOption.Any()) ? null : new PackageSourceLocation(string.IsNullOrEmpty(configOption) ? null : new FilePath(configOption), sourceFeedOverrides: sourceOption); - var workloadManifestProvider = new SdkDirectoryWorkloadManifestProvider(_dotnetPath, _sdkVersion.ToString()); - _workloadResolver = workloadResolver ?? WorkloadResolver.Create(workloadManifestProvider, _dotnetPath, _sdkVersion.ToString()); + var workloadManifestProvider = new SdkDirectoryWorkloadManifestProvider(_dotnetPath, _sdkVersion.ToString(), userProfileDir); + _workloadResolver = workloadResolver ?? WorkloadResolver.Create(workloadManifestProvider, _dotnetPath, _sdkVersion.ToString(), userProfileDir); var sdkFeatureBand = new SdkFeatureBand(_sdkVersion); tempDirPath = tempDirPath ?? (string.IsNullOrWhiteSpace(parseResult.ValueForOption(WorkloadInstallCommandParser.TempDirOption)) ? Path.GetTempPath() : @@ -63,7 +66,7 @@ public WorkloadRepairCommand( new FirstPartyNuGetPackageSigningVerifier(tempPackagesDir, nullLogger), nullLogger, restoreActionConfig: _parseResult.ToRestoreActionConfig()); _workloadInstaller = workloadInstaller ?? WorkloadInstallerFactory.GetWorkloadInstaller(_reporter, sdkFeatureBand, - _workloadResolver, _verbosity, nugetPackageDownloader, dotnetDir, tempDirPath, + _workloadResolver, _verbosity, userProfileDir, nugetPackageDownloader, dotnetDir, tempDirPath, _packageSourceLocation, _parseResult.ToRestoreActionConfig()); } diff --git a/src/Cli/dotnet/commands/dotnet-workload/search/WorkloadSearchCommand.cs b/src/Cli/dotnet/commands/dotnet-workload/search/WorkloadSearchCommand.cs index 85943fdc1ea2..05b160e123cc 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/search/WorkloadSearchCommand.cs +++ b/src/Cli/dotnet/commands/dotnet-workload/search/WorkloadSearchCommand.cs @@ -7,6 +7,7 @@ using Microsoft.Deployment.DotNet.Releases; using Microsoft.DotNet.Cli; using Microsoft.DotNet.Cli.Utils; +using Microsoft.DotNet.Configurer; using Microsoft.NET.Sdk.WorkloadManifestReader; using System.Linq; using Microsoft.DotNet.Workloads.Workload.Install; @@ -26,15 +27,17 @@ public WorkloadSearchCommand( ParseResult result, IReporter reporter = null, IWorkloadResolver workloadResolver = null, - string version = null) : base(result) + string version = null, + string userProfileDir = null) : base(result) { _reporter = reporter ?? Reporter.Output; _verbosity = result.ValueForOption(WorkloadSearchCommandParser.VerbosityOption); _workloadIdStub = result.ValueForArgument(WorkloadSearchCommandParser.WorkloadIdStubArgument); var dotnetPath = Path.GetDirectoryName(Environment.ProcessPath); - _sdkVersion = WorkloadOptionsExtensions.GetValidatedSdkVersion(result.ValueForOption(WorkloadSearchCommandParser.VersionOption), version, dotnetPath); - var workloadManifestProvider = new SdkDirectoryWorkloadManifestProvider(dotnetPath, _sdkVersion.ToString()); - _workloadResolver = workloadResolver ?? WorkloadResolver.Create(workloadManifestProvider, dotnetPath, _sdkVersion.ToString()); + userProfileDir ??= CliFolderPathCalculator.DotnetUserProfileFolderPath; + _sdkVersion = WorkloadOptionsExtensions.GetValidatedSdkVersion(result.ValueForOption(WorkloadSearchCommandParser.VersionOption), version, dotnetPath, userProfileDir); + var workloadManifestProvider = new SdkDirectoryWorkloadManifestProvider(dotnetPath, _sdkVersion.ToString(), userProfileDir); + _workloadResolver = workloadResolver ?? WorkloadResolver.Create(workloadManifestProvider, dotnetPath, _sdkVersion.ToString(), userProfileDir); } public override int Execute() diff --git a/src/Cli/dotnet/commands/dotnet-workload/uninstall/WorkloadUninstallCommand.cs b/src/Cli/dotnet/commands/dotnet-workload/uninstall/WorkloadUninstallCommand.cs index b5caf9f4e51e..7c978970fd1c 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/uninstall/WorkloadUninstallCommand.cs +++ b/src/Cli/dotnet/commands/dotnet-workload/uninstall/WorkloadUninstallCommand.cs @@ -10,6 +10,7 @@ using Microsoft.DotNet.Cli; using Microsoft.DotNet.Cli.NuGetPackageDownloader; using Microsoft.DotNet.Cli.Utils; +using Microsoft.DotNet.Configurer; using Microsoft.DotNet.Workloads.Workload.Install; using Microsoft.DotNet.Workloads.Workload.Install.InstallRecord; using Microsoft.Extensions.EnvironmentAbstractions; @@ -32,20 +33,22 @@ public WorkloadUninstallCommand( IWorkloadResolver workloadResolver = null, INuGetPackageDownloader nugetPackageDownloader = null, string dotnetDir = null, - string version = null) + string version = null, + string userProfileDir = null) : base(parseResult) { _reporter = reporter ?? Reporter.Output; _workloadIds = parseResult.ValueForArgument>(WorkloadUninstallCommandParser.WorkloadIdArgument) .Select(workloadId => new WorkloadId(workloadId)).ToList().AsReadOnly(); var dotnetPath = dotnetDir ?? Path.GetDirectoryName(Environment.ProcessPath); - _sdkVersion = WorkloadOptionsExtensions.GetValidatedSdkVersion(parseResult.ValueForOption(WorkloadUninstallCommandParser.VersionOption), version, dotnetPath); + userProfileDir = userProfileDir ?? CliFolderPathCalculator.DotnetUserProfileFolderPath; + _sdkVersion = WorkloadOptionsExtensions.GetValidatedSdkVersion(parseResult.ValueForOption(WorkloadUninstallCommandParser.VersionOption), version, dotnetPath, userProfileDir); var verbosity = parseResult.ValueForOption(WorkloadUninstallCommandParser.VerbosityOption); - var workloadManifestProvider = new SdkDirectoryWorkloadManifestProvider(dotnetPath, _sdkVersion.ToString()); - workloadResolver ??= WorkloadResolver.Create(workloadManifestProvider, dotnetPath, _sdkVersion.ToString()); + var workloadManifestProvider = new SdkDirectoryWorkloadManifestProvider(dotnetPath, _sdkVersion.ToString(), userProfileDir); + workloadResolver ??= WorkloadResolver.Create(workloadManifestProvider, dotnetPath, _sdkVersion.ToString(), userProfileDir); nugetPackageDownloader ??= new NuGetPackageDownloader(new DirectoryPath(Path.GetTempPath()), filePermissionSetter: null, verboseLogger: new NullLogger()); var sdkFeatureBand = new SdkFeatureBand(_sdkVersion); - _workloadInstaller = WorkloadInstallerFactory.GetWorkloadInstaller(_reporter, sdkFeatureBand, workloadResolver, verbosity, nugetPackageDownloader, dotnetPath); + _workloadInstaller = WorkloadInstallerFactory.GetWorkloadInstaller(_reporter, sdkFeatureBand, workloadResolver, verbosity, userProfileDir, nugetPackageDownloader, dotnetPath); } public override int Execute() diff --git a/src/Cli/dotnet/commands/dotnet-workload/update/WorkloadUpdateCommand.cs b/src/Cli/dotnet/commands/dotnet-workload/update/WorkloadUpdateCommand.cs index 1272593a16ef..07a69a993db1 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/update/WorkloadUpdateCommand.cs +++ b/src/Cli/dotnet/commands/dotnet-workload/update/WorkloadUpdateCommand.cs @@ -42,7 +42,7 @@ internal class WorkloadUpdateCommand : CommandBase private readonly INuGetPackageDownloader _nugetPackageDownloader; private readonly IWorkloadManifestUpdater _workloadManifestUpdater; private readonly ReleaseVersion _sdkVersion; - private readonly string _userHome; + private readonly string _userProfileDir; private readonly string _dotnetPath; private readonly string _tempDirPath; @@ -54,7 +54,7 @@ public WorkloadUpdateCommand( INuGetPackageDownloader nugetPackageDownloader = null, IWorkloadManifestUpdater workloadManifestUpdater = null, string dotnetDir = null, - string userHome = null, + string userProfileDir = null, string tempDirPath = null, string version = null) : base(parseResult) @@ -69,7 +69,8 @@ public WorkloadUpdateCommand( _downloadToCacheOption = parseResult.ValueForOption(WorkloadUpdateCommandParser.DownloadToCacheOption); _verbosity = parseResult.ValueForOption(WorkloadUpdateCommandParser.VerbosityOption); _dotnetPath = dotnetDir ?? Path.GetDirectoryName(Environment.ProcessPath); - _sdkVersion = WorkloadOptionsExtensions.GetValidatedSdkVersion(parseResult.ValueForOption(WorkloadUpdateCommandParser.VersionOption), version, _dotnetPath); + _userProfileDir = userProfileDir ?? CliFolderPathCalculator.DotnetUserProfileFolderPath; + _sdkVersion = WorkloadOptionsExtensions.GetValidatedSdkVersion(parseResult.ValueForOption(WorkloadUpdateCommandParser.VersionOption), version, _dotnetPath, _userProfileDir); _tempDirPath = tempDirPath ?? (string.IsNullOrWhiteSpace(parseResult.ValueForOption(WorkloadUpdateCommandParser.TempDirOption)) ? Path.GetTempPath() : parseResult.ValueForOption(WorkloadUpdateCommandParser.TempDirOption)); @@ -81,20 +82,19 @@ public WorkloadUpdateCommand( _packageSourceLocation = string.IsNullOrEmpty(configOption) && (sourceOption == null || !sourceOption.Any()) ? null : new PackageSourceLocation(string.IsNullOrEmpty(configOption) ? null : new FilePath(configOption), sourceFeedOverrides: sourceOption); - var workloadManifestProvider = new SdkDirectoryWorkloadManifestProvider(_dotnetPath, _sdkVersion.ToString()); - _workloadResolver = workloadResolver ?? WorkloadResolver.Create(workloadManifestProvider, _dotnetPath, _sdkVersion.ToString()); + var workloadManifestProvider = new SdkDirectoryWorkloadManifestProvider(_dotnetPath, _sdkVersion.ToString(), _userProfileDir); + _workloadResolver = workloadResolver ?? WorkloadResolver.Create(workloadManifestProvider, _dotnetPath, _sdkVersion.ToString(), _userProfileDir); var sdkFeatureBand = new SdkFeatureBand(_sdkVersion); var restoreActionConfig = _parseResult.ToRestoreActionConfig(); _workloadInstaller = workloadInstaller ?? WorkloadInstallerFactory.GetWorkloadInstaller(_reporter, - sdkFeatureBand, _workloadResolver, _verbosity, nugetPackageDownloader, + sdkFeatureBand, _workloadResolver, _verbosity, _userProfileDir, nugetPackageDownloader, dotnetDir, _tempDirPath, packageSourceLocation: _packageSourceLocation, restoreActionConfig, elevationRequired: !_printDownloadLinkOnly && string.IsNullOrWhiteSpace(_downloadToCacheOption)); - _userHome = userHome ?? CliFolderPathCalculator.DotnetHomePath; var tempPackagesDir = new DirectoryPath(Path.Combine(_tempDirPath, "dotnet-sdk-advertising-temp")); _nugetPackageDownloader = nugetPackageDownloader ?? new NuGetPackageDownloader(tempPackagesDir, filePermissionSetter: null, new FirstPartyNuGetPackageSigningVerifier(tempPackagesDir, _verbosity.VerbosityIsDetailedOrDiagnostic() ? new NuGetConsoleLogger() : new NullLogger()), _verbosity.VerbosityIsDetailedOrDiagnostic() ? new NuGetConsoleLogger() : new NullLogger(), restoreActionConfig: restoreActionConfig); - _workloadManifestUpdater = workloadManifestUpdater ?? new WorkloadManifestUpdater(_reporter, _workloadResolver, _nugetPackageDownloader, _userHome, _tempDirPath, + _workloadManifestUpdater = workloadManifestUpdater ?? new WorkloadManifestUpdater(_reporter, _workloadResolver, _nugetPackageDownloader, _userProfileDir, _tempDirPath, _workloadInstaller.GetWorkloadInstallationRecordRepository(), _packageSourceLocation); } @@ -241,7 +241,7 @@ private async Task DownloadToOfflineCacheAsync(DirectoryPath offlineCache, bool { await _workloadManifestUpdater.ExtractManifestPackagesToTempDirAsync(manifestPackagePaths, new DirectoryPath(tempManifestDir)); var overlayManifestProvider = new TempDirectoryWorkloadManifestProvider(tempManifestDir, _sdkVersion.ToString()); - _workloadResolver = WorkloadResolver.Create(overlayManifestProvider, _dotnetPath, _sdkVersion.ToString()); + _workloadResolver = WorkloadResolver.Create(overlayManifestProvider, _dotnetPath, _sdkVersion.ToString(), _userProfileDir); if (_workloadInstaller.GetInstallationUnit().Equals(InstallationUnit.Packs)) { @@ -306,7 +306,7 @@ private async Task UseTempManifestsToResolvePacksAsync(DirectoryPath tempPath, b var manifestPackagePaths = await _workloadManifestUpdater.DownloadManifestPackagesAsync(includePreview, tempPath); await _workloadManifestUpdater.ExtractManifestPackagesToTempDirAsync(manifestPackagePaths, tempPath); var overlayManifestProvider = new TempDirectoryWorkloadManifestProvider(tempPath.Value, _sdkVersion.ToString()); - _workloadResolver = WorkloadResolver.Create(overlayManifestProvider, _dotnetPath, _sdkVersion.ToString()); + _workloadResolver = WorkloadResolver.Create(overlayManifestProvider, _dotnetPath, _sdkVersion.ToString(), _userProfileDir); } private IEnumerable GetUpdatableWorkloads() diff --git a/src/Cli/dotnet/dotnet.csproj b/src/Cli/dotnet/dotnet.csproj index 713b797cd209..b27862fb9dcc 100644 --- a/src/Cli/dotnet/dotnet.csproj +++ b/src/Cli/dotnet/dotnet.csproj @@ -18,6 +18,7 @@ + diff --git a/src/Common/CliFolderPathCalculatorCore.cs b/src/Common/CliFolderPathCalculatorCore.cs new file mode 100644 index 000000000000..674dfb2a31df --- /dev/null +++ b/src/Common/CliFolderPathCalculatorCore.cs @@ -0,0 +1,48 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.IO; +using System.Runtime.InteropServices; + +#nullable enable + +namespace Microsoft.DotNet.Configurer +{ + static class CliFolderPathCalculatorCore + { + public const string DotnetHomeVariableName = "DOTNET_CLI_HOME"; + public const string DotnetProfileDirectoryName = ".dotnet"; + + public static string PlatformHomeVariableName => + RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "USERPROFILE" : "HOME"; + + public static string? GetDotnetUserProfileFolderPath() + { + string? homePath = GetDotnetHomePath(); + if (homePath is null) + { + return null; + } + + return Path.Combine(homePath, DotnetProfileDirectoryName); + } + + public static string? GetDotnetHomePath(Func? getEnvironmentVariable = null) + { + getEnvironmentVariable ??= key => Environment.GetEnvironmentVariable(key); + + var home = getEnvironmentVariable(DotnetHomeVariableName); + if (string.IsNullOrEmpty(home)) + { + home = getEnvironmentVariable(PlatformHomeVariableName); + if (string.IsNullOrEmpty(home)) + { + return null; + } + } + + return home; + } + } +} diff --git a/src/Common/WorkloadFileBasedInstall.cs b/src/Common/WorkloadFileBasedInstall.cs new file mode 100644 index 000000000000..f0d82c1fa9a5 --- /dev/null +++ b/src/Common/WorkloadFileBasedInstall.cs @@ -0,0 +1,43 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.IO; + +namespace Microsoft.DotNet.Workloads.Workload +{ + static class WorkloadFileBasedInstall + { + public static bool IsUserLocal(string dotnetDir, string sdkFeatureBand) + => File.Exists(GetUserInstallFilePath(dotnetDir, sdkFeatureBand)); + + internal static void SetUserLocal(string dotnetDir, string sdkFeatureBand) + { + string filePath = GetUserInstallFilePath(dotnetDir, sdkFeatureBand); + + Directory.CreateDirectory(Path.GetDirectoryName(filePath)!); + File.WriteAllText(filePath, ""); + } + + private static string GetUserInstallFilePath(string dotnetDir, string sdkFeatureBand) + { + if (sdkFeatureBand.Contains("-")) + { + // The user passed in the sdk version. Derive the feature band version. + if (!Version.TryParse(sdkFeatureBand.Split('-')[0], out var sdkVersionParsed)) + { + throw new FormatException($"'{nameof(sdkFeatureBand)}' should be a version, but get {sdkFeatureBand}"); + } + + static int Last2DigitsTo0(int versionBuild) + { + return (versionBuild / 100) * 100; + } + + sdkFeatureBand = $"{sdkVersionParsed.Major}.{sdkVersionParsed.Minor}.{Last2DigitsTo0(sdkVersionParsed.Build)}"; + } + + return Path.Combine(dotnetDir, "metadata", "workloads", sdkFeatureBand, "userlocal"); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.DotNet.TemplateLocator/Microsoft.DotNet.TemplateLocator.csproj b/src/Microsoft.DotNet.TemplateLocator/Microsoft.DotNet.TemplateLocator.csproj index 9a22d5c9456e..baf558cdec8a 100644 --- a/src/Microsoft.DotNet.TemplateLocator/Microsoft.DotNet.TemplateLocator.csproj +++ b/src/Microsoft.DotNet.TemplateLocator/Microsoft.DotNet.TemplateLocator.csproj @@ -50,6 +50,7 @@ + diff --git a/src/Microsoft.DotNet.TemplateLocator/TemplateLocator.cs b/src/Microsoft.DotNet.TemplateLocator/TemplateLocator.cs index 03cf6d7cf1da..b0a32fa649db 100644 --- a/src/Microsoft.DotNet.TemplateLocator/TemplateLocator.cs +++ b/src/Microsoft.DotNet.TemplateLocator/TemplateLocator.cs @@ -40,7 +40,8 @@ public TemplateLocator(Func getEnvironmentVariable, VSSettings v public IReadOnlyCollection GetDotnetSdkTemplatePackages( string sdkVersion, - string dotnetRootPath) + string dotnetRootPath, + string? userProfileDir) { if (string.IsNullOrWhiteSpace(sdkVersion)) { @@ -53,8 +54,8 @@ public IReadOnlyCollection GetDotnetSdkTemplate nameof(dotnetRootPath)); } - _workloadManifestProvider ??= new SdkDirectoryWorkloadManifestProvider(dotnetRootPath, sdkVersion); - _workloadResolver ??= WorkloadResolver.Create(_workloadManifestProvider, dotnetRootPath, sdkVersion); + _workloadManifestProvider ??= new SdkDirectoryWorkloadManifestProvider(dotnetRootPath, sdkVersion, userProfileDir); + _workloadResolver ??= WorkloadResolver.Create(_workloadManifestProvider, dotnetRootPath, sdkVersion, userProfileDir); return _workloadResolver.GetInstalledWorkloadPacksOfKind(WorkloadPackKind.Template) .Select(pack => new OptionalSdkTemplatePackageInfo(pack.Id, pack.Version, pack.Path)).ToList(); diff --git a/src/RazorSdk/Tool/Microsoft.NET.Sdk.Razor.Tool.csproj b/src/RazorSdk/Tool/Microsoft.NET.Sdk.Razor.Tool.csproj index feb59557bc48..047441bc3447 100644 --- a/src/RazorSdk/Tool/Microsoft.NET.Sdk.Razor.Tool.csproj +++ b/src/RazorSdk/Tool/Microsoft.NET.Sdk.Razor.Tool.csproj @@ -36,6 +36,10 @@ SkipGetTargetFrameworkProperties="true"/> + + + + diff --git a/src/RazorSdk/Tool/ServerCommand.cs b/src/RazorSdk/Tool/ServerCommand.cs index ebb0783b909e..5e8512a36794 100644 --- a/src/RazorSdk/Tool/ServerCommand.cs +++ b/src/RazorSdk/Tool/ServerCommand.cs @@ -9,6 +9,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; +using Microsoft.DotNet.Configurer; using Microsoft.NET.Sdk.Razor.Tool.CommandLineUtils; namespace Microsoft.NET.Sdk.Razor.Tool @@ -172,9 +173,8 @@ internal virtual string GetPidFilePath(Func getEnvironmentVariab var path = getEnvironmentVariable("DOTNET_BUILD_PIDFILE_DIRECTORY"); if (string.IsNullOrEmpty(path)) { - var homeEnvVariable = PlatformInformation.IsWindows ? "USERPROFILE" : "HOME"; - var homePath = getEnvironmentVariable(homeEnvVariable); - if (string.IsNullOrEmpty(homePath)) + var homePath = CliFolderPathCalculatorCore.GetDotnetHomePath(getEnvironmentVariable); + if (homePath is null) { // Couldn't locate the user profile directory. Bail. return null; diff --git a/src/Resolvers/Microsoft.DotNet.MSBuildSdkResolver/MSBuildSdkResolver.cs b/src/Resolvers/Microsoft.DotNet.MSBuildSdkResolver/MSBuildSdkResolver.cs index 94a44a558a47..08889e6c2890 100644 --- a/src/Resolvers/Microsoft.DotNet.MSBuildSdkResolver/MSBuildSdkResolver.cs +++ b/src/Resolvers/Microsoft.DotNet.MSBuildSdkResolver/MSBuildSdkResolver.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using Microsoft.Build.Framework; +using Microsoft.DotNet.Configurer; using Microsoft.DotNet.DotNetSdkResolver; using Microsoft.DotNet.NativeWrapper; using System; @@ -170,7 +171,8 @@ public override SdkResult Resolve(SdkReference sdkReference, SdkResolverContext }; // First check if requested SDK resolves to a workload SDK pack - var workloadResult = workloadResolver.Resolve(sdkReference.Name, dotnetRoot, netcoreSdkVersion); + string userProfileDir = CliFolderPathCalculatorCore.GetDotnetUserProfileFolderPath(); + var workloadResult = workloadResolver.Resolve(sdkReference.Name, dotnetRoot, netcoreSdkVersion, userProfileDir); if (workloadResult is not CachingWorkloadResolver.NullResolutionResult) { diff --git a/src/Resolvers/Microsoft.DotNet.MSBuildSdkResolver/Microsoft.DotNet.MSBuildSdkResolver.csproj b/src/Resolvers/Microsoft.DotNet.MSBuildSdkResolver/Microsoft.DotNet.MSBuildSdkResolver.csproj index 52571225e855..de9c3d5cd291 100644 --- a/src/Resolvers/Microsoft.DotNet.MSBuildSdkResolver/Microsoft.DotNet.MSBuildSdkResolver.csproj +++ b/src/Resolvers/Microsoft.DotNet.MSBuildSdkResolver/Microsoft.DotNet.MSBuildSdkResolver.csproj @@ -70,6 +70,8 @@ LinkBase="WorkloadInstallRecords" /> + + diff --git a/src/Resolvers/Microsoft.NET.Sdk.WorkloadMSBuildSdkResolver/CachingWorkloadResolver.cs b/src/Resolvers/Microsoft.NET.Sdk.WorkloadMSBuildSdkResolver/CachingWorkloadResolver.cs index 136251301819..75ce81b42cd6 100644 --- a/src/Resolvers/Microsoft.NET.Sdk.WorkloadMSBuildSdkResolver/CachingWorkloadResolver.cs +++ b/src/Resolvers/Microsoft.NET.Sdk.WorkloadMSBuildSdkResolver/CachingWorkloadResolver.cs @@ -173,7 +173,7 @@ private static ResolutionResult Resolve(string sdkReferenceName, IWorkloadManife return new NullResolutionResult(); } - public ResolutionResult Resolve(string sdkReferenceName, string dotnetRootPath, string sdkVersion) + public ResolutionResult Resolve(string sdkReferenceName, string dotnetRootPath, string sdkVersion, string userProfileDir) { if (!_enabled) { @@ -188,8 +188,8 @@ public ResolutionResult Resolve(string sdkReferenceName, string dotnetRootPath, _cachedState.DotnetRootPath != dotnetRootPath || _cachedState.SdkVersion != sdkVersion) { - var workloadManifestProvider = new SdkDirectoryWorkloadManifestProvider(dotnetRootPath, sdkVersion); - var workloadResolver = WorkloadResolver.Create(workloadManifestProvider, dotnetRootPath, sdkVersion); + var workloadManifestProvider = new SdkDirectoryWorkloadManifestProvider(dotnetRootPath, sdkVersion, userProfileDir); + var workloadResolver = WorkloadResolver.Create(workloadManifestProvider, dotnetRootPath, sdkVersion, userProfileDir); _cachedState = new CachedState() { diff --git a/src/Resolvers/Microsoft.NET.Sdk.WorkloadMSBuildSdkResolver/Microsoft.NET.Sdk.WorkloadMSBuildSdkResolver.csproj b/src/Resolvers/Microsoft.NET.Sdk.WorkloadMSBuildSdkResolver/Microsoft.NET.Sdk.WorkloadMSBuildSdkResolver.csproj index 6be21db86107..1e3c98d93e88 100644 --- a/src/Resolvers/Microsoft.NET.Sdk.WorkloadMSBuildSdkResolver/Microsoft.NET.Sdk.WorkloadMSBuildSdkResolver.csproj +++ b/src/Resolvers/Microsoft.NET.Sdk.WorkloadMSBuildSdkResolver/Microsoft.NET.Sdk.WorkloadMSBuildSdkResolver.csproj @@ -23,6 +23,8 @@ + + diff --git a/src/Resolvers/Microsoft.NET.Sdk.WorkloadMSBuildSdkResolver/WorkloadSdkResolver.cs b/src/Resolvers/Microsoft.NET.Sdk.WorkloadMSBuildSdkResolver/WorkloadSdkResolver.cs index 4b87c627252c..51d241f156d6 100644 --- a/src/Resolvers/Microsoft.NET.Sdk.WorkloadMSBuildSdkResolver/WorkloadSdkResolver.cs +++ b/src/Resolvers/Microsoft.NET.Sdk.WorkloadMSBuildSdkResolver/WorkloadSdkResolver.cs @@ -1,13 +1,13 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. - using Microsoft.Build.Framework; using System; using System.Collections.Generic; using System.IO; using System.Linq; using Microsoft.NET.Sdk.WorkloadManifestReader; +using Microsoft.DotNet.Configurer; using Microsoft.DotNet.NativeWrapper; using System.Collections.Immutable; @@ -66,7 +66,8 @@ public override SdkResult Resolve(SdkReference sdkReference, SdkResolverContext resolverContext.State = cachedState; } - var result = cachedState.WorkloadResolver.Resolve(sdkReference.Name, cachedState.DotnetRootPath, cachedState.SdkVersion); + string userProfileDir = CliFolderPathCalculatorCore.GetDotnetUserProfileFolderPath(); + var result = cachedState.WorkloadResolver.Resolve(sdkReference.Name, cachedState.DotnetRootPath, cachedState.SdkVersion, userProfileDir); return result.ToSdkResult(sdkReference, factory); diff --git a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/Microsoft.NET.Sdk.WorkloadManifestReader.csproj b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/Microsoft.NET.Sdk.WorkloadManifestReader.csproj index 3998497c7d7c..0fad2f04f1d4 100644 --- a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/Microsoft.NET.Sdk.WorkloadManifestReader.csproj +++ b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/Microsoft.NET.Sdk.WorkloadManifestReader.csproj @@ -19,6 +19,7 @@ + diff --git a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/SdkDirectoryWorkloadManifestProvider.cs b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/SdkDirectoryWorkloadManifestProvider.cs index 46b6aa5fa2e8..3265bf2e0e6d 100644 --- a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/SdkDirectoryWorkloadManifestProvider.cs +++ b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/SdkDirectoryWorkloadManifestProvider.cs @@ -6,6 +6,7 @@ using System.IO; using System.Linq; using Microsoft.DotNet.Cli; +using Microsoft.DotNet.Workloads.Workload; using Microsoft.NET.Sdk.Localization; namespace Microsoft.NET.Sdk.WorkloadManifestReader @@ -19,13 +20,13 @@ public class SdkDirectoryWorkloadManifestProvider : IWorkloadManifestProvider "microsoft.net.workload.maccatalyst", "microsoft.net.workload.macos", "microsoft.net.workload.tvos" }; private readonly HashSet? _knownManifestIds; - public SdkDirectoryWorkloadManifestProvider(string sdkRootPath, string sdkVersion) - : this(sdkRootPath, sdkVersion, Environment.GetEnvironmentVariable) + public SdkDirectoryWorkloadManifestProvider(string sdkRootPath, string sdkVersion, string? userProfileDir) + : this(sdkRootPath, sdkVersion, Environment.GetEnvironmentVariable, userProfileDir) { } - internal SdkDirectoryWorkloadManifestProvider(string sdkRootPath, string sdkVersion, Func getEnvironmentVariable) + internal SdkDirectoryWorkloadManifestProvider(string sdkRootPath, string sdkVersion, Func getEnvironmentVariable, string? userProfileDir) { if (string.IsNullOrWhiteSpace(sdkVersion)) { @@ -60,7 +61,16 @@ static int Last2DigitsTo0(int versionBuild) _knownManifestIds = File.ReadAllLines(knownManifestIdsFilePath).Where(l => !string.IsNullOrEmpty(l)).ToHashSet(); } - var manifestDirectory = Path.Combine(_sdkRootPath, "sdk-manifests", _sdkVersionBand); + string? userManifestsDir = userProfileDir is null ? null : Path.Combine(userProfileDir, "sdk-manifests", _sdkVersionBand); + string dotnetManifestDir = Path.Combine(_sdkRootPath, "sdk-manifests", _sdkVersionBand); + if (userManifestsDir != null && WorkloadFileBasedInstall.IsUserLocal(_sdkRootPath, _sdkVersionBand) && Directory.Exists(userManifestsDir)) + { + _manifestDirectories = new[] { userManifestsDir, dotnetManifestDir }; + } + else + { + _manifestDirectories = new[] { dotnetManifestDir }; + } var manifestDirectoryEnvironmentVariable = getEnvironmentVariable(EnvironmentVariableNames.WORKLOAD_MANIFEST_ROOTS); if (manifestDirectoryEnvironmentVariable != null) @@ -69,11 +79,7 @@ static int Last2DigitsTo0(int versionBuild) // environment variable settings to be shared by multiple SDKs. _manifestDirectories = manifestDirectoryEnvironmentVariable.Split(Path.PathSeparator) .Select(p => Path.Combine(p, _sdkVersionBand)) - .Append(manifestDirectory).ToArray(); - } - else - { - _manifestDirectories = new[] { manifestDirectory }; + .Concat(_manifestDirectories).ToArray(); } } diff --git a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/WorkloadResolver.cs b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/WorkloadResolver.cs index 7a2f6b916435..d6213de41f59 100644 --- a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/WorkloadResolver.cs +++ b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/WorkloadResolver.cs @@ -6,6 +6,7 @@ using System.IO; using System.Linq; using Microsoft.DotNet.Cli; +using Microsoft.DotNet.Workloads.Workload; using Microsoft.NET.Sdk.Localization; using FXVersion = Microsoft.DotNet.MSBuildSdkResolver.FXVersion; @@ -22,34 +23,50 @@ public class WorkloadResolver : IWorkloadResolver private readonly Dictionary _packs = new(); private IWorkloadManifestProvider? _manifestProvider; private string[] _currentRuntimeIdentifiers; - private readonly string[] _dotnetRootPaths; + private readonly (string path, bool installable)[] _dotnetRootPaths; private Func? _fileExistOverride; private Func? _directoryExistOverride; - public static WorkloadResolver Create(IWorkloadManifestProvider manifestProvider, string dotnetRootPath, string sdkVersion) + public static WorkloadResolver Create(IWorkloadManifestProvider manifestProvider, string dotnetRootPath, string sdkVersion, string? userProfileDir) { string runtimeIdentifierChainPath = Path.Combine(dotnetRootPath, "sdk", sdkVersion, "NETCoreSdkRuntimeIdentifierChain.txt"); string[] currentRuntimeIdentifiers = File.Exists(runtimeIdentifierChainPath) ? File.ReadAllLines(runtimeIdentifierChainPath).Where(l => !string.IsNullOrEmpty(l)).ToArray() : new string[] { }; - var packRootEnvironmentVariable = Environment.GetEnvironmentVariable(EnvironmentVariableNames.WORKLOAD_PACK_ROOTS); - - string[] dotnetRootPaths; - if (!string.IsNullOrEmpty(packRootEnvironmentVariable)) + (string path, bool installable)[] workloadRootPaths; + if (userProfileDir != null && WorkloadFileBasedInstall.IsUserLocal(dotnetRootPath, sdkVersion) && Directory.Exists(userProfileDir)) { - dotnetRootPaths = packRootEnvironmentVariable.Split(Path.PathSeparator).Append(dotnetRootPath).ToArray(); + workloadRootPaths = new[] { (userProfileDir, true), (dotnetRootPath, true) }; } else { - dotnetRootPaths = new[] { dotnetRootPath }; + workloadRootPaths = new[] { (dotnetRootPath, true) }; + } + + var packRootEnvironmentVariable = Environment.GetEnvironmentVariable(EnvironmentVariableNames.WORKLOAD_PACK_ROOTS); + if (!string.IsNullOrEmpty(packRootEnvironmentVariable)) + { + workloadRootPaths = packRootEnvironmentVariable.Split(Path.PathSeparator).Select(path => (path, false)).Concat(workloadRootPaths).ToArray(); } - return new WorkloadResolver(manifestProvider, dotnetRootPaths, currentRuntimeIdentifiers); + return new WorkloadResolver(manifestProvider, workloadRootPaths, currentRuntimeIdentifiers); } - public static WorkloadResolver CreateForTests(IWorkloadManifestProvider manifestProvider, string[] dotNetRootPaths, string[]? currentRuntimeIdentifiers = null) + public static WorkloadResolver CreateForTests(IWorkloadManifestProvider manifestProvider, string dotNetRoot, bool userLocal = false, string? userProfileDir = null, string[]? currentRuntimeIdentifiers = null) + { + if (userLocal && userProfileDir is null) + { + throw new ArgumentNullException(nameof(userProfileDir)); + } + (string path, bool installable)[] dotNetRootPaths = userLocal + ? new[] { (userProfileDir!, true), (dotNetRoot, true) } + : new[] { (dotNetRoot, true) }; + return CreateForTests(manifestProvider, dotNetRootPaths, currentRuntimeIdentifiers); + } + + public static WorkloadResolver CreateForTests(IWorkloadManifestProvider manifestProvider, (string path, bool installable)[] dotNetRootPaths, string[]? currentRuntimeIdentifiers = null) { if (currentRuntimeIdentifiers == null) { @@ -61,7 +78,7 @@ public static WorkloadResolver CreateForTests(IWorkloadManifestProvider manifest /// /// Creates a resolver by composing all the manifests from the provider. /// - private WorkloadResolver(IWorkloadManifestProvider manifestProvider, string[] dotnetRootPaths, string[] currentRuntimeIdentifiers) + private WorkloadResolver(IWorkloadManifestProvider manifestProvider, (string path, bool installable)[] dotnetRootPaths, string[] currentRuntimeIdentifiers) : this(dotnetRootPaths, currentRuntimeIdentifiers) { _manifestProvider = manifestProvider; @@ -73,7 +90,7 @@ private WorkloadResolver(IWorkloadManifestProvider manifestProvider, string[] do /// /// Creates a resolver with no manifests. /// A - private WorkloadResolver(string[] dotnetRootPaths, string[] currentRuntimeIdentifiers) + private WorkloadResolver((string path, bool installable)[] dotnetRootPaths, string[] currentRuntimeIdentifiers) { _dotnetRootPaths = dotnetRootPaths; _currentRuntimeIdentifiers = currentRuntimeIdentifiers; @@ -283,33 +300,40 @@ internal void ReplaceFilesystemChecksForTest(Func fileExists, Func string GetPackPath(WorkloadPackId resolvedPackageId, string packageVersion, WorkloadPackKind kind, out bool isInstalled) { isInstalled = false; - string packPath = ""; - bool isFile; + string? firstInstallablePackPath = null; + string? installedPackPath = null; foreach (var rootPath in _dotnetRootPaths) { + string packPath; + bool isFile; switch (kind) { case WorkloadPackKind.Framework: case WorkloadPackKind.Sdk: - packPath = Path.Combine(rootPath, "packs", resolvedPackageId.ToString(), packageVersion); + packPath = Path.Combine(rootPath.path, "packs", resolvedPackageId.ToString(), packageVersion); isFile = false; break; case WorkloadPackKind.Template: - packPath = Path.Combine(rootPath, "template-packs", resolvedPackageId.GetNuGetCanonicalId() + "." + packageVersion.ToLowerInvariant() + ".nupkg"); + packPath = Path.Combine(rootPath.path, "template-packs", resolvedPackageId.GetNuGetCanonicalId() + "." + packageVersion.ToLowerInvariant() + ".nupkg"); isFile = true; break; case WorkloadPackKind.Library: - packPath = Path.Combine(rootPath, "library-packs", resolvedPackageId.GetNuGetCanonicalId() + "." + packageVersion.ToLowerInvariant() + ".nupkg"); + packPath = Path.Combine(rootPath.path, "library-packs", resolvedPackageId.GetNuGetCanonicalId() + "." + packageVersion.ToLowerInvariant() + ".nupkg"); isFile = true; break; case WorkloadPackKind.Tool: - packPath = Path.Combine(rootPath, "tool-packs", resolvedPackageId.ToString(), packageVersion); + packPath = Path.Combine(rootPath.path, "tool-packs", resolvedPackageId.ToString(), packageVersion); isFile = false; break; default: throw new ArgumentException($"The package kind '{kind}' is not known", nameof(kind)); } + if (rootPath.installable && firstInstallablePackPath is null) + { + firstInstallablePackPath = packPath; + } + //can we do a more robust check than directory.exists? isInstalled = isFile ? _fileExistOverride?.Invoke(packPath) ?? File.Exists(packPath) : @@ -317,10 +341,11 @@ string GetPackPath(WorkloadPackId resolvedPackageId, string packageVersion, Work if (isInstalled) { + installedPackPath = packPath; break; } } - return packPath; + return installedPackPath ?? firstInstallablePackPath ?? ""; } } diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/Microsoft.NET.Build.Tasks.csproj b/src/Tasks/Microsoft.NET.Build.Tasks/Microsoft.NET.Build.Tasks.csproj index f5543e055924..3032bc666003 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/Microsoft.NET.Build.Tasks.csproj +++ b/src/Tasks/Microsoft.NET.Build.Tasks/Microsoft.NET.Build.Tasks.csproj @@ -80,6 +80,8 @@ + + diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ProcessFrameworkReferences.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ProcessFrameworkReferences.cs index 6de9e34c8bf2..ca87e30899ac 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ProcessFrameworkReferences.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ProcessFrameworkReferences.cs @@ -10,6 +10,8 @@ using Microsoft.Build.Framework; using Microsoft.Build.Utilities; using Microsoft.DotNet.Cli; +using Microsoft.DotNet.Configurer; +using Microsoft.DotNet.Workloads.Workload; using Microsoft.NET.Sdk.WorkloadManifestReader; using Newtonsoft.Json; using NuGet.Frameworks; @@ -634,10 +636,6 @@ private string GetPackPath(string packName, string packVersion) { IEnumerable GetPackFolders() { - if (!string.IsNullOrEmpty(TargetingPackRoot)) - { - yield return TargetingPackRoot; - } var packRootEnvironmentVariable = Environment.GetEnvironmentVariable(EnvironmentVariableNames.WORKLOAD_PACK_ROOTS); if (!string.IsNullOrEmpty(packRootEnvironmentVariable)) { @@ -646,6 +644,20 @@ IEnumerable GetPackFolders() yield return Path.Combine(packRoot, "packs"); } } + + if (!string.IsNullOrEmpty(NetCoreRoot) && !string.IsNullOrEmpty(NETCoreSdkVersion)) + { + if (WorkloadFileBasedInstall.IsUserLocal(NetCoreRoot, NETCoreSdkVersion) && + CliFolderPathCalculatorCore.GetDotnetUserProfileFolderPath() is { } userProfileDir) + { + yield return Path.Combine(userProfileDir, "packs"); + } + } + + if (!string.IsNullOrEmpty(TargetingPackRoot)) + { + yield return TargetingPackRoot; + } } foreach (var packFolder in GetPackFolders()) @@ -672,8 +684,9 @@ private string GetResolvedPackVersion(string packID, string packVersion) if (_workloadManifestProvider == null) { - _workloadManifestProvider = new SdkDirectoryWorkloadManifestProvider(NetCoreRoot, NETCoreSdkVersion); - _workloadResolver = WorkloadResolver.Create(_workloadManifestProvider, NetCoreRoot, NETCoreSdkVersion); + string userProfileDir = CliFolderPathCalculatorCore.GetDotnetUserProfileFolderPath(); + _workloadManifestProvider = new SdkDirectoryWorkloadManifestProvider(NetCoreRoot, NETCoreSdkVersion, userProfileDir); + _workloadResolver = WorkloadResolver.Create(_workloadManifestProvider, NetCoreRoot, NETCoreSdkVersion, userProfileDir); } var packInfo = _workloadResolver.TryGetPackInfo(new WorkloadPackId(packID)); diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ShowMissingWorkloads.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ShowMissingWorkloads.cs index 950046e1307d..fcc6a4ec9202 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ShowMissingWorkloads.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ShowMissingWorkloads.cs @@ -10,6 +10,7 @@ using System.Threading.Tasks; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; +using Microsoft.DotNet.Configurer; using Microsoft.NET.Sdk.WorkloadManifestReader; using static Microsoft.NET.Sdk.WorkloadManifestReader.WorkloadResolver; @@ -37,8 +38,9 @@ protected override void ExecuteCore() { if (MissingWorkloadPacks.Any()) { - var workloadManifestProvider = new SdkDirectoryWorkloadManifestProvider(NetCoreRoot, NETCoreSdkVersion); - var workloadResolver = WorkloadResolver.Create(workloadManifestProvider, NetCoreRoot, NETCoreSdkVersion); + string? userProfileDir = CliFolderPathCalculatorCore.GetDotnetUserProfileFolderPath(); + var workloadManifestProvider = new SdkDirectoryWorkloadManifestProvider(NetCoreRoot, NETCoreSdkVersion, userProfileDir); + var workloadResolver = WorkloadResolver.Create(workloadManifestProvider, NetCoreRoot, NETCoreSdkVersion, userProfileDir); var suggestedWorkloads = workloadResolver.GetWorkloadSuggestionForMissingPacks( MissingWorkloadPacks.Select(item => new WorkloadPackId (item.ItemSpec)).ToList(), diff --git a/src/Tests/Microsoft.DotNet.TemplateLocator.Tests/GivenAnTemplateLocator.cs b/src/Tests/Microsoft.DotNet.TemplateLocator.Tests/GivenAnTemplateLocator.cs index 260fa23c4173..6ddb438444dc 100644 --- a/src/Tests/Microsoft.DotNet.TemplateLocator.Tests/GivenAnTemplateLocator.cs +++ b/src/Tests/Microsoft.DotNet.TemplateLocator.Tests/GivenAnTemplateLocator.cs @@ -47,7 +47,7 @@ public void ItShouldReturnListOfTemplates() string templateNupkgPath = Path.Combine(templatePacksDirectory, "xamarin.android.templates.1.0.3.nupkg"); File.WriteAllText(templateNupkgPath, ""); - var result = _resolver.GetDotnetSdkTemplatePackages("5.0.102", _fakeDotnetRootDirectory); + var result = _resolver.GetDotnetSdkTemplatePackages("5.0.102", _fakeDotnetRootDirectory, userProfileDir: null); result.First().Path.Should().Be(templateNupkgPath); result.First().TemplatePackageId.Should().Be("Xamarin.Android.Templates"); @@ -63,7 +63,7 @@ public void GivenNoSdkToBondItShouldReturnEmpty() File.Copy(Path.Combine("Manifests", "AndroidWorkloadManifest.json"), Path.Combine(_manifestDirectory, "Android", "WorkloadManifest.json")); - var result = _resolver.GetDotnetSdkTemplatePackages("5.1.100", _fakeDotnetRootDirectory); + var result = _resolver.GetDotnetSdkTemplatePackages("5.1.100", _fakeDotnetRootDirectory, userProfileDir: null); result.Should().BeEmpty(); } @@ -72,7 +72,7 @@ public void GivenNoManifestDirectoryItShouldReturnEmpty() { var fakeDotnetRootDirectory = Path.Combine(TestContext.Current.TestExecutionDirectory, Path.GetRandomFileName()); - var result = _resolver.GetDotnetSdkTemplatePackages("5.0.102", fakeDotnetRootDirectory); + var result = _resolver.GetDotnetSdkTemplatePackages("5.0.102", fakeDotnetRootDirectory, userProfileDir: null); result.Should().BeEmpty(); } } diff --git a/src/Tests/Microsoft.NET.Build.Tests/GivenThatWeWantToControlGeneratedAssemblyInfo.cs b/src/Tests/Microsoft.NET.Build.Tests/GivenThatWeWantToControlGeneratedAssemblyInfo.cs index c196f3339024..0358bd0f1cf2 100644 --- a/src/Tests/Microsoft.NET.Build.Tests/GivenThatWeWantToControlGeneratedAssemblyInfo.cs +++ b/src/Tests/Microsoft.NET.Build.Tests/GivenThatWeWantToControlGeneratedAssemblyInfo.cs @@ -386,7 +386,7 @@ private static string LatestTargetFramework .WithWorkingDirectory(path) .Execute("new", "console"); - string projectFile = Path.Combine(path, "LatestTargetFramework.csproj"); + string projectFile = Path.Combine(path, $"{Path.GetFileName(path)}.csproj"); XDocument projectXml = XDocument.Load(projectFile); XNamespace ns = projectXml.Root.Name.Namespace; _cachedLatestTargetFramework = projectXml.Root.Element(ns + "PropertyGroup").Element(ns + "TargetFramework").Value; diff --git a/src/Tests/Microsoft.NET.Sdk.WorkloadManifestReader.Tests/ManifestReaderFunctionalTests.cs b/src/Tests/Microsoft.NET.Sdk.WorkloadManifestReader.Tests/ManifestReaderFunctionalTests.cs index ce7da0d9bc87..1c12e9055aec 100644 --- a/src/Tests/Microsoft.NET.Sdk.WorkloadManifestReader.Tests/ManifestReaderFunctionalTests.cs +++ b/src/Tests/Microsoft.NET.Sdk.WorkloadManifestReader.Tests/ManifestReaderFunctionalTests.cs @@ -60,7 +60,7 @@ private WorkloadResolver SetUp() { var workloadResolver = WorkloadResolver.CreateForTests(new FakeManifestProvider(new[] { ManifestPath }), - new[] { "fakepath" }); + "fakepath"); workloadResolver.ReplaceFilesystemChecksForTest(fileExists: (_) => true, directoryExists: (_) => true); return workloadResolver; @@ -71,7 +71,7 @@ public void GivenTemplateNupkgDoesNotExistOnDiskItShouldReturnEmpty() { var workloadResolver = WorkloadResolver.CreateForTests(new FakeManifestProvider(new[] { ManifestPath }), - new[] { "fakepath" }); + "fakepath"); workloadResolver.ReplaceFilesystemChecksForTest(fileExists: (_) => false, directoryExists: (_) => true); var result = workloadResolver.GetInstalledWorkloadPacksOfKind(WorkloadPackKind.Template); result.Should().HaveCount(0); @@ -82,7 +82,7 @@ public void GivenWorkloadSDKsDirectoryNotExistOnDiskItShouldReturnEmpty() { var workloadResolver = WorkloadResolver.CreateForTests(new FakeManifestProvider(new[] { ManifestPath }), - new[] { "fakepath" }); + "fakepath"); workloadResolver.ReplaceFilesystemChecksForTest(fileExists: (_) => true, directoryExists: (_) => false); var result = workloadResolver.GetInstalledWorkloadPacksOfKind(WorkloadPackKind.Sdk); result.Should().HaveCount(0); @@ -99,7 +99,7 @@ public void ItCanReadIntegerVersion() }"); var workloadResolver = - WorkloadResolver.CreateForTests(new FakeManifestProvider(manifestPath), new[] { "fakepath" }); + WorkloadResolver.CreateForTests(new FakeManifestProvider(manifestPath), "fakepath"); workloadResolver.ReplaceFilesystemChecksForTest(fileExists: (_) => true, directoryExists: (_) => true); diff --git a/src/Tests/Microsoft.NET.Sdk.WorkloadManifestReader.Tests/ManifestTests.cs b/src/Tests/Microsoft.NET.Sdk.WorkloadManifestReader.Tests/ManifestTests.cs index 62c0be9a9060..6dc893e114b2 100644 --- a/src/Tests/Microsoft.NET.Sdk.WorkloadManifestReader.Tests/ManifestTests.cs +++ b/src/Tests/Microsoft.NET.Sdk.WorkloadManifestReader.Tests/ManifestTests.cs @@ -51,7 +51,7 @@ public void ItCanDeserialize() public void AliasedPackPath() { var manifestProvider = new FakeManifestProvider(ManifestPath); - var resolver = WorkloadResolver.CreateForTests(manifestProvider, new[] { fakeRootPath }); + var resolver = WorkloadResolver.CreateForTests(manifestProvider, fakeRootPath); resolver.ReplaceFilesystemChecksForTest(_ => true, _ => true); @@ -67,7 +67,7 @@ public void AliasedPackPath() public void UnresolvedAliasedPackPath() { var manifestProvider = new FakeManifestProvider(ManifestPath); - var resolver = WorkloadResolver.CreateForTests(manifestProvider, new[] { fakeRootPath }, new[] { "fake-platform" }); + var resolver = WorkloadResolver.CreateForTests(manifestProvider, fakeRootPath, currentRuntimeIdentifiers: new[] { "fake-platform" }); resolver.ReplaceFilesystemChecksForTest(_ => true, _ => true); @@ -77,7 +77,7 @@ public void UnresolvedAliasedPackPath() } [Fact] - public void GivenMultiplePackRoots_ItUsesTheLastOneIfThePackDoesntExist() + public void GivenMultiplePackRoots_ItUsesTheFirstInstallableIfThePackDoesntExist() { TestMultiplePackRoots(false, false); } @@ -121,7 +121,7 @@ void TestMultiplePackRoots(bool defaultExists, bool additionalExists) } var manifestProvider = new FakeManifestProvider(ManifestPath); - var resolver = WorkloadResolver.CreateForTests(manifestProvider, new[] { additionalRoot, dotnetRoot }); + var resolver = WorkloadResolver.CreateForTests(manifestProvider, new[] { (additionalRoot, false), (dotnetRoot, true), ("other", true) }); var pack = resolver.TryGetPackInfo(new WorkloadPackId("Xamarin.Android.Sdk")); pack.Should().NotBeNull(); @@ -143,7 +143,7 @@ public void GivenNonExistentPackRoot_ItIgnoresIt() Directory.CreateDirectory(defaultPackPath); var manifestProvider = new FakeManifestProvider(ManifestPath); - var resolver = WorkloadResolver.CreateForTests(manifestProvider, new[] { additionalRoot, dotnetRoot }); + var resolver = WorkloadResolver.CreateForTests(manifestProvider, new[] { (additionalRoot, false), (dotnetRoot, true) }); var pack = resolver.TryGetPackInfo(new WorkloadPackId("Xamarin.Android.Sdk")); pack.Should().NotBeNull(); @@ -183,14 +183,14 @@ string MakeManifest(string version, params (string id, string version)[] depends { "DDD", MakeManifest("25.0.0") }, }; - WorkloadResolver.CreateForTests(goodManifestProvider, new[] { fakeRootPath }); + WorkloadResolver.CreateForTests(goodManifestProvider, fakeRootPath); var missingManifestProvider = new InMemoryFakeManifestProvider { { "AAA", MakeManifest("20.0.0", ("BBB", "5.0.0"), ("CCC", "63.0.0"), ("DDD", "25.0.0")) } }; - var missingManifestEx = Assert.Throws(() => WorkloadResolver.CreateForTests(missingManifestProvider, new[] { fakeRootPath })); + var missingManifestEx = Assert.Throws(() => WorkloadResolver.CreateForTests(missingManifestProvider, fakeRootPath)); Assert.StartsWith("Did not find workload manifest dependency 'BBB' required by manifest 'AAA'", missingManifestEx.Message); var inconsistentManifestProvider = new InMemoryFakeManifestProvider @@ -201,7 +201,7 @@ string MakeManifest(string version, params (string id, string version)[] depends { "DDD", MakeManifest("30.0.0") }, }; - var inconsistentManifestEx = Assert.Throws(() => WorkloadResolver.CreateForTests(inconsistentManifestProvider, new[] { fakeRootPath })); + var inconsistentManifestEx = Assert.Throws(() => WorkloadResolver.CreateForTests(inconsistentManifestProvider, fakeRootPath)); Assert.StartsWith("Workload manifest dependency 'DDD' version '39.0.0' is lower than version '30.0.0' required by manifest 'BBB'", inconsistentManifestEx.Message); } diff --git a/src/Tests/Microsoft.NET.Sdk.WorkloadManifestReader.Tests/SdkDirectoryWorkloadManifestProviderTests.cs b/src/Tests/Microsoft.NET.Sdk.WorkloadManifestReader.Tests/SdkDirectoryWorkloadManifestProviderTests.cs index 31eda221580e..2c13309d153f 100644 --- a/src/Tests/Microsoft.NET.Sdk.WorkloadManifestReader.Tests/SdkDirectoryWorkloadManifestProviderTests.cs +++ b/src/Tests/Microsoft.NET.Sdk.WorkloadManifestReader.Tests/SdkDirectoryWorkloadManifestProviderTests.cs @@ -51,7 +51,7 @@ public void ItShouldReturnListOfManifestFiles() File.WriteAllText(Path.Combine(_manifestDirectory, "iOS", "WorkloadManifest.json"), iosManifestFileContent); var sdkDirectoryWorkloadManifestProvider - = new SdkDirectoryWorkloadManifestProvider(sdkRootPath: _fakeDotnetRootDirectory, sdkVersion: "5.0.100"); + = new SdkDirectoryWorkloadManifestProvider(sdkRootPath: _fakeDotnetRootDirectory, sdkVersion: "5.0.100", userProfileDir: null); GetManifestContents(sdkDirectoryWorkloadManifestProvider) .Should() @@ -68,7 +68,7 @@ public void GivenSDKVersionItShouldReturnListOfManifestFilesForThisVersionBand() File.WriteAllText(Path.Combine(_manifestDirectory, "Android", "WorkloadManifest.json"), androidManifestFileContent); var sdkDirectoryWorkloadManifestProvider - = new SdkDirectoryWorkloadManifestProvider(sdkRootPath: _fakeDotnetRootDirectory, sdkVersion: "5.0.105"); + = new SdkDirectoryWorkloadManifestProvider(sdkRootPath: _fakeDotnetRootDirectory, sdkVersion: "5.0.100", userProfileDir: null); GetManifestContents(sdkDirectoryWorkloadManifestProvider) .Should() @@ -81,7 +81,7 @@ public void GivenNoManifestDirectoryItShouldReturnEmpty() Initialize(); var sdkDirectoryWorkloadManifestProvider - = new SdkDirectoryWorkloadManifestProvider(sdkRootPath: _fakeDotnetRootDirectory, sdkVersion: "5.0.105"); + = new SdkDirectoryWorkloadManifestProvider(sdkRootPath: _fakeDotnetRootDirectory, sdkVersion: "5.0.100", userProfileDir: null); sdkDirectoryWorkloadManifestProvider.GetManifests().Should().BeEmpty(); } @@ -93,7 +93,7 @@ public void GivenNoManifestJsonFileInDirectoryItShouldThrow() Directory.CreateDirectory(Path.Combine(_manifestDirectory, "Android")); var sdkDirectoryWorkloadManifestProvider - = new SdkDirectoryWorkloadManifestProvider(sdkRootPath: _fakeDotnetRootDirectory, sdkVersion: "5.0.105"); + = new SdkDirectoryWorkloadManifestProvider(sdkRootPath: _fakeDotnetRootDirectory, sdkVersion: "5.0.100", userProfileDir: null); Action a = () => sdkDirectoryWorkloadManifestProvider.GetManifests().Select(m => { using (m.openManifestStream()) { } @@ -126,7 +126,7 @@ public void ItShouldReturnManifestsFromTestHook() var sdkDirectoryWorkloadManifestProvider - = new SdkDirectoryWorkloadManifestProvider(sdkRootPath: _fakeDotnetRootDirectory, sdkVersion: sdkVersion, environmentMock.GetEnvironmentVariable); + = new SdkDirectoryWorkloadManifestProvider(sdkRootPath: _fakeDotnetRootDirectory, sdkVersion: sdkVersion, environmentMock.GetEnvironmentVariable, userProfileDir: null); GetManifestContents(sdkDirectoryWorkloadManifestProvider) .Should() @@ -155,7 +155,7 @@ public void ManifestFromTestHookShouldOverrideDefault() File.WriteAllText(Path.Combine(_manifestDirectory, "Android", "WorkloadManifest.json"), "OverriddenAndroidContent"); var sdkDirectoryWorkloadManifestProvider - = new SdkDirectoryWorkloadManifestProvider(sdkRootPath: _fakeDotnetRootDirectory, sdkVersion: sdkVersion, environmentMock.GetEnvironmentVariable); + = new SdkDirectoryWorkloadManifestProvider(sdkRootPath: _fakeDotnetRootDirectory, sdkVersion: sdkVersion, environmentMock.GetEnvironmentVariable, userProfileDir: null); GetManifestContents(sdkDirectoryWorkloadManifestProvider) .Should() @@ -198,7 +198,7 @@ public void ItSupportsMultipleTestHookFolders() File.WriteAllText(Path.Combine(additionalManifestDirectory2, sdkVersion, "Test", "WorkloadManifest.json"), "TestContent2"); var sdkDirectoryWorkloadManifestProvider - = new SdkDirectoryWorkloadManifestProvider(sdkRootPath: _fakeDotnetRootDirectory, sdkVersion: sdkVersion, environmentMock.GetEnvironmentVariable); + = new SdkDirectoryWorkloadManifestProvider(sdkRootPath: _fakeDotnetRootDirectory, sdkVersion: sdkVersion, environmentMock.GetEnvironmentVariable, userProfileDir: null); GetManifestContents(sdkDirectoryWorkloadManifestProvider) .Should() @@ -221,7 +221,7 @@ public void IfTestHookFolderDoesNotExistItShouldBeIgnored() File.WriteAllText(Path.Combine(_manifestDirectory, "Android", "WorkloadManifest.json"), "AndroidContent"); var sdkDirectoryWorkloadManifestProvider - = new SdkDirectoryWorkloadManifestProvider(sdkRootPath: _fakeDotnetRootDirectory, sdkVersion: "5.0.100", environmentMock.GetEnvironmentVariable); + = new SdkDirectoryWorkloadManifestProvider(sdkRootPath: _fakeDotnetRootDirectory, sdkVersion: "5.0.100", environmentMock.GetEnvironmentVariable, userProfileDir: null); GetManifestContents(sdkDirectoryWorkloadManifestProvider) .Should() @@ -240,7 +240,7 @@ public void ItShouldIgnoreOutdatedManifestIds() File.WriteAllText(Path.Combine(_manifestDirectory, "Microsoft.NET.Workload.Android", "WorkloadManifest.json"), "iOSContent"); var sdkDirectoryWorkloadManifestProvider - = new SdkDirectoryWorkloadManifestProvider(sdkRootPath: _fakeDotnetRootDirectory, sdkVersion: "5.0.100"); + = new SdkDirectoryWorkloadManifestProvider(sdkRootPath: _fakeDotnetRootDirectory, sdkVersion: "5.0.100", userProfileDir: null); GetManifestContents(sdkDirectoryWorkloadManifestProvider) .Should() @@ -286,7 +286,7 @@ public void ItShouldFallbackWhenFeatureBandHasNoManifests() File.WriteAllText(knownWorkloadsFilePath, "Android\niOS"); var sdkDirectoryWorkloadManifestProvider - = new SdkDirectoryWorkloadManifestProvider(sdkRootPath: fakeDotnetRootDirectory, sdkVersion: "6.0.100"); + = new SdkDirectoryWorkloadManifestProvider(sdkRootPath: fakeDotnetRootDirectory, sdkVersion: "6.0.100", userProfileDir: null); GetManifestContents(sdkDirectoryWorkloadManifestProvider) .Should() diff --git a/src/Tests/Microsoft.NET.Sdk.WorkloadManifestReader.Tests/WorkloadSuggestionFinderTests.cs b/src/Tests/Microsoft.NET.Sdk.WorkloadManifestReader.Tests/WorkloadSuggestionFinderTests.cs index 2952c04c98ae..6a5821813101 100644 --- a/src/Tests/Microsoft.NET.Sdk.WorkloadManifestReader.Tests/WorkloadSuggestionFinderTests.cs +++ b/src/Tests/Microsoft.NET.Sdk.WorkloadManifestReader.Tests/WorkloadSuggestionFinderTests.cs @@ -30,7 +30,7 @@ public WorkloadSuggestionFinderTests(ITestOutputHelper log) : base(log) public void CanSuggestSimpleWorkload() { var manifestProvider = new FakeManifestProvider(ManifestPath); - var resolver = WorkloadResolver.CreateForTests(manifestProvider, new[] { fakeRootPath }); + var resolver = WorkloadResolver.CreateForTests(manifestProvider, fakeRootPath); FakeFileSystemChecksSoThesePackagesAppearInstalled(resolver, "Xamarin.Android.Sdk", "Xamarin.Android.BuildTools"); @@ -48,7 +48,7 @@ public void CanSuggestSimpleWorkload() public void CanSuggestTwoWorkloadsToFulfilTwoRequirements() { var manifestProvider = new FakeManifestProvider(ManifestPath); - var resolver = WorkloadResolver.CreateForTests(manifestProvider, new[] { fakeRootPath }); + var resolver = WorkloadResolver.CreateForTests(manifestProvider, fakeRootPath); FakeFileSystemChecksSoThesePackagesAppearInstalled(resolver, //xamarin-android-build is fully installed @@ -73,7 +73,7 @@ public void CanSuggestTwoWorkloadsToFulfilTwoRequirements() public void CanSuggestWorkloadThatFulfillsTwoRequirements() { var manifestProvider = new FakeManifestProvider(ManifestPath); - var resolver = WorkloadResolver.CreateForTests(manifestProvider, new[] { fakeRootPath }); + var resolver = WorkloadResolver.CreateForTests(manifestProvider, fakeRootPath); FakeFileSystemChecksSoThesePackagesAppearInstalled(resolver, //xamarin-android-build is fully installed diff --git a/src/Tests/dotnet-workload-install.Tests/GivenDotnetWorkloadInstall.cs b/src/Tests/dotnet-workload-install.Tests/GivenDotnetWorkloadInstall.cs index f8fd8296ce97..50790e432aa9 100644 --- a/src/Tests/dotnet-workload-install.Tests/GivenDotnetWorkloadInstall.cs +++ b/src/Tests/dotnet-workload-install.Tests/GivenDotnetWorkloadInstall.cs @@ -10,6 +10,7 @@ using FluentAssertions; using ManifestReaderTests; using Microsoft.DotNet.Cli.NuGetPackageDownloader; +using Microsoft.DotNet.Workloads.Workload; using Microsoft.DotNet.Workloads.Workload.Install; using Microsoft.NET.Sdk.WorkloadManifestReader; using Microsoft.NET.TestFramework; @@ -50,12 +51,14 @@ public void GivenWorkloadInstallItErrorsOnFakeWorkloadName() .HaveStdErrContaining(String.Format(Workloads.Workload.Install.LocalizableStrings.WorkloadNotRecognized, "fake")); } - [Fact] - public void GivenWorkloadInstallItCanInstallPacks() + [Theory] + [InlineData(true)] + [InlineData(false)] + public void GivenWorkloadInstallItCanInstallPacks(bool userLocal) { var mockWorkloadIds = new WorkloadId[] { new WorkloadId("xamarin-android") }; var parseResult = Parser.Instance.Parse(new string[] { "dotnet", "workload", "install", "xamarin-android", "--skip-manifest-update" }); - (_, var installManager, var installer, _, _, _) = GetTestInstallers(parseResult); + (_, var installManager, var installer, _, _, _) = GetTestInstallers(parseResult, userLocal); installManager.InstallWorkloads(mockWorkloadIds, true); @@ -66,12 +69,14 @@ public void GivenWorkloadInstallItCanInstallPacks() installer.InstalledPacks.Where(pack => pack.Id.ToString().Contains("Android")).Count().Should().Be(8); } - [Fact] - public void GivenWorkloadInstallItCanRollBackPackInstallation() + [Theory] + [InlineData(true)] + [InlineData(false)] + public void GivenWorkloadInstallItCanRollBackPackInstallation(bool userLocal) { var mockWorkloadIds = new WorkloadId[] { new WorkloadId("xamarin-android"), new WorkloadId("xamarin-android-build") }; var parseResult = Parser.Instance.Parse(new string[] { "dotnet", "workload", "install", "xamarin-android", "xamarin-android-build", "--skip-manifest-update" }); - (_, var installManager, var installer, var workloadResolver, _, _) = GetTestInstallers(parseResult, failingWorkload: "xamarin-android-build"); + (_, var installManager, var installer, var workloadResolver, _, _) = GetTestInstallers(parseResult, userLocal, failingWorkload: "xamarin-android-build"); var exceptionThrown = Assert.Throws(() => installManager.InstallWorkloads(mockWorkloadIds, true)); exceptionThrown.Message.Should().Be("Failing workload: xamarin-android-build"); @@ -91,7 +96,7 @@ public void GivenWorkloadInstallOnFailingRollbackItDisplaysTopLevelError() var testDirectory = _testAssetsManager.CreateTestDirectory().Path; var dotnetRoot = Path.Combine(testDirectory, "dotnet"); var installer = new MockPackWorkloadInstaller(failingWorkload: "xamarin-android-build", failingRollback: true); - var workloadResolver = WorkloadResolver.CreateForTests(new MockManifestProvider(new[] { _manifestPath }), new string[] { dotnetRoot }); + var workloadResolver = WorkloadResolver.CreateForTests(new MockManifestProvider(new[] { _manifestPath }), dotnetRoot); var parseResult = Parser.Instance.Parse(new string[] { "dotnet", "workload", "install", "xamarin-android", "xamarin-android-build", "--skip-manifest-update" }); var installManager = new WorkloadInstallCommand(parseResult, reporter: _reporter, workloadResolver: workloadResolver, workloadInstaller: installer, version: "6.0.100"); @@ -100,11 +105,13 @@ public void GivenWorkloadInstallOnFailingRollbackItDisplaysTopLevelError() string.Join(" ", _reporter.Lines).Should().Contain("Rollback failure"); } - [Fact] - public void GivenWorkloadInstallItCanUpdateAdvertisingManifests() + [Theory] + [InlineData(true)] + [InlineData(false)] + public void GivenWorkloadInstallItCanUpdateAdvertisingManifests(bool userLocal) { var parseResult = Parser.Instance.Parse(new string[] { "dotnet", "workload", "install", "xamarin-android" }); - (_, var installManager, var installer, _, var manifestUpdater, _) = GetTestInstallers(parseResult); + (_, var installManager, var installer, _, var manifestUpdater, _) = GetTestInstallers(parseResult, userLocal); installManager.InstallWorkloads(new List(), false); // Don't actually do any installs, just update manifests @@ -121,7 +128,7 @@ public void GivenWorkloadInstallItWarnsOnGarbageCollectionFailure() var testDirectory = _testAssetsManager.CreateTestDirectory().Path; var dotnetRoot = Path.Combine(testDirectory, "dotnet"); var installer = new MockPackWorkloadInstaller(failingGarbageCollection: true); - var workloadResolver = WorkloadResolver.CreateForTests(new MockManifestProvider(new[] { _manifestPath }), new string[] { dotnetRoot }); + var workloadResolver = WorkloadResolver.CreateForTests(new MockManifestProvider(new[] { _manifestPath }), dotnetRoot); var parseResult = Parser.Instance.Parse(new string[] { "dotnet", "workload", "install", "xamarin-android", "xamarin-android-build", "--skip-manifest-update" }); var installManager = new WorkloadInstallCommand(parseResult, reporter: _reporter, workloadResolver: workloadResolver, workloadInstaller: installer, version: "6.0.100"); @@ -129,8 +136,10 @@ public void GivenWorkloadInstallItWarnsOnGarbageCollectionFailure() string.Join(" ", _reporter.Lines).Should().Contain("Failing garbage collection"); } - [Fact] - public void GivenWorkloadInstallItCanUpdateInstalledManifests() + [Theory] + [InlineData(true)] + [InlineData(false)] + public void GivenWorkloadInstallItCanUpdateInstalledManifests(bool userLocal) { var parseResult = Parser.Instance.Parse(new string[] {"dotnet", "workload", "install", "xamarin-android"}); @@ -142,7 +151,7 @@ public void GivenWorkloadInstallItCanUpdateInstalledManifests() null), }; (_, var installManager, var installer, _, _, _) = - GetTestInstallers(parseResult, manifestUpdates: manifestsToUpdate); + GetTestInstallers(parseResult, userLocal, manifestUpdates: manifestsToUpdate); installManager.InstallWorkloads(new List(), false); // Don't actually do any installs, just update manifests @@ -152,8 +161,10 @@ public void GivenWorkloadInstallItCanUpdateInstalledManifests() installer.InstalledManifests[0].offlineCache.Should().Be(null); } - [Fact] - public void GivenWorkloadInstallFromCacheItInstallsCachedManifest() + [Theory] + [InlineData(true)] + [InlineData(false)] + public void GivenWorkloadInstallFromCacheItInstallsCachedManifest(bool userLocal) { var manifestsToUpdate = new (ManifestId, ManifestVersion, ManifestVersion, Dictionary @@ -162,13 +173,13 @@ public void GivenWorkloadInstallFromCacheItInstallsCachedManifest() (new ManifestId("mock-manifest"), new ManifestVersion("1.0.0"), new ManifestVersion("2.0.0"), null) }; - var cachePath = Path.Combine(_testAssetsManager.CreateTestDirectory(identifier: "mockCache").Path, + var cachePath = Path.Combine(_testAssetsManager.CreateTestDirectory(identifier: AppendForUserLocal("mockCache_", userLocal)).Path, "mockCachePath"); var parseResult = Parser.Instance.Parse(new string[] { "dotnet", "workload", "install", "xamarin-android", "--from-cache", cachePath }); - (_, var installManager, var installer, _, _, _) = GetTestInstallers(parseResult, + (_, var installManager, var installer, _, _, _) = GetTestInstallers(parseResult, userLocal, tempDirManifestPath: _manifestPath, manifestUpdates: manifestsToUpdate); installManager.Execute(); @@ -179,12 +190,14 @@ public void GivenWorkloadInstallFromCacheItInstallsCachedManifest() installer.InstalledManifests[0].offlineCache.Should().Be(new DirectoryPath(cachePath)); } - [Fact] - public void GivenWorkloadInstallItCanDownloadToOfflineCache() + [Theory] + [InlineData(true)] + [InlineData(false)] + public void GivenWorkloadInstallItCanDownloadToOfflineCache(bool userLocal) { - var cachePath = Path.Combine(_testAssetsManager.CreateTestDirectory(identifier: "mockCache").Path, "mockCachePath"); + var cachePath = Path.Combine(_testAssetsManager.CreateTestDirectory(identifier: AppendForUserLocal("mockCache_", userLocal)).Path, "mockCachePath"); var parseResult = Parser.Instance.Parse(new string[] { "dotnet", "workload", "install", "xamarin-android", "--download-to-cache", cachePath }); - (_, var installManager, var installer, _, var manifestUpdater, _) = GetTestInstallers(parseResult, tempDirManifestPath: _manifestPath); + (_, var installManager, var installer, _, var manifestUpdater, _) = GetTestInstallers(parseResult, userLocal, tempDirManifestPath: _manifestPath); installManager.Execute(); @@ -196,13 +209,15 @@ public void GivenWorkloadInstallItCanDownloadToOfflineCache() installer.CachePath.Should().Be(cachePath); } - [Fact] - public void GivenWorkloadInstallItCanInstallFromOfflineCache() + [Theory] + [InlineData(true)] + [InlineData(false)] + public void GivenWorkloadInstallItCanInstallFromOfflineCache(bool userLocal) { var mockWorkloadIds = new WorkloadId[] { new WorkloadId("xamarin-android") }; var cachePath = "mockCachePath"; var parseResult = Parser.Instance.Parse(new string[] { "dotnet", "workload", "install", "xamarin-android", "--from-cache", cachePath }); - (_, var installManager, var installer, _, _, var nugetDownloader) = GetTestInstallers(parseResult); + (_, var installManager, var installer, _, _, var nugetDownloader) = GetTestInstallers(parseResult, userLocal); installManager.Execute(); @@ -214,11 +229,13 @@ public void GivenWorkloadInstallItCanInstallFromOfflineCache() nugetDownloader.DownloadCallParams.Count().Should().Be(0); } - [Fact] - public void GivenWorkloadInstallItPrintsDownloadUrls() + [Theory] + [InlineData(true)] + [InlineData(false)] + public void GivenWorkloadInstallItPrintsDownloadUrls(bool userLocal) { var parseResult = Parser.Instance.Parse(new string[] { "dotnet", "workload", "install", "xamarin-android", "--print-download-link-only" }); - (_, var installManager, _, _, _, _) = GetTestInstallers(parseResult, tempDirManifestPath: _manifestPath); + (_, var installManager, _, _, _, _) = GetTestInstallers(parseResult, userLocal, tempDirManifestPath: _manifestPath); installManager.Execute(); @@ -235,67 +252,84 @@ public void GivenWorkloadInstallItErrorsOnUnsupportedPlatform() var testDirectory = _testAssetsManager.CreateTestDirectory().Path; var dotnetRoot = Path.Combine(testDirectory, "dotnet"); var installer = new MockPackWorkloadInstaller(); - var workloadResolver = WorkloadResolver.CreateForTests(new MockManifestProvider(new[] { manifestPath }), new string[] { dotnetRoot }); + var workloadResolver = WorkloadResolver.CreateForTests(new MockManifestProvider(new[] { manifestPath }), dotnetRoot); var nugetDownloader = new MockNuGetPackageDownloader(dotnetRoot); var manifestUpdater = new MockWorkloadManifestUpdater(); var parseResult = Parser.Instance.Parse(new string[] { "dotnet", "workload", "install", mockWorkloadId }); var exceptionThrown = Assert.Throws(() => new WorkloadInstallCommand(parseResult, reporter: _reporter, workloadResolver: workloadResolver, workloadInstaller: installer, - nugetPackageDownloader: nugetDownloader, workloadManifestUpdater: manifestUpdater, userHome: testDirectory, dotnetDir: dotnetRoot, version: "6.0.100")); + nugetPackageDownloader: nugetDownloader, workloadManifestUpdater: manifestUpdater, userProfileDir: testDirectory, dotnetDir: dotnetRoot, version: "6.0.100")); exceptionThrown.Message.Should().Be(String.Format(Workloads.Workload.Install.LocalizableStrings.WorkloadNotSupportedOnPlatform, mockWorkloadId)); } - [Fact] - public void GivenWorkloadInstallItDoesNotRemoveOldInstallsOnRollback() + [Theory] + [InlineData(true)] + [InlineData(false)] + public void GivenWorkloadInstallItDoesNotRemoveOldInstallsOnRollback(bool userLocal) { - var testDirectory = _testAssetsManager.CreateTestDirectory().Path; + var testDirectory = _testAssetsManager.CreateTestDirectory(identifier: userLocal ? "userlocal" : "default").Path; var dotnetRoot = Path.Combine(testDirectory, "dotnet"); + var userProfileDir = Path.Combine(testDirectory, "user-profile"); + var tmpDir = Path.Combine(testDirectory, "tmp"); var manifestPath = Path.Combine(_testAssetsManager.GetAndValidateTestProjectDirectory("SampleManifest"), "MockWorkloadsSample.json"); - var workloadResolver = WorkloadResolver.CreateForTests(new MockManifestProvider(new[] { manifestPath }), new string[] { dotnetRoot }); - var nugetDownloader = new FailingNuGetPackageDownloader(dotnetRoot); + var workloadResolver = WorkloadResolver.CreateForTests(new MockManifestProvider(new[] { manifestPath }), dotnetRoot, userLocal, userProfileDir); + var nugetDownloader = new FailingNuGetPackageDownloader(tmpDir); var manifestUpdater = new MockWorkloadManifestUpdater(); var sdkFeatureVersion = "6.0.100"; var existingWorkload = "mock-1"; var installingWorkload = "mock-2"; + if (userLocal) + { + WorkloadFileBasedInstall.SetUserLocal(dotnetRoot, sdkFeatureVersion); + } + // Successfully install a workload var installParseResult = Parser.Instance.Parse(new string[] { "dotnet", "workload", "install", existingWorkload }); - var installCommand = new WorkloadInstallCommand(installParseResult, reporter: _reporter, workloadResolver: workloadResolver, nugetPackageDownloader: new MockNuGetPackageDownloader(dotnetRoot), - workloadManifestUpdater: manifestUpdater, userHome: testDirectory, version: sdkFeatureVersion, dotnetDir: dotnetRoot, tempDirPath: testDirectory); + var installCommand = new WorkloadInstallCommand(installParseResult, reporter: _reporter, workloadResolver: workloadResolver, nugetPackageDownloader: new MockNuGetPackageDownloader(tmpDir), + workloadManifestUpdater: manifestUpdater, userProfileDir: userProfileDir, version: sdkFeatureVersion, dotnetDir: dotnetRoot, tempDirPath: testDirectory); installCommand.Execute(); // Install a workload with a mocked nuget failure installParseResult = Parser.Instance.Parse(new string[] { "dotnet", "workload", "install", installingWorkload }); installCommand = new WorkloadInstallCommand(installParseResult, reporter: _reporter, workloadResolver: workloadResolver, nugetPackageDownloader: nugetDownloader, - workloadManifestUpdater: manifestUpdater, userHome: testDirectory, version: sdkFeatureVersion, dotnetDir: dotnetRoot, tempDirPath: testDirectory); + workloadManifestUpdater: manifestUpdater, userProfileDir: userProfileDir, version: sdkFeatureVersion, dotnetDir: dotnetRoot, tempDirPath: testDirectory); var exceptionThrown = Assert.Throws(() => installCommand.Execute()); exceptionThrown.Message.Should().Contain("Test Failure"); // Existing installation is still present - var installRecordPath = Path.Combine(dotnetRoot, "metadata", "workloads", sdkFeatureVersion, "InstalledWorkloads"); + string installRoot = userLocal ? userProfileDir : dotnetRoot; + var installRecordPath = Path.Combine(installRoot, "metadata", "workloads", sdkFeatureVersion, "InstalledWorkloads"); Directory.GetFiles(installRecordPath).Count().Should().Be(1); File.Exists(Path.Combine(installRecordPath, existingWorkload)) .Should().BeTrue(); - var packRecordDirs = Directory.GetDirectories(Path.Combine(dotnetRoot, "metadata", "workloads", "InstalledPacks", "v1")); + var packRecordDirs = Directory.GetDirectories(Path.Combine(installRoot, "metadata", "workloads", "InstalledPacks", "v1")); packRecordDirs.Count().Should().Be(3); - var installPacks = Directory.GetDirectories(Path.Combine(dotnetRoot, "packs")); + var installPacks = Directory.GetDirectories(Path.Combine(installRoot, "packs")); installPacks.Count().Should().Be(3); } private (string, WorkloadInstallCommand, MockPackWorkloadInstaller, IWorkloadResolver, MockWorkloadManifestUpdater, MockNuGetPackageDownloader) GetTestInstallers( ParseResult parseResult, + bool userLocal, [CallerMemberName] string testName = "", string failingWorkload = null, IEnumerable<(ManifestId, ManifestVersion, ManifestVersion, Dictionary Workloads)> manifestUpdates = null, string tempDirManifestPath = null) { _reporter.Clear(); - var testDirectory = _testAssetsManager.CreateTestDirectory(testName: testName).Path; + var testDirectory = _testAssetsManager.CreateTestDirectory(testName: testName, identifier: userLocal ? "userlocal" : "default").Path; var dotnetRoot = Path.Combine(testDirectory, "dotnet"); + var userProfileDir = Path.Combine(testDirectory, "user-profile"); var installer = new MockPackWorkloadInstaller(failingWorkload); - var workloadResolver = WorkloadResolver.CreateForTests(new MockManifestProvider(new[] { _manifestPath }), new string[] { dotnetRoot }); + var workloadResolver = WorkloadResolver.CreateForTests(new MockManifestProvider(new[] { _manifestPath }), dotnetRoot); var nugetDownloader = new MockNuGetPackageDownloader(dotnetRoot); var manifestUpdater = new MockWorkloadManifestUpdater(manifestUpdates, tempDirManifestPath); + string sdkVersion = "6.0.100"; + if (userLocal) + { + WorkloadFileBasedInstall.SetUserLocal(dotnetRoot, sdkVersion); + } var installManager = new WorkloadInstallCommand( parseResult, reporter: _reporter, @@ -303,11 +337,21 @@ public void GivenWorkloadInstallItDoesNotRemoveOldInstallsOnRollback() workloadInstaller: installer, nugetPackageDownloader: nugetDownloader, workloadManifestUpdater: manifestUpdater, - userHome: testDirectory, + userProfileDir: userProfileDir, dotnetDir: dotnetRoot, version: "6.0.100"); return (testDirectory, installManager, installer, workloadResolver, manifestUpdater, nugetDownloader); } + + private string AppendForUserLocal(string identifier, bool userLocal) + { + if (!userLocal) + { + return identifier; + } + + return $"{identifier}_userlocal"; + } } } diff --git a/src/Tests/dotnet-workload-install.Tests/GivenNetSdkManagedWorkloadInstall.cs b/src/Tests/dotnet-workload-install.Tests/GivenNetSdkManagedWorkloadInstall.cs index 35e72fa80b20..8a31d1b3b201 100644 --- a/src/Tests/dotnet-workload-install.Tests/GivenNetSdkManagedWorkloadInstall.cs +++ b/src/Tests/dotnet-workload-install.Tests/GivenNetSdkManagedWorkloadInstall.cs @@ -431,9 +431,9 @@ public void GivenManagedInstallItCanErrorsWhenMissingOfflineCache() var testDirectory = _testAssetsManager.CreateTestDirectory(testName, identifier: identifier).Path; var dotnetRoot = Path.Combine(testDirectory, "dotnet"); INuGetPackageDownloader nugetInstaller = failingInstaller ? new FailingNuGetPackageDownloader(testDirectory) : new MockNuGetPackageDownloader(dotnetRoot, manifestDownload); - var workloadResolver = WorkloadResolver.CreateForTests(new MockManifestProvider(new[] { _manifestPath }), new string[] { dotnetRoot }); + var workloadResolver = WorkloadResolver.CreateForTests(new MockManifestProvider(new[] { _manifestPath }), dotnetRoot); var sdkFeatureBand = new SdkFeatureBand("6.0.100"); - return (dotnetRoot, new NetSdkManagedInstaller(_reporter, sdkFeatureBand, workloadResolver, nugetInstaller, dotnetRoot, packageSourceLocation: packageSourceLocation), nugetInstaller); + return (dotnetRoot, new NetSdkManagedInstaller(_reporter, sdkFeatureBand, workloadResolver, userProfileDir: testDirectory, nugetInstaller, dotnetRoot, packageSourceLocation: packageSourceLocation), nugetInstaller); } } } diff --git a/src/Tests/dotnet-workload-install.Tests/GivenWorkloadManifestUpdater.cs b/src/Tests/dotnet-workload-install.Tests/GivenWorkloadManifestUpdater.cs index 0b371dc4daa8..f6bb23a1563a 100644 --- a/src/Tests/dotnet-workload-install.Tests/GivenWorkloadManifestUpdater.cs +++ b/src/Tests/dotnet-workload-install.Tests/GivenWorkloadManifestUpdater.cs @@ -51,22 +51,22 @@ public void GivenWorkloadManifestUpdateItCanUpdateAdvertisingManifests() [Fact] public void GivenAdvertisingManifestUpdateItUpdatesWhenNoSentinalExists() { - (var manifestUpdater, var nugetDownloader, var testDir) = GetTestUpdater(); + (var manifestUpdater, var nugetDownloader, var userProfileDir) = GetTestUpdater(); manifestUpdater.BackgroundUpdateAdvertisingManifestsWhenRequiredAsync().Wait(); var expectedDownloadedPackages = _installedManifests .Select(id => ((PackageId, NuGetVersion, DirectoryPath?, PackageSourceLocation))(new PackageId($"{id}.manifest-6.0.100"), null, null, null)); nugetDownloader.DownloadCallParams.Should().BeEquivalentTo(expectedDownloadedPackages); - File.Exists(Path.Combine(testDir, ".dotnet", _manifestSentinalFileName)).Should().BeTrue(); + File.Exists(Path.Combine(userProfileDir, _manifestSentinalFileName)).Should().BeTrue(); } [Fact] public void GivenAdvertisingManifestUpdateItUpdatesWhenDue() { Func getEnvironmentVariable = (envVar) => envVar.Equals(EnvironmentVariableNames.WORKLOAD_UPDATE_NOTIFY_INTERVAL_HOURS) ? "0" : string.Empty; - (var manifestUpdater, var nugetDownloader, var testDir) = GetTestUpdater(getEnvironmentVariable: getEnvironmentVariable); + (var manifestUpdater, var nugetDownloader, var userProfileDir) = GetTestUpdater(getEnvironmentVariable: getEnvironmentVariable); - var sentinalPath = Path.Combine(testDir, ".dotnet", _manifestSentinalFileName); + var sentinalPath = Path.Combine(userProfileDir, _manifestSentinalFileName); File.WriteAllText(sentinalPath, string.Empty); var createTime = DateTime.Now; @@ -82,9 +82,9 @@ public void GivenAdvertisingManifestUpdateItUpdatesWhenDue() [Fact] public void GivenAdvertisingManifestUpdateItDoesNotUpdateWhenNotDue() { - (var manifestUpdater, var nugetDownloader, var testDir) = GetTestUpdater(); + (var manifestUpdater, var nugetDownloader, var userProfileDir) = GetTestUpdater(); - var sentinalPath = Path.Combine(testDir, ".dotnet", _manifestSentinalFileName); + var sentinalPath = Path.Combine(userProfileDir, _manifestSentinalFileName); File.Create(sentinalPath); var createTime = DateTime.Now; @@ -140,9 +140,9 @@ public void GivenWorkloadManifestUpdateItCanCalculateUpdates() .ToArray(); var workloadManifestProvider = new MockManifestProvider(manifestDirs); var nugetDownloader = new MockNuGetPackageDownloader(dotnetRoot); - var workloadResolver = WorkloadResolver.CreateForTests(workloadManifestProvider, new string[] { dotnetRoot }); + var workloadResolver = WorkloadResolver.CreateForTests(workloadManifestProvider, dotnetRoot); var installationRepo = new MockInstallationRecordRepository(); - var manifestUpdater = new WorkloadManifestUpdater(_reporter, workloadResolver, nugetDownloader, testDir, testDir, installationRepo); + var manifestUpdater = new WorkloadManifestUpdater(_reporter, workloadResolver, nugetDownloader, userProfileDir: Path.Combine(testDir, ".dotnet"), testDir, installationRepo); var manifestUpdates = manifestUpdater.CalculateManifestUpdates().Select( m => (m.manifestId, m.existingVersion,m .newVersion)); manifestUpdates.Should().BeEquivalentTo(expectedManifestUpdates); @@ -178,7 +178,7 @@ public void GivenWorkloadManifestRollbackItCanCalculateUpdates() .ToArray(); var workloadManifestProvider = new MockManifestProvider(manifestDirs); var nugetDownloader = new MockNuGetPackageDownloader(dotnetRoot); - var workloadResolver = WorkloadResolver.CreateForTests(workloadManifestProvider, new string[] { dotnetRoot }); + var workloadResolver = WorkloadResolver.CreateForTests(workloadManifestProvider, dotnetRoot); var installationRepo = new MockInstallationRecordRepository(); var manifestUpdater = new WorkloadManifestUpdater(_reporter, workloadResolver, nugetDownloader, testDir, testDir, installationRepo); @@ -221,7 +221,7 @@ public void GivenFromRollbackDefinitionItErrorsOnInstalledExtraneousManifestId() .ToArray(); var workloadManifestProvider = new MockManifestProvider(manifestDirs); var nugetDownloader = new MockNuGetPackageDownloader(dotnetRoot); - var workloadResolver = WorkloadResolver.CreateForTests(new MockManifestProvider(Array.Empty()), new string[] { dotnetRoot }); + var workloadResolver = WorkloadResolver.CreateForTests(new MockManifestProvider(Array.Empty()), dotnetRoot); var installationRepo = new MockInstallationRecordRepository(); var manifestUpdater = new WorkloadManifestUpdater(_reporter, workloadResolver, nugetDownloader, testDir, testDir, installationRepo); @@ -263,7 +263,7 @@ public void GivenFromRollbackDefinitionItErrorsOnExtraneousManifestIdInRollbackD .ToArray(); var workloadManifestProvider = new MockManifestProvider(manifestDirs); var nugetDownloader = new MockNuGetPackageDownloader(dotnetRoot); - var workloadResolver = WorkloadResolver.CreateForTests(new MockManifestProvider(Array.Empty()), new string[] { dotnetRoot }); + var workloadResolver = WorkloadResolver.CreateForTests(new MockManifestProvider(Array.Empty()), dotnetRoot); var installationRepo = new MockInstallationRecordRepository(); var manifestUpdater = new WorkloadManifestUpdater(_reporter, workloadResolver, nugetDownloader, testDir, testDir, installationRepo); @@ -294,7 +294,7 @@ public void GivenWorkloadManifestUpdateItChoosesHighestManifestVersionInCache() var workloadManifestProvider = new MockManifestProvider(new string[] { Path.Combine(installedManifestDir, manifestId, _manifestFileName) }); var nugetDownloader = new MockNuGetPackageDownloader(dotnetRoot); - var workloadResolver = WorkloadResolver.CreateForTests(workloadManifestProvider, new string[] { dotnetRoot }); + var workloadResolver = WorkloadResolver.CreateForTests(workloadManifestProvider, dotnetRoot); var installationRepo = new MockInstallationRecordRepository(); var manifestUpdater = new WorkloadManifestUpdater(_reporter, workloadResolver, nugetDownloader, testDir, testDir, installationRepo); manifestUpdater.UpdateAdvertisingManifestsAsync(false, new DirectoryPath(offlineCache)).Wait(); @@ -351,7 +351,7 @@ public void GivenWorkloadsAreOutOfDateUpdatesAreAdvertisedOnRestoringCommands(st .Select(manifest => Path.Combine(installedManifestDir, manifest.ToString(), _manifestFileName)) .ToArray(); var workloadManifestProvider = new MockManifestProvider(manifestDirs); - var workloadResolver = WorkloadResolver.CreateForTests(workloadManifestProvider, new string[] { dotnetRoot }); + var workloadResolver = WorkloadResolver.CreateForTests(workloadManifestProvider, dotnetRoot); var nugetDownloader = new MockNuGetPackageDownloader(dotnetRoot, manifestDownload: true); var installationRepo = new MockInstallationRecordRepository(); var manifestUpdater = new WorkloadManifestUpdater(_reporter, workloadResolver, nugetDownloader, testDir, testDir, installationRepo, getEnvironmentVariable: getEnvironmentVariable); diff --git a/src/Tests/dotnet-workload-list.Tests/GivenDotnetWorkloadList.cs b/src/Tests/dotnet-workload-list.Tests/GivenDotnetWorkloadList.cs index 8a25958e5c13..c02c63c735fa 100644 --- a/src/Tests/dotnet-workload-list.Tests/GivenDotnetWorkloadList.cs +++ b/src/Tests/dotnet-workload-list.Tests/GivenDotnetWorkloadList.cs @@ -64,7 +64,7 @@ public void GivenNoWorkloadsAreInstalledListIsNotEmpty() _reporter.Clear(); var expectedWorkloads = new List() { new WorkloadId("mock-workload-1"), new WorkloadId("mock-workload-2"), new WorkloadId("mock-workload-3") }; var workloadInstaller = new MockWorkloadRecordRepo(expectedWorkloads); - var workloadResolver = WorkloadResolver.CreateForTests(new MockManifestProvider(new[] { _manifestPath }), new string[] { Directory.GetCurrentDirectory() }); + var workloadResolver = WorkloadResolver.CreateForTests(new MockManifestProvider(new[] { _manifestPath }), Directory.GetCurrentDirectory()); var command = new WorkloadListCommand(_parseResult, _reporter, workloadInstaller, "6.0.100", workloadResolver: workloadResolver); command.Execute(); @@ -93,15 +93,15 @@ public void GivenWorkloadsAreOutOfDateUpdatesAreAdvertised() var testDirectory = _testAssetsManager.CreateTestDirectory().Path; var expectedWorkloads = new List() { new WorkloadId("mock-workload-1"), new WorkloadId("mock-workload-2"), new WorkloadId("mock-workload-3") }; var workloadInstaller = new MockWorkloadRecordRepo(expectedWorkloads); - var workloadResolver = WorkloadResolver.CreateForTests(new MockManifestProvider(new[] { _manifestPath }), new string[] { testDirectory }); + var workloadResolver = WorkloadResolver.CreateForTests(new MockManifestProvider(new[] { _manifestPath }), testDirectory); // Lay out fake advertising manifests with pack version update for pack A (in workloads 1 and 3) - var userHome = Path.Combine(testDirectory, "userHome"); - var manifestPath = Path.Combine(userHome, ".dotnet", "sdk-advertising", "6.0.100", "SampleManifest", "WorkloadManifest.json"); + var userProfileDir = Path.Combine(testDirectory, "user-profile"); + var manifestPath = Path.Combine(userProfileDir, "sdk-advertising", "6.0.100", "SampleManifest", "WorkloadManifest.json"); Directory.CreateDirectory(Path.GetDirectoryName(manifestPath)); File.Copy(Path.Combine(_testAssetsManager.GetAndValidateTestProjectDirectory("SampleManifest"), "MockListSampleUpdated.json"), manifestPath); - var command = new WorkloadListCommand(_parseResult, _reporter, workloadInstaller, "6.0.100", workloadResolver: workloadResolver, userHome: userHome); + var command = new WorkloadListCommand(_parseResult, _reporter, workloadInstaller, "6.0.100", workloadResolver: workloadResolver, userProfileDir: userProfileDir); command.Execute(); // Workloads 1 and 3 should have updates diff --git a/src/Tests/dotnet-workload-list.Tests/GivenWorkloadInstallerAndWorkloadsInstalled.cs b/src/Tests/dotnet-workload-list.Tests/GivenWorkloadInstallerAndWorkloadsInstalled.cs index 1ff884358465..3dd57df289cc 100644 --- a/src/Tests/dotnet-workload-list.Tests/GivenWorkloadInstallerAndWorkloadsInstalled.cs +++ b/src/Tests/dotnet-workload-list.Tests/GivenWorkloadInstallerAndWorkloadsInstalled.cs @@ -96,7 +96,7 @@ private void Setup(string identifier) _reporter, nugetPackageDownloader: _nugetDownloader, workloadManifestUpdater: new MockWorkloadManifestUpdater(_mockManifestUpdates), - userHome: _testDirectory, + userProfileDir: _testDirectory, currentSdkVersion: CurrentSdkVersion, dotnetDir: _dotnetRoot, workloadRecordRepo: new MockMatchingFeatureBandInstallationRecordRepository()); @@ -135,7 +135,7 @@ public void GivenLowerTargetVersionItShouldThrow() _reporter, nugetPackageDownloader: _nugetDownloader, workloadManifestUpdater: new MockWorkloadManifestUpdater(_mockManifestUpdates), - userHome: _testDirectory, + userProfileDir: _testDirectory, currentSdkVersion: CurrentSdkVersion, dotnetDir: _dotnetRoot, workloadRecordRepo: new MockMatchingFeatureBandInstallationRecordRepository()); @@ -155,7 +155,7 @@ public void GivenSameLowerTargetVersionBandItShouldNotThrow() _reporter, nugetPackageDownloader: _nugetDownloader, workloadManifestUpdater: new MockWorkloadManifestUpdater(_mockManifestUpdates), - userHome: _testDirectory, + userProfileDir: _testDirectory, currentSdkVersion: "6.0.101", dotnetDir: _dotnetRoot, workloadRecordRepo: new MockMatchingFeatureBandInstallationRecordRepository()); diff --git a/src/Tests/dotnet-workload-repair.Tests/GivenDotnetWorkloadRepair.cs b/src/Tests/dotnet-workload-repair.Tests/GivenDotnetWorkloadRepair.cs index aa197f961b75..6d23e38386b2 100644 --- a/src/Tests/dotnet-workload-repair.Tests/GivenDotnetWorkloadRepair.cs +++ b/src/Tests/dotnet-workload-repair.Tests/GivenDotnetWorkloadRepair.cs @@ -9,6 +9,7 @@ using System.CommandLine.Parsing; using Microsoft.NET.TestFramework.Utilities; using Microsoft.NET.Sdk.WorkloadManifestReader; +using Microsoft.DotNet.Workloads.Workload; using Microsoft.DotNet.Workloads.Workload.Repair; using System.IO; using ManifestReaderTests; @@ -32,48 +33,65 @@ public GivenDotnetWorkloadRepair(ITestOutputHelper log) : base(log) _manifestPath = Path.Combine(_testAssetsManager.GetAndValidateTestProjectDirectory("SampleManifest"), "Sample.json"); } - [Fact] - public void GivenNoWorkloadsAreInstalledRepairIsNoOp() + [Theory] + [InlineData(true)] + [InlineData(false)] + public void GivenNoWorkloadsAreInstalledRepairIsNoOp(bool userLocal) { _reporter.Clear(); - var testDirectory = _testAssetsManager.CreateTestDirectory().Path; + var testDirectory = _testAssetsManager.CreateTestDirectory(identifier: userLocal ? "userlocal" : "default").Path; var dotnetRoot = Path.Combine(testDirectory, "dotnet"); + var userProfileDir = Path.Combine(testDirectory, "user-profile"); var nugetDownloader = new MockNuGetPackageDownloader(dotnetRoot); - var workloadResolver = WorkloadResolver.CreateForTests(new MockManifestProvider(new[] { _manifestPath }), new string[] { dotnetRoot }); + var workloadResolver = WorkloadResolver.CreateForTests(new MockManifestProvider(new[] { _manifestPath }), dotnetRoot, userLocal, userProfileDir); var sdkFeatureVersion = "6.0.100"; + if (userLocal) + { + WorkloadFileBasedInstall.SetUserLocal(dotnetRoot, sdkFeatureVersion); + } + var repairCommand = new WorkloadRepairCommand(_parseResult, reporter: _reporter, workloadResolver: workloadResolver, - nugetPackageDownloader: nugetDownloader, version: sdkFeatureVersion, dotnetDir: dotnetRoot); + nugetPackageDownloader: nugetDownloader, version: sdkFeatureVersion, dotnetDir: dotnetRoot, userProfileDir: userProfileDir); repairCommand.Execute(); _reporter.Lines.Should().Contain(LocalizableStrings.NoWorkloadsToRepair); } - [Fact] - public void GivenExtraPacksInstalledRepairGarbageCollects() + [Theory] + [InlineData(true)] + [InlineData(false)] + public void GivenExtraPacksInstalledRepairGarbageCollects(bool userLocal) { - var testDirectory = _testAssetsManager.CreateTestDirectory().Path; + var testDirectory = _testAssetsManager.CreateTestDirectory(identifier: userLocal ? "userlocal" : "default").Path; var dotnetRoot = Path.Combine(testDirectory, "dotnet"); - var workloadResolver = WorkloadResolver.CreateForTests(new MockManifestProvider(new[] { _manifestPath }), new string[] { dotnetRoot }); + var userProfileDir = Path.Combine(testDirectory, "user-profile"); + var workloadResolver = WorkloadResolver.CreateForTests(new MockManifestProvider(new[] { _manifestPath }), dotnetRoot, userLocal, userProfileDir); var nugetDownloader = new MockNuGetPackageDownloader(dotnetRoot); var manifestUpdater = new MockWorkloadManifestUpdater(); var sdkFeatureVersion = "6.0.100"; var installingWorkload = "xamarin-android"; + string installRoot = userLocal ? userProfileDir : dotnetRoot; + if (userLocal) + { + WorkloadFileBasedInstall.SetUserLocal(dotnetRoot, sdkFeatureVersion); + } + // Install a workload var installParseResult = Parser.Instance.Parse(new string[] { "dotnet", "workload", "install", installingWorkload }); var installCommand = new WorkloadInstallCommand(installParseResult, reporter: _reporter, workloadResolver: workloadResolver, nugetPackageDownloader: nugetDownloader, - workloadManifestUpdater: manifestUpdater, userHome: testDirectory, version: sdkFeatureVersion, dotnetDir: dotnetRoot, tempDirPath: testDirectory); + workloadManifestUpdater: manifestUpdater, userProfileDir: userProfileDir, version: sdkFeatureVersion, dotnetDir: dotnetRoot, tempDirPath: testDirectory); installCommand.Execute(); // Add extra pack dirs and records - var extraPackRecordPath = Path.Combine(dotnetRoot, "metadata", "workloads", "InstalledPacks", "v1", "Test.Pack.A", "1.0.0", sdkFeatureVersion); + var extraPackRecordPath = Path.Combine(installRoot, "metadata", "workloads", "InstalledPacks", "v1", "Test.Pack.A", "1.0.0", sdkFeatureVersion); Directory.CreateDirectory(Path.GetDirectoryName(extraPackRecordPath)); File.WriteAllText(extraPackRecordPath, string.Empty); - var extraPackPath = Path.Combine(dotnetRoot, "packs", "Test.Pack.A", "1.0.0"); + var extraPackPath = Path.Combine(installRoot, "packs", "Test.Pack.A", "1.0.0"); Directory.CreateDirectory(extraPackPath); - var repairCommand = new WorkloadRepairCommand(_parseResult, reporter: _reporter, workloadResolver: workloadResolver, + var repairCommand = new WorkloadRepairCommand(_parseResult, reporter: _reporter, workloadResolver: workloadResolver, userProfileDir: userProfileDir, nugetPackageDownloader: nugetDownloader, version: sdkFeatureVersion, dotnetDir: dotnetRoot, tempDirPath: testDirectory); repairCommand.Execute(); @@ -83,34 +101,43 @@ public void GivenExtraPacksInstalledRepairGarbageCollects() Directory.Exists(extraPackPath).Should().BeFalse(); // Expected packs are still present - Directory.GetDirectories(Path.Combine(dotnetRoot, "packs")).Length.Should().Be(7); - Directory.GetDirectories(Path.Combine(dotnetRoot, "metadata", "workloads", "InstalledPacks", "v1")).Length.Should().Be(8); + Directory.GetDirectories(Path.Combine(installRoot, "packs")).Length.Should().Be(7); + Directory.GetDirectories(Path.Combine(installRoot, "metadata", "workloads", "InstalledPacks", "v1")).Length.Should().Be(8); } - [Fact] - public void GivenMissingPacksRepairFixesInstall() + [Theory] + [InlineData(true)] + [InlineData(false)] + public void GivenMissingPacksRepairFixesInstall(bool userLocal) { - var testDirectory = _testAssetsManager.CreateTestDirectory().Path; + var testDirectory = _testAssetsManager.CreateTestDirectory(identifier: userLocal ? "userlocal" : "default").Path; var dotnetRoot = Path.Combine(testDirectory, "dotnet"); - var workloadResolver = WorkloadResolver.CreateForTests(new MockManifestProvider(new[] { _manifestPath }), new string[] { dotnetRoot }); + var userProfileDir = Path.Combine(testDirectory, "user-profile"); + var workloadResolver = WorkloadResolver.CreateForTests(new MockManifestProvider(new[] { _manifestPath }), dotnetRoot, userLocal, userProfileDir); var nugetDownloader = new MockNuGetPackageDownloader(dotnetRoot); var manifestUpdater = new MockWorkloadManifestUpdater(); var sdkFeatureVersion = "6.0.100"; var installingWorkload = "xamarin-android"; + string installRoot = userLocal ? userProfileDir : dotnetRoot; + if (userLocal) + { + WorkloadFileBasedInstall.SetUserLocal(dotnetRoot, sdkFeatureVersion); + } + // Install a workload var installParseResult = Parser.Instance.Parse(new string[] { "dotnet", "workload", "install", installingWorkload }); var installCommand = new WorkloadInstallCommand(installParseResult, reporter: _reporter, workloadResolver: workloadResolver, nugetPackageDownloader: nugetDownloader, - workloadManifestUpdater: manifestUpdater, userHome: testDirectory, version: sdkFeatureVersion, dotnetDir: dotnetRoot, tempDirPath: testDirectory); + workloadManifestUpdater: manifestUpdater, userProfileDir: userProfileDir, version: sdkFeatureVersion, dotnetDir: dotnetRoot, tempDirPath: testDirectory); installCommand.Execute(); // Delete pack dirs/ records - var deletedPackRecordPath = Path.Combine(dotnetRoot, "metadata", "workloads", "InstalledPacks", "v1", "Xamarin.Android.Sdk", "8.4.7", sdkFeatureVersion); + var deletedPackRecordPath = Path.Combine(installRoot, "metadata", "workloads", "InstalledPacks", "v1", "Xamarin.Android.Sdk", "8.4.7", sdkFeatureVersion); File.Delete(deletedPackRecordPath); - var deletedPackPath = Path.Combine(dotnetRoot, "packs", "Xamarin.Android.Sdk"); + var deletedPackPath = Path.Combine(installRoot, "packs", "Xamarin.Android.Sdk"); Directory.Delete(deletedPackPath, true); - var repairCommand = new WorkloadRepairCommand(_parseResult, reporter: _reporter, workloadResolver: workloadResolver, + var repairCommand = new WorkloadRepairCommand(_parseResult, reporter: _reporter, workloadResolver: workloadResolver, userProfileDir: userProfileDir, nugetPackageDownloader: nugetDownloader, version: sdkFeatureVersion, dotnetDir: dotnetRoot, tempDirPath: testDirectory); repairCommand.Execute(); @@ -119,8 +146,8 @@ public void GivenMissingPacksRepairFixesInstall() Directory.Exists(deletedPackPath).Should().BeTrue(); // All expected packs are still present - Directory.GetDirectories(Path.Combine(dotnetRoot, "packs")).Length.Should().Be(7); - Directory.GetDirectories(Path.Combine(dotnetRoot, "metadata", "workloads", "InstalledPacks", "v1")).Length.Should().Be(8); + Directory.GetDirectories(Path.Combine(installRoot, "packs")).Length.Should().Be(7); + Directory.GetDirectories(Path.Combine(installRoot, "metadata", "workloads", "InstalledPacks", "v1")).Length.Should().Be(8); } } } diff --git a/src/Tests/dotnet-workload-uninstall.Tests/GivenDotnetWorkloadUninstall.cs b/src/Tests/dotnet-workload-uninstall.Tests/GivenDotnetWorkloadUninstall.cs index 04124718ef47..b4fbe4ab059a 100644 --- a/src/Tests/dotnet-workload-uninstall.Tests/GivenDotnetWorkloadUninstall.cs +++ b/src/Tests/dotnet-workload-uninstall.Tests/GivenDotnetWorkloadUninstall.cs @@ -7,6 +7,7 @@ using FluentAssertions; using ManifestReaderTests; using Microsoft.DotNet.Cli.NuGetPackageDownloader; +using Microsoft.DotNet.Workloads.Workload; using Microsoft.DotNet.Workloads.Workload.Install; using Microsoft.NET.Sdk.WorkloadManifestReader; using Microsoft.NET.TestFramework; @@ -39,92 +40,120 @@ public void GivenWorkloadUninstallItErrorsWhenWorkloadIsNotInstalled() exceptionThrown.Message.Should().Contain("mock-1"); } - [Fact] - public void GivenWorkloadUninstallItCanUninstallWorkload() + [Theory] + [InlineData(true)] + [InlineData(false)] + public void GivenWorkloadUninstallItCanUninstallWorkload(bool userLocal) { - var testDirectory = _testAssetsManager.CreateTestDirectory().Path; + var testDirectory = _testAssetsManager.CreateTestDirectory(identifier: userLocal ? "userlocal" : "default").Path; var dotnetRoot = Path.Combine(testDirectory, "dotnet"); + var userProfileDir = Path.Combine(testDirectory, "user-profile"); var sdkFeatureVersion = "6.0.100"; var installingWorkload = "mock-1"; + string installRoot = userLocal ? userProfileDir : dotnetRoot; + if (userLocal) + { + WorkloadFileBasedInstall.SetUserLocal(dotnetRoot, sdkFeatureVersion); + } + InstallWorkload(installingWorkload, testDirectory, sdkFeatureVersion); // Assert install was successful - var installPacks = Directory.GetDirectories(Path.Combine(dotnetRoot, "packs")); + var installPacks = Directory.GetDirectories(Path.Combine(installRoot, "packs")); installPacks.Count().Should().Be(2); - File.Exists(Path.Combine(dotnetRoot, "metadata", "workloads", sdkFeatureVersion, "InstalledWorkloads", installingWorkload)) + File.Exists(Path.Combine(installRoot, "metadata", "workloads", sdkFeatureVersion, "InstalledWorkloads", installingWorkload)) .Should().BeTrue(); - var packRecordDirs = Directory.GetDirectories(Path.Combine(dotnetRoot, "metadata", "workloads", "InstalledPacks", "v1")); + var packRecordDirs = Directory.GetDirectories(Path.Combine(installRoot, "metadata", "workloads", "InstalledPacks", "v1")); packRecordDirs.Count().Should().Be(3); UninstallWorkload(installingWorkload, testDirectory, sdkFeatureVersion); // Assert uninstall was successful - installPacks = Directory.GetDirectories(Path.Combine(dotnetRoot, "packs")); + installPacks = Directory.GetDirectories(Path.Combine(installRoot, "packs")); installPacks.Count().Should().Be(0); - File.Exists(Path.Combine(dotnetRoot, "metadata", "workloads", sdkFeatureVersion, "InstalledWorkloads", installingWorkload)) + File.Exists(Path.Combine(installRoot, "metadata", "workloads", sdkFeatureVersion, "InstalledWorkloads", installingWorkload)) .Should().BeFalse(); - packRecordDirs = Directory.GetDirectories(Path.Combine(dotnetRoot, "metadata", "workloads", "InstalledPacks", "v1")); + packRecordDirs = Directory.GetDirectories(Path.Combine(installRoot, "metadata", "workloads", "InstalledPacks", "v1")); packRecordDirs.Count().Should().Be(0); } - [Fact] - public void GivenWorkloadUninstallItCanUninstallOnlySpecifiedWorkload() + [Theory] + [InlineData(true)] + [InlineData(false)] + public void GivenWorkloadUninstallItCanUninstallOnlySpecifiedWorkload(bool userLocal) { - var testDirectory = _testAssetsManager.CreateTestDirectory().Path; + var testDirectory = _testAssetsManager.CreateTestDirectory(identifier: userLocal ? "userlocal" : "default").Path; var dotnetRoot = Path.Combine(testDirectory, "dotnet"); + var userProfileDir = Path.Combine(testDirectory, "user-profile"); var sdkFeatureVersion = "6.0.100"; var installedWorkload = "mock-1"; var uninstallingWorkload = "mock-2"; + string installRoot = userLocal ? userProfileDir : dotnetRoot; + if (userLocal) + { + WorkloadFileBasedInstall.SetUserLocal(dotnetRoot, sdkFeatureVersion); + } + InstallWorkload(installedWorkload, testDirectory, sdkFeatureVersion); InstallWorkload(uninstallingWorkload, testDirectory, sdkFeatureVersion); // Assert installs were successful - var installPacks = Directory.GetDirectories(Path.Combine(dotnetRoot, "packs")); + var installPacks = Directory.GetDirectories(Path.Combine(installRoot, "packs")); installPacks.Count().Should().Be(3); - File.Exists(Path.Combine(dotnetRoot, "metadata", "workloads", sdkFeatureVersion, "InstalledWorkloads", installedWorkload)) + File.Exists(Path.Combine(installRoot, "metadata", "workloads", sdkFeatureVersion, "InstalledWorkloads", installedWorkload)) .Should().BeTrue(); - File.Exists(Path.Combine(dotnetRoot, "metadata", "workloads", sdkFeatureVersion, "InstalledWorkloads", uninstallingWorkload)) + File.Exists(Path.Combine(installRoot, "metadata", "workloads", sdkFeatureVersion, "InstalledWorkloads", uninstallingWorkload)) .Should().BeTrue(); - var packRecordDirs = Directory.GetDirectories(Path.Combine(dotnetRoot, "metadata", "workloads", "InstalledPacks", "v1")); + var packRecordDirs = Directory.GetDirectories(Path.Combine(installRoot, "metadata", "workloads", "InstalledPacks", "v1")); packRecordDirs.Count().Should().Be(4); UninstallWorkload(uninstallingWorkload, testDirectory, sdkFeatureVersion); // Assert uninstall was successful, other workload is still installed - installPacks = Directory.GetDirectories(Path.Combine(dotnetRoot, "packs")); + installPacks = Directory.GetDirectories(Path.Combine(installRoot, "packs")); installPacks.Count().Should().Be(2); - File.Exists(Path.Combine(dotnetRoot, "metadata", "workloads", sdkFeatureVersion, "InstalledWorkloads", uninstallingWorkload)) + File.Exists(Path.Combine(installRoot, "metadata", "workloads", sdkFeatureVersion, "InstalledWorkloads", uninstallingWorkload)) .Should().BeFalse(); - File.Exists(Path.Combine(dotnetRoot, "metadata", "workloads", sdkFeatureVersion, "InstalledWorkloads", installedWorkload)) + File.Exists(Path.Combine(installRoot, "metadata", "workloads", sdkFeatureVersion, "InstalledWorkloads", installedWorkload)) .Should().BeTrue(); - packRecordDirs = Directory.GetDirectories(Path.Combine(dotnetRoot, "metadata", "workloads", "InstalledPacks", "v1")); + packRecordDirs = Directory.GetDirectories(Path.Combine(installRoot, "metadata", "workloads", "InstalledPacks", "v1")); packRecordDirs.Count().Should().Be(3); } - [Fact] - public void GivenWorkloadUninstallItCanUninstallOnlySpecifiedFeatureBand() + [Theory] + [InlineData(true)] + [InlineData(false)] + public void GivenWorkloadUninstallItCanUninstallOnlySpecifiedFeatureBand(bool userLocal) { - var testDirectory = _testAssetsManager.CreateTestDirectory().Path; + var testDirectory = _testAssetsManager.CreateTestDirectory(identifier: userLocal ? "userlocal" : "default").Path; var dotnetRoot = Path.Combine(testDirectory, "dotnet"); + var userProfileDir = Path.Combine(testDirectory, "user-profile"); var prevSdkFeatureVersion = "5.0.100"; var sdkFeatureVersion = "6.0.100"; var uninstallingWorkload = "mock-1"; + string installRoot = userLocal ? userProfileDir : dotnetRoot; + if (userLocal) + { + WorkloadFileBasedInstall.SetUserLocal(dotnetRoot, sdkFeatureVersion); + WorkloadFileBasedInstall.SetUserLocal(dotnetRoot, prevSdkFeatureVersion); + } + InstallWorkload(uninstallingWorkload, testDirectory, prevSdkFeatureVersion); InstallWorkload(uninstallingWorkload, testDirectory, sdkFeatureVersion); // Assert installs were successful - var installPacks = Directory.GetDirectories(Path.Combine(dotnetRoot, "packs")); + var installPacks = Directory.GetDirectories(Path.Combine(installRoot, "packs")); installPacks.Count().Should().Be(2); - File.Exists(Path.Combine(dotnetRoot, "metadata", "workloads", prevSdkFeatureVersion, "InstalledWorkloads", uninstallingWorkload)) + File.Exists(Path.Combine(installRoot, "metadata", "workloads", prevSdkFeatureVersion, "InstalledWorkloads", uninstallingWorkload)) .Should().BeTrue(); - File.Exists(Path.Combine(dotnetRoot, "metadata", "workloads", sdkFeatureVersion, "InstalledWorkloads", uninstallingWorkload)) + File.Exists(Path.Combine(installRoot, "metadata", "workloads", sdkFeatureVersion, "InstalledWorkloads", uninstallingWorkload)) .Should().BeTrue(); - var packRecordDirs = Directory.GetDirectories(Path.Combine(dotnetRoot, "metadata", "workloads", "InstalledPacks", "v1")); + var packRecordDirs = Directory.GetDirectories(Path.Combine(installRoot, "metadata", "workloads", "InstalledPacks", "v1")); packRecordDirs.Count().Should().Be(3); - var featureBandMarkerFiles = Directory.GetDirectories(Path.Combine(dotnetRoot, "metadata", "workloads", "InstalledPacks", "v1")) + var featureBandMarkerFiles = Directory.GetDirectories(Path.Combine(installRoot, "metadata", "workloads", "InstalledPacks", "v1")) .SelectMany(packIdDirs => Directory.GetDirectories(packIdDirs)) .SelectMany(packVersionDirs => Directory.GetFiles(packVersionDirs)); featureBandMarkerFiles.Count().Should().Be(6); // 3 packs x 2 feature bands @@ -132,36 +161,40 @@ public void GivenWorkloadUninstallItCanUninstallOnlySpecifiedFeatureBand() UninstallWorkload(uninstallingWorkload, testDirectory, sdkFeatureVersion); // Assert uninstall was successful, other workload is still installed - installPacks = Directory.GetDirectories(Path.Combine(dotnetRoot, "packs")); + installPacks = Directory.GetDirectories(Path.Combine(installRoot, "packs")); installPacks.Count().Should().Be(2); - File.Exists(Path.Combine(dotnetRoot, "metadata", "workloads", sdkFeatureVersion, "InstalledWorkloads", uninstallingWorkload)) + File.Exists(Path.Combine(installRoot, "metadata", "workloads", sdkFeatureVersion, "InstalledWorkloads", uninstallingWorkload)) .Should().BeFalse(); - File.Exists(Path.Combine(dotnetRoot, "metadata", "workloads", prevSdkFeatureVersion, "InstalledWorkloads", uninstallingWorkload)) + File.Exists(Path.Combine(installRoot, "metadata", "workloads", prevSdkFeatureVersion, "InstalledWorkloads", uninstallingWorkload)) .Should().BeTrue(); - packRecordDirs = Directory.GetDirectories(Path.Combine(dotnetRoot, "metadata", "workloads", "InstalledPacks", "v1")); + packRecordDirs = Directory.GetDirectories(Path.Combine(installRoot, "metadata", "workloads", "InstalledPacks", "v1")); packRecordDirs.Count().Should().Be(3); } private void InstallWorkload(string installingWorkload, string testDirectory, string sdkFeatureVersion) { var dotnetRoot = Path.Combine(testDirectory, "dotnet"); - var workloadResolver = WorkloadResolver.CreateForTests(new MockManifestProvider(new[] { _manifestPath }), new string[] { dotnetRoot }); + var userProfileDir = Path.Combine(testDirectory, "user-profile"); + bool userLocal = WorkloadFileBasedInstall.IsUserLocal(dotnetRoot, sdkFeatureVersion); + var workloadResolver = WorkloadResolver.CreateForTests(new MockManifestProvider(new[] { _manifestPath }), dotnetRoot, userLocal, userProfileDir); var nugetDownloader = new MockNuGetPackageDownloader(dotnetRoot); var manifestUpdater = new MockWorkloadManifestUpdater(); var installParseResult = Parser.Instance.Parse(new string[] { "dotnet", "workload", "install", installingWorkload }); var installCommand = new WorkloadInstallCommand(installParseResult, reporter: _reporter, workloadResolver: workloadResolver, nugetPackageDownloader: nugetDownloader, - workloadManifestUpdater: manifestUpdater, userHome: testDirectory, version: sdkFeatureVersion, dotnetDir: dotnetRoot, tempDirPath: testDirectory); + workloadManifestUpdater: manifestUpdater, userProfileDir: userProfileDir, version: sdkFeatureVersion, dotnetDir: dotnetRoot, tempDirPath: testDirectory); installCommand.Execute(); } private void UninstallWorkload(string uninstallingWorkload, string testDirectory, string sdkFeatureVersion) { var dotnetRoot = Path.Combine(testDirectory, "dotnet"); - var workloadResolver = WorkloadResolver.CreateForTests(new MockManifestProvider(new[] { _manifestPath }), new string[] { dotnetRoot }); + var userProfileDir = Path.Combine(testDirectory, "user-profile"); + bool userLocal = WorkloadFileBasedInstall.IsUserLocal(dotnetRoot, sdkFeatureVersion); + var workloadResolver = WorkloadResolver.CreateForTests(new MockManifestProvider(new[] { _manifestPath }), dotnetRoot, userLocal, userProfileDir); var nugetDownloader = new MockNuGetPackageDownloader(dotnetRoot); var uninstallParseResult = Parser.Instance.Parse(new string[] { "dotnet", "workload", "uninstall", uninstallingWorkload }); var uninstallCommand = new WorkloadUninstallCommand(uninstallParseResult, reporter: _reporter, workloadResolver, nugetDownloader, - dotnetDir: dotnetRoot, version: sdkFeatureVersion); + dotnetDir: dotnetRoot, version: sdkFeatureVersion, userProfileDir: userProfileDir); uninstallCommand.Execute(); } } diff --git a/src/Tests/dotnet-workload-update.Tests/GivenDotnetWorkloadUpdate.cs b/src/Tests/dotnet-workload-update.Tests/GivenDotnetWorkloadUpdate.cs index 48d8645343cf..edd4834c8985 100644 --- a/src/Tests/dotnet-workload-update.Tests/GivenDotnetWorkloadUpdate.cs +++ b/src/Tests/dotnet-workload-update.Tests/GivenDotnetWorkloadUpdate.cs @@ -9,6 +9,7 @@ using FluentAssertions; using ManifestReaderTests; using Microsoft.DotNet.Cli.NuGetPackageDownloader; +using Microsoft.DotNet.Workloads.Workload; using Microsoft.DotNet.Workloads.Workload.Install; using Microsoft.NET.Sdk.WorkloadManifestReader; using Microsoft.NET.TestFramework; @@ -37,38 +38,47 @@ public GivenDotnetWorkloadUpdate(ITestOutputHelper log) : base(log) _parseResult = Parser.Instance.Parse(new string[] { "dotnet", "workload", "update" }); } - [Fact] - public void GivenWorkloadUpdateItRemovesOldPacksAfterInstall() + [Theory] + [InlineData(true)] + [InlineData(false)] + public void GivenWorkloadUpdateItRemovesOldPacksAfterInstall(bool userLocal) { - var testDirectory = _testAssetsManager.CreateTestDirectory().Path; + var testDirectory = _testAssetsManager.CreateTestDirectory(identifier: userLocal ? "userlocal" : "default").Path; var dotnetRoot = Path.Combine(testDirectory, "dotnet"); - var workloadResolver = WorkloadResolver.CreateForTests(new MockManifestProvider(new[] { _manifestPath }), new string[] { dotnetRoot }); + var userProfileDir = Path.Combine(testDirectory, "user-profile"); + var workloadResolver = WorkloadResolver.CreateForTests(new MockManifestProvider(new[] { _manifestPath }), dotnetRoot, userLocal, userProfileDir); var nugetDownloader = new MockNuGetPackageDownloader(dotnetRoot); var manifestUpdater = new MockWorkloadManifestUpdater(); var sdkFeatureVersion = "6.0.100"; var installingWorkload = "xamarin-android"; + string installRoot = userLocal ? userProfileDir : dotnetRoot; + if (userLocal) + { + WorkloadFileBasedInstall.SetUserLocal(dotnetRoot, sdkFeatureVersion); + } + // Install a workload var installParseResult = Parser.Instance.Parse(new string[] { "dotnet", "workload", "install", installingWorkload }); var installCommand = new WorkloadInstallCommand(installParseResult, reporter: _reporter, workloadResolver: workloadResolver, nugetPackageDownloader: nugetDownloader, - workloadManifestUpdater: manifestUpdater, userHome: testDirectory, version: sdkFeatureVersion, dotnetDir: dotnetRoot, tempDirPath: testDirectory); + workloadManifestUpdater: manifestUpdater, userProfileDir: userProfileDir, version: sdkFeatureVersion, dotnetDir: dotnetRoot, tempDirPath: testDirectory); installCommand.Execute(); // 7 packs in packs dir, 1 template pack - var installPacks = Directory.GetDirectories(Path.Combine(dotnetRoot, "packs")); + var installPacks = Directory.GetDirectories(Path.Combine(installRoot, "packs")); installPacks.Count().Should().Be(7); foreach (var packDir in installPacks) { Directory.GetDirectories(packDir).Count().Should().Be(1); // 1 version of each pack installed } - File.Exists(Path.Combine(dotnetRoot, "metadata", "workloads", "InstalledPacks", "v1", "Xamarin.Android.Sdk", "8.4.7", "6.0.100")) // Original pack version is installed + File.Exists(Path.Combine(installRoot, "metadata", "workloads", "InstalledPacks", "v1", "Xamarin.Android.Sdk", "8.4.7", "6.0.100")) // Original pack version is installed .Should().BeTrue(); - File.Exists(Path.Combine(dotnetRoot, "template-packs", "xamarin.android.templates.1.0.3.nupkg")) + File.Exists(Path.Combine(installRoot, "template-packs", "xamarin.android.templates.1.0.3.nupkg")) .Should().BeTrue(); // Install records are correct - File.Exists(Path.Combine(dotnetRoot, "metadata", "workloads", sdkFeatureVersion, "InstalledWorkloads", installingWorkload)) + File.Exists(Path.Combine(installRoot, "metadata", "workloads", sdkFeatureVersion, "InstalledWorkloads", installingWorkload)) .Should().BeTrue(); - var packRecordDirs = Directory.GetDirectories(Path.Combine(dotnetRoot, "metadata", "workloads", "InstalledPacks", "v1")); + var packRecordDirs = Directory.GetDirectories(Path.Combine(installRoot, "metadata", "workloads", "InstalledPacks", "v1")); packRecordDirs.Count().Should().Be(8); foreach (var packRecordDir in packRecordDirs) { @@ -80,29 +90,29 @@ public void GivenWorkloadUpdateItRemovesOldPacksAfterInstall() // Mock updating the manifest workloadResolver = WorkloadResolver.CreateForTests( new MockManifestProvider(new[] { Path.Combine(_testAssetsManager.GetAndValidateTestProjectDirectory("SampleUpdatedManifest"), "Sample.json") }), - new string[] { dotnetRoot }); + dotnetRoot, userLocal, userProfileDir); // Update workload var updateParseResult = Parser.Instance.Parse(new string[] { "dotnet", "workload", "update" }); var updateCommand = new WorkloadUpdateCommand(updateParseResult, reporter: _reporter, workloadResolver: workloadResolver, nugetPackageDownloader: nugetDownloader, - workloadManifestUpdater: manifestUpdater, userHome: testDirectory, version: sdkFeatureVersion, dotnetDir: dotnetRoot, tempDirPath: testDirectory); + workloadManifestUpdater: manifestUpdater, userProfileDir: userProfileDir, version: sdkFeatureVersion, dotnetDir: dotnetRoot, tempDirPath: testDirectory); updateCommand.Execute(); // 6 packs in packs dir, 1 template pack - var updatePacks = Directory.GetDirectories(Path.Combine(dotnetRoot, "packs")); + var updatePacks = Directory.GetDirectories(Path.Combine(installRoot, "packs")); updatePacks.Count().Should().Be(6); foreach (var packDir in updatePacks) { Directory.GetDirectories(packDir).Count().Should().Be(1); // 1 version of each pack installed } - File.Exists(Path.Combine(dotnetRoot, "metadata", "workloads", "InstalledPacks", "v1", "Xamarin.Android.Sdk", "8.5.7", "6.0.100")) // New pack version is installed + File.Exists(Path.Combine(installRoot, "metadata", "workloads", "InstalledPacks", "v1", "Xamarin.Android.Sdk", "8.5.7", "6.0.100")) // New pack version is installed .Should().BeTrue(); - File.Exists(Path.Combine(dotnetRoot, "template-packs", "xamarin.android.templates.2.1.3.nupkg")) + File.Exists(Path.Combine(installRoot, "template-packs", "xamarin.android.templates.2.1.3.nupkg")) .Should().BeTrue(); // Install records are correct - File.Exists(Path.Combine(dotnetRoot, "metadata", "workloads", sdkFeatureVersion, "InstalledWorkloads", installingWorkload)) + File.Exists(Path.Combine(installRoot, "metadata", "workloads", sdkFeatureVersion, "InstalledWorkloads", installingWorkload)) .Should().BeTrue(); - packRecordDirs = Directory.GetDirectories(Path.Combine(dotnetRoot, "metadata", "workloads", "InstalledPacks", "v1")); + packRecordDirs = Directory.GetDirectories(Path.Combine(installRoot, "metadata", "workloads", "InstalledPacks", "v1")); packRecordDirs.Count().Should().Be(7); foreach (var packRecordDir in packRecordDirs) { @@ -112,50 +122,60 @@ public void GivenWorkloadUpdateItRemovesOldPacksAfterInstall() } } - [Fact] - public void GivenWorkloadUpdateAcrossFeatureBandsItUpdatesPacks() + [Theory] + [InlineData(true)] + [InlineData(false)] + public void GivenWorkloadUpdateAcrossFeatureBandsItUpdatesPacks(bool userLocal) { - var testDirectory = _testAssetsManager.CreateTestDirectory().Path; + var testDirectory = _testAssetsManager.CreateTestDirectory(identifier: userLocal ? "userlocal" : "default").Path; var dotnetRoot = Path.Combine(testDirectory, "dotnet"); + var userProfileDir = Path.Combine(testDirectory, "user-profile"); var manifestPath = Path.Combine(_testAssetsManager.GetAndValidateTestProjectDirectory("SampleManifest"), "BasicSample.json"); - var workloadResolver = WorkloadResolver.CreateForTests(new MockManifestProvider(new[] { manifestPath }), new string[] { dotnetRoot }); + var workloadResolver = WorkloadResolver.CreateForTests(new MockManifestProvider(new[] { manifestPath }), dotnetRoot, userLocal, userProfileDir); var nugetDownloader = new MockNuGetPackageDownloader(dotnetRoot); var manifestUpdater = new MockWorkloadManifestUpdater(); var sdkFeatureVersion = "6.0.100"; var installingWorkload = "simple-workload"; + + string installRoot = userLocal ? userProfileDir : dotnetRoot; + if (userLocal) + { + WorkloadFileBasedInstall.SetUserLocal(dotnetRoot, sdkFeatureVersion); + } + var workloadPacks = new List() { - CreatePackInfo("mock-pack-1", "1.0.0", WorkloadPackKind.Framework, Path.Combine(dotnetRoot, "packs", "mock-pack-1", "1.0.0"), "mock-pack-1"), - CreatePackInfo("mock-pack-2", "2.0.0", WorkloadPackKind.Framework, Path.Combine(dotnetRoot, "packs", "mock-pack-2", "2.0.0"), "mock-pack-2") + CreatePackInfo("mock-pack-1", "1.0.0", WorkloadPackKind.Framework, Path.Combine(installRoot, "packs", "mock-pack-1", "1.0.0"), "mock-pack-1"), + CreatePackInfo("mock-pack-2", "2.0.0", WorkloadPackKind.Framework, Path.Combine(installRoot, "packs", "mock-pack-2", "2.0.0"), "mock-pack-2") }; // Lay out workload installs for a previous feature band var oldFeatureBand = "5.0.100"; - var packRecordDir = Path.Combine(dotnetRoot, "metadata", "workloads", "InstalledPacks", "v1"); + var packRecordDir = Path.Combine(installRoot, "metadata", "workloads", "InstalledPacks", "v1"); foreach (var pack in workloadPacks) { Directory.CreateDirectory(Path.Combine(packRecordDir, pack.Id, pack.Version)); File.Create(Path.Combine(packRecordDir, pack.Id, pack.Version, oldFeatureBand)); } - Directory.CreateDirectory(Path.Combine(dotnetRoot, "metadata", "workloads", oldFeatureBand, "InstalledWorkloads")); - Directory.CreateDirectory(Path.Combine(dotnetRoot, "metadata", "workloads", sdkFeatureVersion, "InstalledWorkloads")); - File.Create(Path.Combine(dotnetRoot, "metadata", "workloads", oldFeatureBand, "InstalledWorkloads", installingWorkload)); - File.Create(Path.Combine(dotnetRoot, "metadata", "workloads", sdkFeatureVersion, "InstalledWorkloads", installingWorkload)); + Directory.CreateDirectory(Path.Combine(installRoot, "metadata", "workloads", oldFeatureBand, "InstalledWorkloads")); + Directory.CreateDirectory(Path.Combine(installRoot, "metadata", "workloads", sdkFeatureVersion, "InstalledWorkloads")); + File.Create(Path.Combine(installRoot, "metadata", "workloads", oldFeatureBand, "InstalledWorkloads", installingWorkload)); + File.Create(Path.Combine(installRoot, "metadata", "workloads", sdkFeatureVersion, "InstalledWorkloads", installingWorkload)); // Update workload (without installing any workloads to this feature band) var updateParseResult = Parser.Instance.Parse(new string[] { "dotnet", "workload", "update", "--from-previous-sdk" }); var updateCommand = new WorkloadUpdateCommand(updateParseResult, reporter: _reporter, workloadResolver: workloadResolver, nugetPackageDownloader: nugetDownloader, - workloadManifestUpdater: manifestUpdater, userHome: testDirectory, version: sdkFeatureVersion, dotnetDir: dotnetRoot, tempDirPath: testDirectory); + workloadManifestUpdater: manifestUpdater, userProfileDir: userProfileDir, version: sdkFeatureVersion, dotnetDir: dotnetRoot, tempDirPath: testDirectory); updateCommand.Execute(); foreach (var pack in workloadPacks) { - Directory.Exists(pack.Path).Should().BeTrue(because: "Pack should be installed"); + Directory.Exists(pack.Path).Should().BeTrue(because: $"Pack should be installed {testDirectory}"); File.Exists(Path.Combine(packRecordDir, pack.Id, pack.Version, oldFeatureBand)) .Should().BeTrue(because: "Pack install record should still be present for old feature band"); } - File.Exists(Path.Combine(dotnetRoot, "metadata", "workloads", oldFeatureBand, "InstalledWorkloads", installingWorkload)) + File.Exists(Path.Combine(installRoot, "metadata", "workloads", oldFeatureBand, "InstalledWorkloads", installingWorkload)) .Should().BeTrue(because: "Workload install record should still be present for old feature band"); - File.Exists(Path.Combine(dotnetRoot, "metadata", "workloads", sdkFeatureVersion, "InstalledWorkloads", installingWorkload)) + File.Exists(Path.Combine(installRoot, "metadata", "workloads", sdkFeatureVersion, "InstalledWorkloads", installingWorkload)) .Should().BeTrue(because: "Workload install record should be present for current feature band"); } @@ -244,27 +264,46 @@ public void GivenWorkloadUpdateItPrintsDownloadUrls() string.Join(" ", _reporter.Lines).Should().Contain("mock-manifest-url"); } - [Fact] - public void GivenWorkloadUpdateAcrossFeatureBandsItErrorsWhenManifestsDoNotExist() + [Theory] + [InlineData(true)] + [InlineData(false)] + public void GivenWorkloadUpdateAcrossFeatureBandsItErrorsWhenManifestsDoNotExist(bool userLocal) { - var testDirectory = _testAssetsManager.CreateTestDirectory().Path; + var testDirectory = _testAssetsManager.CreateTestDirectory(identifier: userLocal ? "userlocal" : "default").Path; var dotnetRoot = Path.Combine(testDirectory, "dotnet"); - var updateParseResult = Parser.Instance.Parse(new string[] { "dotnet", "workload", "update", "--sdk-version", "7.0.100" }); + var userProfileDir = Path.Combine(testDirectory, "user-profile"); + var sdkFeatureVersion = "7.0.100"; + var updateParseResult = Parser.Instance.Parse(new string[] { "dotnet", "workload", "update", "--sdk-version", sdkFeatureVersion }); + + if (userLocal) + { + WorkloadFileBasedInstall.SetUserLocal(dotnetRoot, sdkFeatureVersion); + } - var exceptionThrown = Assert.Throws(() => new WorkloadUpdateCommand(updateParseResult, reporter: _reporter, dotnetDir: dotnetRoot)); + var exceptionThrown = Assert.Throws(() => new WorkloadUpdateCommand(updateParseResult, reporter: _reporter, dotnetDir: dotnetRoot, userProfileDir: userProfileDir)); exceptionThrown.Message.Should().Contain("No manifests exist"); } - [Fact] - public void GivenWorkloadUpdateAcrossFeatureBandsItErrorsWhenUnableToReadManifest() + [Theory] + [InlineData(true)] + [InlineData(false)] + public void GivenWorkloadUpdateAcrossFeatureBandsItErrorsWhenUnableToReadManifest(bool userLocal) { - var testDirectory = _testAssetsManager.CreateTestDirectory().Path; + var testDirectory = _testAssetsManager.CreateTestDirectory(identifier: userLocal ? "userlocal" : "default").Path; var dotnetRoot = Path.Combine(testDirectory, "dotnet"); - var updateParseResult = Parser.Instance.Parse(new string[] { "dotnet", "workload", "update", "--sdk-version", "7.0.100" }); + var userProfileDir = Path.Combine(testDirectory, "user-profile"); + var sdkFeatureVersion = "7.0.100"; + var updateParseResult = Parser.Instance.Parse(new string[] { "dotnet", "workload", "update", "--sdk-version", sdkFeatureVersion }); + + string installRoot = userLocal ? userProfileDir : dotnetRoot; + if (userLocal) + { + WorkloadFileBasedInstall.SetUserLocal(dotnetRoot, sdkFeatureVersion); + } // Write manifest of "new" format that we don't recognize - Directory.CreateDirectory(Path.Combine(dotnetRoot, "sdk-manifests", "7.0.100", "mock.workload")); - File.WriteAllText(Path.Combine(dotnetRoot, "sdk-manifests", "7.0.100", "mock.workload", "WorkloadManifest.json"), @"{ + Directory.CreateDirectory(Path.Combine(installRoot, "sdk-manifests", "7.0.100", "mock.workload")); + File.WriteAllText(Path.Combine(installRoot, "sdk-manifests", "7.0.100", "mock.workload", "WorkloadManifest.json"), @"{ ""version"": 1, ""workloads"": { ""mock.workload"": { @@ -274,7 +313,7 @@ public void GivenWorkloadUpdateAcrossFeatureBandsItErrorsWhenUnableToReadManifes } "); - var exceptionThrown = Assert.Throws(() => new WorkloadUpdateCommand(updateParseResult, reporter: _reporter, dotnetDir: dotnetRoot)); + var exceptionThrown = Assert.Throws(() => new WorkloadUpdateCommand(updateParseResult, reporter: _reporter, dotnetDir: dotnetRoot, userProfileDir: userProfileDir)); exceptionThrown.Message.Should().Contain(string.Format(Workloads.Workload.Install.LocalizableStrings.IncompatibleManifests, "7.0.100")); } @@ -318,7 +357,7 @@ public void GivenPrintRollbackDefinitionItIncludesAllInstalledManifests() var installer = includeInstalledPacks ? new MockPackWorkloadInstaller(failingWorkload, failingPack, installedWorkloads: installedWorkloads, installedPacks: installedPacks) : new MockPackWorkloadInstaller(failingWorkload, failingPack, installedWorkloads: installedWorkloads); - var workloadResolver = WorkloadResolver.CreateForTests(new MockManifestProvider(new[] { _manifestPath }), new string[] { dotnetRoot }); + var workloadResolver = WorkloadResolver.CreateForTests(new MockManifestProvider(new[] { _manifestPath }), dotnetRoot); var nugetDownloader = new MockNuGetPackageDownloader(dotnetRoot); var manifestUpdater = new MockWorkloadManifestUpdater(manifestUpdates, _manifestPath); var installManager = new WorkloadUpdateCommand( @@ -328,7 +367,7 @@ public void GivenPrintRollbackDefinitionItIncludesAllInstalledManifests() workloadInstaller: installer, nugetPackageDownloader: nugetDownloader, workloadManifestUpdater: manifestUpdater, - userHome: testDirectory, + userProfileDir: testDirectory, version: "6.0.100"); return (testDirectory, installManager, installer, workloadResolver, manifestUpdater, nugetDownloader);