diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/Constants.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/Constants.cs index a565090b6e46..5fff0de94751 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/Constants.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/Constants.cs @@ -28,5 +28,6 @@ public static class Constants public static readonly string AnyRid = "any"; public static readonly string RestoreInteractiveOption = "--interactive"; + public static readonly string workloadSetVersionFileName = "workloadVersion.txt"; } } diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/PathUtility.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/PathUtility.cs index 75019c9e32e3..1957a49a1739 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/PathUtility.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/PathUtility.cs @@ -97,6 +97,30 @@ public static bool TryDeleteDirectory(string directoryPath) } } + /// + /// Deletes the provided file. Then deletes the parent directory if empty + /// and continues to its parent until it fails. Returns whether it succeeded + /// in deleting the file it was intended to delete. + /// + public static bool DeleteFileAndEmptyParents(string path) + { + if (!File.Exists(path)) + { + return false; + } + + File.Delete(path); + var dir = Path.GetDirectoryName(path); + + while (!Directory.EnumerateFileSystemEntries(dir).Any()) + { + Directory.Delete(dir); + dir = Path.GetDirectoryName(dir); + } + + return !File.Exists(path); + } + /// /// Returns childItem relative to directory, with Path.DirectorySeparatorChar as separator /// diff --git a/src/Cli/dotnet/Installer/Windows/InstallMessageDispatcher.cs b/src/Cli/dotnet/Installer/Windows/InstallMessageDispatcher.cs index 7d446db4944d..538bae6d1a46 100644 --- a/src/Cli/dotnet/Installer/Windows/InstallMessageDispatcher.cs +++ b/src/Cli/dotnet/Installer/Windows/InstallMessageDispatcher.cs @@ -192,5 +192,21 @@ public InstallResponseMessage SendUpdateWorkloadModeRequest(SdkFeatureBand sdkFe UseWorkloadSets = newMode, }); } + + /// + /// Send an to adjust the workload set version used for installing and updating workloads + /// + /// The SDK feature band of the install state file to write + /// The workload set version + /// + public InstallResponseMessage SendUpdateWorkloadSetRequest(SdkFeatureBand sdkFeatureBand, string newVersion) + { + return Send(new InstallRequestMessage + { + RequestType = InstallRequestType.AdjustWorkloadSetVersion, + SdkFeatureBand = sdkFeatureBand.ToString(), + WorkloadSetVersion = newVersion, + }); + } } } diff --git a/src/Cli/dotnet/Installer/Windows/InstallRequestMessage.cs b/src/Cli/dotnet/Installer/Windows/InstallRequestMessage.cs index 5ab6430bcbf4..962bdd90dd9f 100644 --- a/src/Cli/dotnet/Installer/Windows/InstallRequestMessage.cs +++ b/src/Cli/dotnet/Installer/Windows/InstallRequestMessage.cs @@ -128,6 +128,14 @@ public bool UseWorkloadSets get; set; } + /// + /// The workload set version + /// + public string WorkloadSetVersion + { + get; set; + } + /// /// Converts a deserialized array of bytes into an . /// diff --git a/src/Cli/dotnet/Installer/Windows/InstallRequestType.cs b/src/Cli/dotnet/Installer/Windows/InstallRequestType.cs index 2d90d3f2f68f..b734531d1b53 100644 --- a/src/Cli/dotnet/Installer/Windows/InstallRequestType.cs +++ b/src/Cli/dotnet/Installer/Windows/InstallRequestType.cs @@ -69,5 +69,10 @@ public enum InstallRequestType /// Changes the workload mode /// AdjustWorkloadMode, + + /// + /// Changes the workload set version + /// + AdjustWorkloadSetVersion, } } diff --git a/src/Cli/dotnet/commands/InstallingWorkloadCommand.cs b/src/Cli/dotnet/commands/InstallingWorkloadCommand.cs index 9e010dfb3632..d8ca6e0fc51d 100644 --- a/src/Cli/dotnet/commands/InstallingWorkloadCommand.cs +++ b/src/Cli/dotnet/commands/InstallingWorkloadCommand.cs @@ -34,6 +34,7 @@ internal abstract class InstallingWorkloadCommand : WorkloadCommandBase protected readonly SdkFeatureBand _sdkFeatureBand; protected readonly ReleaseVersion _targetSdkVersion; protected readonly string _fromRollbackDefinition; + protected string _workloadSetVersion; protected readonly PackageSourceLocation _packageSourceLocation; protected readonly IWorkloadResolverFactory _workloadResolverFactory; protected IWorkloadResolver _workloadResolver; @@ -96,6 +97,53 @@ protected static Dictionary GetInstallStateContents(IEnumerable< manifestVersionUpdates.Select(update => new WorkloadManifestInfo(update.ManifestId.ToString(), update.NewVersion.ToString(), /* We don't actually use the directory here */ string.Empty, update.NewFeatureBand)) ).ToDictionaryForJson(); + public static bool ShouldUseWorkloadSetMode(SdkFeatureBand sdkFeatureBand, string dotnetDir) + { + string path = Path.Combine(WorkloadInstallType.GetInstallStateFolder(sdkFeatureBand, dotnetDir), "default.json"); + var installStateContents = File.Exists(path) ? InstallStateContents.FromString(File.ReadAllText(path)) : new InstallStateContents(); + return installStateContents.UseWorkloadSets ?? false; + } + + protected IEnumerable HandleWorkloadUpdateFromVersion(ITransactionContext context, DirectoryPath? offlineCache) + { + // Ensure workload set mode is set to 'workloadset' + // Do not skip checking the mode first, as setting it triggers + // an admin authorization popup for MSI-based installs. + if (!ShouldUseWorkloadSetMode(_sdkFeatureBand, _dotnetPath)) + { + _workloadInstaller.UpdateInstallMode(_sdkFeatureBand, true); + } + + _workloadManifestUpdater.DownloadWorkloadSet(_workloadSetVersion, offlineCache); + return InstallWorkloadSet(context); + } + + public IEnumerable InstallWorkloadSet(ITransactionContext context) + { + var advertisingPackagePath = Path.Combine(_userProfileDir, "sdk-advertising", _sdkFeatureBand.ToString(), "microsoft.net.workloads"); + if (File.Exists(Path.Combine(advertisingPackagePath, Constants.workloadSetVersionFileName))) + { + // This file isn't created in tests. + PrintWorkloadSetTransition(File.ReadAllText(Path.Combine(advertisingPackagePath, Constants.workloadSetVersionFileName))); + } + var workloadSetPath = _workloadInstaller.InstallWorkloadSet(context, advertisingPackagePath); + var files = Directory.EnumerateFiles(workloadSetPath, "*.workloadset.json"); + return _workloadManifestUpdater.ParseRollbackDefinitionFiles(files); + } + + private void PrintWorkloadSetTransition(string newVersion) + { + var currentVersion = _workloadResolver.GetWorkloadVersion(); + if (currentVersion == null) + { + Reporter.WriteLine(string.Format(Strings.NewWorkloadSet, newVersion)); + } + else + { + Reporter.WriteLine(string.Format(Strings.WorkloadSetUpgrade, currentVersion, newVersion)); + } + } + protected async Task> GetDownloads(IEnumerable workloadIds, bool skipManifestUpdate, bool includePreview, string downloadFolder = null) { List ret = new(); @@ -202,6 +250,11 @@ internal static class InstallingWorkloadCommandParser Hidden = true }; + public static readonly CliOption WorkloadSetVersionOption = new("--version") + { + Description = Strings.WorkloadSetVersionOptionDescription + }; + public static readonly CliOption PrintDownloadLinkOnlyOption = new("--print-download-link-only") { Description = Strings.PrintDownloadLinkOnlyDescription, diff --git a/src/Cli/dotnet/commands/dotnet-workload/InstallStateContents.cs b/src/Cli/dotnet/commands/dotnet-workload/InstallStateContents.cs index 3c30ce66ab73..b625067712d3 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/InstallStateContents.cs +++ b/src/Cli/dotnet/commands/dotnet-workload/InstallStateContents.cs @@ -4,6 +4,8 @@ using System.Text.Json; using System.Text.Json.Serialization; +#pragma warning disable CS8632 + namespace Microsoft.DotNet.Workloads.Workload { internal class InstallStateContents @@ -12,17 +14,21 @@ internal class InstallStateContents public bool? UseWorkloadSets { get; set; } [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public Dictionary Manifests { get; set; } + public Dictionary? Manifests { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? WorkloadVersion { get; set; } private static readonly JsonSerializerOptions s_options = new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, WriteIndented = true, + AllowTrailingCommas = true, }; public static InstallStateContents FromString(string contents) { - return JsonSerializer.Deserialize(contents, s_options); + return JsonSerializer.Deserialize(contents, s_options) ?? new InstallStateContents(); } public static InstallStateContents FromPath(string path) @@ -35,4 +41,6 @@ public override string ToString() return JsonSerializer.Serialize(this, s_options); } } -} \ No newline at end of file +} + +#pragma warning restore CS8632 \ No newline at end of file diff --git a/src/Cli/dotnet/commands/dotnet-workload/WorkloadInfoHelper.cs b/src/Cli/dotnet/commands/dotnet-workload/WorkloadInfoHelper.cs index f686d494a53d..ec6a7641db54 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/WorkloadInfoHelper.cs +++ b/src/Cli/dotnet/commands/dotnet-workload/WorkloadInfoHelper.cs @@ -17,6 +17,7 @@ internal class WorkloadInfoHelper : IWorkloadInfoHelper { public readonly SdkFeatureBand _currentSdkFeatureBand; private readonly string _targetSdkVersion; + public string DotnetPath { get; } public WorkloadInfoHelper( bool isInteractive, @@ -30,20 +31,20 @@ public WorkloadInfoHelper( string userProfileDir = null, IWorkloadResolver workloadResolver = null) { - string dotnetPath = dotnetDir ?? Path.GetDirectoryName(Environment.ProcessPath); + DotnetPath = dotnetDir ?? Path.GetDirectoryName(Environment.ProcessPath); ReleaseVersion currentSdkReleaseVersion = new(currentSdkVersion ?? Product.Version); _currentSdkFeatureBand = new SdkFeatureBand(currentSdkReleaseVersion); _targetSdkVersion = targetSdkVersion; userProfileDir ??= CliFolderPathCalculator.DotnetUserProfileFolderPath; ManifestProvider = - new SdkDirectoryWorkloadManifestProvider(dotnetPath, + new SdkDirectoryWorkloadManifestProvider(DotnetPath, string.IsNullOrWhiteSpace(_targetSdkVersion) ? currentSdkReleaseVersion.ToString() : _targetSdkVersion, userProfileDir, SdkDirectoryWorkloadManifestProvider.GetGlobalJsonPath(Environment.CurrentDirectory)); WorkloadResolver = workloadResolver ?? NET.Sdk.WorkloadManifestReader.WorkloadResolver.Create( - ManifestProvider, dotnetPath, + ManifestProvider, DotnetPath, currentSdkReleaseVersion.ToString(), userProfileDir); var restoreConfig = new RestoreActionConfig(Interactive: isInteractive); diff --git a/src/Cli/dotnet/commands/dotnet-workload/clean/WorkloadCleanCommand.cs b/src/Cli/dotnet/commands/dotnet-workload/clean/WorkloadCleanCommand.cs index 5ccc9998f91f..ef0b679607b0 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/clean/WorkloadCleanCommand.cs +++ b/src/Cli/dotnet/commands/dotnet-workload/clean/WorkloadCleanCommand.cs @@ -59,7 +59,7 @@ public override int Execute() private void ExecuteGarbageCollection() { - _workloadInstaller.GarbageCollect(workloadSetVersion => _workloadResolverFactory.CreateForWorkloadSet(_dotnetPath, _sdkVersion.ToString(), _userProfileDir, workloadSetVersion), + _workloadInstaller.GarbageCollect(workloadVersion => _workloadResolverFactory.CreateForWorkloadSet(_dotnetPath, _sdkVersion.ToString(), _userProfileDir, workloadVersion), cleanAllPacks: _cleanAll); DisplayUninstallableVSWorkloads(); diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/FileBasedInstaller.cs b/src/Cli/dotnet/commands/dotnet-workload/install/FileBasedInstaller.cs index 6b4af4b5f2cb..170dda6cccd4 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/install/FileBasedInstaller.cs +++ b/src/Cli/dotnet/commands/dotnet-workload/install/FileBasedInstaller.cs @@ -13,7 +13,7 @@ using NuGet.Common; using NuGet.Versioning; using static Microsoft.NET.Sdk.WorkloadManifestReader.WorkloadResolver; - +using PathUtility = Microsoft.DotNet.Tools.Common.PathUtility; namespace Microsoft.DotNet.Workloads.Workload.Install { @@ -85,6 +85,31 @@ IEnumerable GetPacksInWorkloads(IEnumerable workloadIds) return packs; } + public string InstallWorkloadSet(ITransactionContext context, string advertisingPackagePath) + { + var workloadVersion = File.ReadAllText(Path.Combine(advertisingPackagePath, Constants.workloadSetVersionFileName)); + var workloadSetPath = Path.Combine(_dotnetDir, "sdk-manifests", _sdkFeatureBand.ToString(), "workloadsets", workloadVersion); + context.Run( + action: () => + { + Directory.CreateDirectory(workloadSetPath); + + foreach (var file in Directory.EnumerateFiles(advertisingPackagePath)) + { + File.Copy(file, Path.Combine(workloadSetPath, Path.GetFileName(file)), overwrite: true); + } + }, + rollback: () => + { + foreach (var file in Directory.EnumerateFiles(workloadSetPath)) + { + PathUtility.DeleteFileAndEmptyParents(file); + } + }); + + return workloadSetPath; + } + public void InstallWorkloads(IEnumerable workloadIds, SdkFeatureBand sdkFeatureBand, ITransactionContext transactionContext, DirectoryPath? offlineCache = null) { var packInfos = GetPacksInWorkloads(workloadIds); @@ -452,6 +477,15 @@ public void GarbageCollect(Func getResolverForWorkloa } + public void AdjustWorkloadSetInInstallState(SdkFeatureBand sdkFeatureBand, string workloadVersion) + { + string path = Path.Combine(WorkloadInstallType.GetInstallStateFolder(_sdkFeatureBand, _dotnetDir), "default.json"); + Directory.CreateDirectory(Path.GetDirectoryName(path)); + var installStateContents = InstallStateContents.FromPath(path); + installStateContents.WorkloadVersion = workloadVersion; + File.WriteAllText(path, installStateContents.ToString()); + } + public void RemoveManifestsFromInstallState(SdkFeatureBand sdkFeatureBand) { string path = Path.Combine(WorkloadInstallType.GetInstallStateFolder(_sdkFeatureBand, _dotnetDir), "default.json"); @@ -509,7 +543,14 @@ public void Shutdown() public PackageId GetManifestPackageId(ManifestId manifestId, SdkFeatureBand featureBand) { - return new PackageId($"{manifestId}.Manifest-{featureBand}"); + if (manifestId.ToString().Equals("Microsoft.NET.Workloads", StringComparison.OrdinalIgnoreCase)) + { + return new PackageId($"{manifestId}.{featureBand}"); + } + else + { + return new PackageId($"{manifestId}.Manifest-{featureBand}"); + } } public async Task ExtractManifestAsync(string nupkgPath, string targetPath) diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/IInstaller.cs b/src/Cli/dotnet/commands/dotnet-workload/install/IInstaller.cs index 5d388ef8bd21..275d5ecbcdce 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/install/IInstaller.cs +++ b/src/Cli/dotnet/commands/dotnet-workload/install/IInstaller.cs @@ -13,6 +13,8 @@ internal interface IInstaller : IWorkloadManifestInstaller { int ExitCode { get; } + string InstallWorkloadSet(ITransactionContext context, string advertisingPackagePath); + void InstallWorkloads(IEnumerable workloadIds, SdkFeatureBand sdkFeatureBand, ITransactionContext transactionContext, DirectoryPath? offlineCache = null); void RepairWorkloads(IEnumerable workloadIds, SdkFeatureBand sdkFeatureBand, DirectoryPath? offlineCache = null); @@ -25,6 +27,8 @@ internal interface IInstaller : IWorkloadManifestInstaller IEnumerable GetDownloads(IEnumerable workloadIds, SdkFeatureBand sdkFeatureBand, bool includeInstalledItems); + void AdjustWorkloadSetInInstallState(SdkFeatureBand sdkFeatureBand, string workloadVersion); + /// /// Replace the workload resolver used by this installer. Typically used to call /// for a set of workload manifests that isn't currently installed diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/IWorkloadManifestUpdater.cs b/src/Cli/dotnet/commands/dotnet-workload/install/IWorkloadManifestUpdater.cs index d9dec221ad29..49af91545c87 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/install/IWorkloadManifestUpdater.cs +++ b/src/Cli/dotnet/commands/dotnet-workload/install/IWorkloadManifestUpdater.cs @@ -8,19 +8,22 @@ namespace Microsoft.DotNet.Workloads.Workload.Install { internal interface IWorkloadManifestUpdater { - Task UpdateAdvertisingManifestsAsync(bool includePreviews, DirectoryPath? offlineCache = null); + Task UpdateAdvertisingManifestsAsync(bool includePreviews, bool useWorkloadSets = false, DirectoryPath? offlineCache = null); Task BackgroundUpdateAdvertisingManifestsWhenRequiredAsync(); IEnumerable CalculateManifestUpdates(); IEnumerable CalculateManifestRollbacks(string rollbackDefinitionFilePath); + IEnumerable ParseRollbackDefinitionFiles(IEnumerable files); Task> GetManifestPackageDownloadsAsync(bool includePreviews, SdkFeatureBand providedSdkFeatureBand, SdkFeatureBand installedSdkFeatureBand); IEnumerable GetUpdatableWorkloadsToAdvertise(IEnumerable installedWorkloads); void DeleteUpdatableWorkloadsFile(); + + void DownloadWorkloadSet(string version, DirectoryPath? offlineCache); } internal record ManifestUpdateWithWorkloads(ManifestVersionUpdate ManifestUpdate, WorkloadCollection Workloads); diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/LocalizableStrings.resx b/src/Cli/dotnet/commands/dotnet-workload/install/LocalizableStrings.resx index b9cd5af6b5dd..6fddf990e564 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/install/LocalizableStrings.resx +++ b/src/Cli/dotnet/commands/dotnet-workload/install/LocalizableStrings.resx @@ -346,4 +346,13 @@ Manifest MSI not found in NuGet package {0} + + Update to the specified workload version. + + + Installing workload version {0}. + + + Updating workload version from {0} to {1}. + diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/MsiInstallerBase.cs b/src/Cli/dotnet/commands/dotnet-workload/install/MsiInstallerBase.cs index ed4dfcbe35c8..8f3b0ca4d4e2 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/install/MsiInstallerBase.cs +++ b/src/Cli/dotnet/commands/dotnet-workload/install/MsiInstallerBase.cs @@ -212,10 +212,8 @@ protected void UpdateInstallMode(SdkFeatureBand sdkFeatureBand, bool newMode) if (IsElevated) { // Create the parent folder for the state file and set up all required ACLs - SecurityUtils.CreateSecureDirectory(Path.GetDirectoryName(path)); installStateContents.UseWorkloadSets = newMode; - File.WriteAllText(path, installStateContents.ToString()); - SecurityUtils.SecureFile(path); + CreateSecureFileInDirectory(path, installStateContents.ToString()); } else if (IsClient) { @@ -228,6 +226,35 @@ protected void UpdateInstallMode(SdkFeatureBand sdkFeatureBand, bool newMode) } } + public void AdjustWorkloadSetInInstallState(SdkFeatureBand sdkFeatureBand, string workloadVersion) + { + string path = Path.Combine(WorkloadInstallType.GetInstallStateFolder(sdkFeatureBand, DotNetHome), "default.json"); + var installStateContents = InstallStateContents.FromPath(path); + if ((installStateContents.WorkloadVersion == null && workloadVersion == null) || + (installStateContents.WorkloadVersion != null && installStateContents.WorkloadVersion.Equals(workloadVersion))) + { + return; + } + + Elevate(); + + if (IsElevated) + { + // Create the parent folder for the state file and set up all required ACLs + installStateContents.WorkloadVersion = workloadVersion; + CreateSecureFileInDirectory(path, installStateContents.ToString()); + } + else if (IsClient) + { + InstallResponseMessage response = Dispatcher.SendUpdateWorkloadSetRequest(sdkFeatureBand, workloadVersion); + ExitOnFailure(response, "Failed to update install mode."); + } + else + { + throw new InvalidOperationException($"Invalid configuration: elevated: {IsElevated}, client: {IsClient}"); + } + } + /// /// Installs the specified MSI. /// @@ -506,13 +533,8 @@ public void SaveInstallStateManifestVersions(SdkFeatureBand sdkFeatureBand, Dict if (IsElevated) { // Create the parent folder for the state file and set up all required ACLs - SecurityUtils.CreateSecureDirectory(Path.GetDirectoryName(path)); - - installStateContents.Manifests = manifestContents; - File.WriteAllText(path, installStateContents.ToString()); - - SecurityUtils.SecureFile(path); + CreateSecureFileInDirectory(path, installStateContents.ToString()); } else if (IsClient) { @@ -520,5 +542,12 @@ public void SaveInstallStateManifestVersions(SdkFeatureBand sdkFeatureBand, Dict ExitOnFailure(respone, $"Failed to write install state file: {path}"); } } + + private void CreateSecureFileInDirectory(string path, string contents) + { + SecurityUtils.CreateSecureDirectory(Path.GetDirectoryName(path)); + File.WriteAllText(path, contents); + SecurityUtils.SecureFile(path); + } } } diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/NetSdkMsiInstallerClient.cs b/src/Cli/dotnet/commands/dotnet-workload/install/NetSdkMsiInstallerClient.cs index da42df3373d3..5e22f6bb329d 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/install/NetSdkMsiInstallerClient.cs +++ b/src/Cli/dotnet/commands/dotnet-workload/install/NetSdkMsiInstallerClient.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Linq; using System.Runtime.Versioning; using Microsoft.DotNet.Cli; using Microsoft.DotNet.Cli.NuGetPackageDownloader; @@ -257,6 +258,60 @@ public void GarbageCollect(Func getResolverForWorkloa } } + // advertisingPackagePath is the path to the workload set MSI nupkg in the advertising package. + public string InstallWorkloadSet(ITransactionContext context, string advertisingPackagePath) + { + var pathToReturn = string.Empty; + context.Run( + action: () => + { + pathToReturn = ModifyWorkloadSet(advertisingPackagePath, InstallAction.Install); + }, + rollback: () => + { + ModifyWorkloadSet(advertisingPackagePath, InstallAction.Uninstall); + }); + + return pathToReturn; + } + + private string ModifyWorkloadSet(string advertisingPackagePath, InstallAction requestedAction) + { + ReportPendingReboot(); + + // Resolve the package ID for the manifest payload package + var featureBand = Path.GetFileName(Path.GetDirectoryName(advertisingPackagePath)); + var workloadSetVersion = File.ReadAllText(Path.Combine(advertisingPackagePath, Constants.workloadSetVersionFileName)); + string msiPackageId = GetManifestPackageId(new ManifestId("Microsoft.NET.Workloads"), new SdkFeatureBand(featureBand)).ToString(); + string msiPackageVersion = WorkloadManifestUpdater.WorkloadSetVersionToWorkloadSetPackageVersion(workloadSetVersion); + + Log?.LogMessage($"Resolving Microsoft.NET.Workloads ({workloadSetVersion}) to {msiPackageId} ({msiPackageVersion})."); + + // Retrieve the payload from the MSI package cache. + MsiPayload msi = GetCachedMsiPayload(msiPackageId, msiPackageVersion, null); + VerifyPackage(msi); + DetectState state = DetectPackage(msi.ProductCode, out Version installedVersion); + + InstallAction plannedAction = PlanPackage(msi, state, requestedAction, installedVersion); + + if (plannedAction != InstallAction.None) + { + Elevate(); + + ExecutePackage(msi, plannedAction, msiPackageId); + + // Update the reference count against the MSI. + UpdateDependent( + plannedAction == InstallAction.Uninstall ? + InstallRequestType.RemoveDependent : + InstallRequestType.AddDependent, + msi.Manifest.ProviderKeyName, + _dependent); + } + + return Path.Combine(DotNetHome, "sdk-manifests", _sdkFeatureBand.ToString(), "workloadsets", workloadSetVersion); + } + /// /// Find all the dependents that look like they belong to SDKs. We only care /// about dependents that match the SDK host we're running under. For example, an x86 SDK should not be @@ -587,7 +642,14 @@ public void Shutdown() public PackageId GetManifestPackageId(ManifestId manifestId, SdkFeatureBand featureBand) { - return new PackageId($"{manifestId}.Manifest-{featureBand}.Msi.{RuntimeInformation.ProcessArchitecture.ToString().ToLowerInvariant()}"); + if (manifestId.ToString().Equals("Microsoft.NET.Workloads", StringComparison.OrdinalIgnoreCase)) + { + return new PackageId($"{manifestId}.{featureBand}.Msi.{RuntimeInformation.ProcessArchitecture.ToString().ToLowerInvariant()}"); + } + else + { + return new PackageId($"{manifestId}.Manifest-{featureBand}.Msi.{RuntimeInformation.ProcessArchitecture.ToString().ToLowerInvariant()}"); + } } private static object _msiAdminInstallLock = new(); diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/NetSdkMsiInstallerServer.cs b/src/Cli/dotnet/commands/dotnet-workload/install/NetSdkMsiInstallerServer.cs index 02c3bd3ead58..b2522e50e2d6 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/install/NetSdkMsiInstallerServer.cs +++ b/src/Cli/dotnet/commands/dotnet-workload/install/NetSdkMsiInstallerServer.cs @@ -109,6 +109,11 @@ public void Run() Dispatcher.ReplySuccess($"Updated install mode to use {newMode}."); break; + case InstallRequestType.AdjustWorkloadSetVersion: + AdjustWorkloadSetInInstallState(new SdkFeatureBand(request.SdkFeatureBand), request.WorkloadSetVersion); + Dispatcher.ReplySuccess($"Updated workload set version in install state to {request.WorkloadSetVersion}."); + break; + default: throw new InvalidOperationException($"Unknown message request: {(int)request.RequestType}"); } diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadGarbageCollector.cs b/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadGarbageCollector.cs index cf25019d9994..8b75adb64743 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadGarbageCollector.cs +++ b/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadGarbageCollector.cs @@ -78,11 +78,11 @@ void GarbageCollectWorkloadSets() if (File.Exists(installStateFilePath)) { // If there is a rollback state file (default.json) in the workload install state folder, don't garbage collect the workload set it specifies. - var installState = SdkDirectoryWorkloadManifestProvider.InstallStateReader.ReadInstallState(installStateFilePath); - if (!string.IsNullOrEmpty(installState.WorkloadSetVersion)) + var installState = InstallStateContents.FromPath(installStateFilePath); + if (!string.IsNullOrEmpty(installState.WorkloadVersion)) { - WorkloadSetsToKeep.Add(installState.WorkloadSetVersion); - _verboseReporter.WriteLine($"GC: Keeping workload set version {installState.WorkloadSetVersion} because it is specified in the install state file {installStateFilePath}"); + WorkloadSetsToKeep.Add(installState.WorkloadVersion); + _verboseReporter.WriteLine($"GC: Keeping workload set version {installState.WorkloadVersion} because it is specified in the install state file {installStateFilePath}"); } } else diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadInstallCommand.cs b/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadInstallCommand.cs index b5aa34015e8c..7025e5a5bf2b 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadInstallCommand.cs +++ b/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadInstallCommand.cs @@ -43,6 +43,14 @@ public WorkloadInstallCommand( _workloadManifestUpdater = _workloadManifestUpdaterFromConstructor ?? new WorkloadManifestUpdater(Reporter, _workloadResolver, PackageDownloader, _userProfileDir, _workloadInstaller.GetWorkloadInstallationRecordRepository(), _workloadInstaller, _packageSourceLocation, displayManifestUpdates: Verbosity.IsDetailedOrDiagnostic()); + _workloadSetVersion = parseResult.GetValue(InstallingWorkloadCommandParser.WorkloadSetVersionOption); + if (string.IsNullOrWhiteSpace(_workloadSetVersion)) + { + // If the version of the workload set is currently pinned, treat it as if it were freshly pinned. + var installStateContents = InstallStateContents.FromPath(Path.Combine(WorkloadInstallType.GetInstallStateFolder(_sdkFeatureBand, _dotnetPath), "default.json")); + _workloadSetVersion = installStateContents.WorkloadVersion; + } + ValidateWorkloadIdsInput(); } @@ -107,11 +115,24 @@ public override int Execute() { try { - InstallWorkloads( - _workloadIds.Select(id => new WorkloadId(id)), - _skipManifestUpdate, - _includePreviews, - string.IsNullOrWhiteSpace(_fromCacheOption) ? null : new DirectoryPath(_fromCacheOption)); + DirectoryPath? offlineCache = string.IsNullOrWhiteSpace(_fromCacheOption) ? null : new DirectoryPath(_fromCacheOption); + var workloadIds = _workloadIds.Select(id => new WorkloadId(id)); + if (string.IsNullOrWhiteSpace(_workloadSetVersion)) + { + InstallWorkloads( + workloadIds, + _skipManifestUpdate, + _includePreviews, + offlineCache); + } + else + { + RunInNewTransaction(context => + { + var manifests = HandleWorkloadUpdateFromVersion(context, offlineCache); + InstallWorkloadsAndGarbageCollect(context, workloadIds, manifests, offlineCache, false); + }); + } } catch (Exception e) { @@ -144,32 +165,46 @@ public void InstallWorkloads(IEnumerable workloadIds, bool skipManif } } - if (!skipManifestUpdate) + RunInNewTransaction(context => { - if (Verbosity != VerbosityOptions.quiet && Verbosity != VerbosityOptions.q) + if (!skipManifestUpdate) { - Reporter.WriteLine(LocalizableStrings.CheckForUpdatedWorkloadManifests); - } + if (Verbosity != VerbosityOptions.quiet && Verbosity != VerbosityOptions.q) + { + Reporter.WriteLine(LocalizableStrings.CheckForUpdatedWorkloadManifests); + } + // Add workload Ids that already exist to our collection to later trigger an update in those installed workloads + var installedWorkloads = _workloadInstaller.GetWorkloadInstallationRecordRepository().GetInstalledWorkloads(_sdkFeatureBand); + var previouslyInstalledWorkloads = installedWorkloads.Intersect(workloadIds); + if (previouslyInstalledWorkloads.Any()) + { + Reporter.WriteLine(string.Format(LocalizableStrings.WorkloadAlreadyInstalled, string.Join(" ", previouslyInstalledWorkloads)).Yellow()); + } + workloadIds = workloadIds.Concat(installedWorkloads).Distinct(); - // Add workload Ids that already exist to our collection to later trigger an update in those installed workloads - var installedWorkloads = _workloadInstaller.GetWorkloadInstallationRecordRepository().GetInstalledWorkloads(_sdkFeatureBand); - var previouslyInstalledWorkloads = installedWorkloads.Intersect(workloadIds); - if (previouslyInstalledWorkloads.Any()) - { - Reporter.WriteLine(string.Format(LocalizableStrings.WorkloadAlreadyInstalled, string.Join(" ", previouslyInstalledWorkloads)).Yellow()); - } + var useWorkloadSets = ShouldUseWorkloadSetMode(_sdkFeatureBand, _dotnetPath); + useRollback = !string.IsNullOrWhiteSpace(_fromRollbackDefinition); - workloadIds = workloadIds.Concat(installedWorkloads).Distinct(); + _workloadManifestUpdater.UpdateAdvertisingManifestsAsync(includePreviews, useWorkloadSets, offlineCache).Wait(); - useRollback = !string.IsNullOrWhiteSpace(_fromRollbackDefinition); + if (useWorkloadSets) + { + manifestsToUpdate = InstallWorkloadSet(context); + } + else + { + manifestsToUpdate = useRollback ? _workloadManifestUpdater.CalculateManifestRollbacks(_fromRollbackDefinition) : + _workloadManifestUpdater.CalculateManifestUpdates().Select(m => m.ManifestUpdate); + } + } - _workloadManifestUpdater.UpdateAdvertisingManifestsAsync(includePreviews, offlineCache).Wait(); - manifestsToUpdate = useRollback ? - _workloadManifestUpdater.CalculateManifestRollbacks(_fromRollbackDefinition) : - _workloadManifestUpdater.CalculateManifestUpdates().Select(m => m.ManifestUpdate); - } + InstallWorkloadsAndGarbageCollect(context, workloadIds, manifestsToUpdate, offlineCache, useRollback); + }); + } - InstallWorkloadsWithInstallRecord(_workloadInstaller, workloadIds, _sdkFeatureBand, manifestsToUpdate, offlineCache, useRollback); + private void InstallWorkloadsAndGarbageCollect(ITransactionContext context, IEnumerable workloadIds, IEnumerable manifestsToUpdate, DirectoryPath? offlineCache, bool useRollback) + { + InstallWorkloadsWithInstallRecord(context, _workloadInstaller, workloadIds, _sdkFeatureBand, manifestsToUpdate, offlineCache, useRollback); TryRunGarbageCollection(_workloadInstaller, Reporter, Verbosity, workloadSetVersion => _workloadResolverFactory.CreateForWorkloadSet(_dotnetPath, _sdkVersion.ToString(), _userProfileDir, workloadSetVersion), offlineCache); @@ -204,6 +239,7 @@ private void WriteSDKInstallRecordsForVSWorkloads() } private void InstallWorkloadsWithInstallRecord( + ITransactionContext context, IInstaller installer, IEnumerable workloadIds, SdkFeatureBand sdkFeatureBand, @@ -214,15 +250,8 @@ private void InstallWorkloadsWithInstallRecord( IEnumerable workloadPackToInstall = new List(); IEnumerable newWorkloadInstallRecords = new List(); - var transaction = new CliTransaction - { - RollbackStarted = () => Reporter.WriteLine(LocalizableStrings.RollingBackInstall), - // Don't hide the original error if roll back fails, but do log the rollback failure - RollbackFailed = ex => Reporter.WriteLine(string.Format(LocalizableStrings.RollBackFailedMessage, ex.Message)) - }; - - transaction.Run( - action: context => + context.Run( + action: () => { bool rollback = !string.IsNullOrWhiteSpace(_fromRollbackDefinition); @@ -236,6 +265,8 @@ private void InstallWorkloadsWithInstallRecord( installer.SaveInstallStateManifestVersions(sdkFeatureBand, GetInstallStateContents(manifestsToUpdate)); } + installer.AdjustWorkloadSetInInstallState(sdkFeatureBand, string.IsNullOrWhiteSpace(_workloadSetVersion) ? null : _workloadSetVersion); + _workloadResolver.RefreshWorkloadManifests(); installer.InstallWorkloads(workloadIds, sdkFeatureBand, context, offlineCache); @@ -246,13 +277,11 @@ private void InstallWorkloadsWithInstallRecord( { recordRepo.WriteWorkloadInstallationRecord(workloadId, sdkFeatureBand); } - }, rollback: () => { // InstallWorkloadManifest and InstallWorkloadPacks already handle rolling back their actions, so here we only // need to delete the installation records - foreach (var workloadId in newWorkloadInstallRecords) { installer.GetWorkloadInstallationRecordRepository() @@ -282,5 +311,16 @@ private Task DownloadToOfflineCacheAsync(IEnumerable workloadIds, Di { return GetDownloads(workloadIds, skipManifestUpdate, includePreviews, offlineCache.Value); } + + private void RunInNewTransaction(Action a) + { + var transaction = new CliTransaction() + { + RollbackStarted = () => Reporter.WriteLine(LocalizableStrings.RollingBackInstall), + // Don't hide the original error if roll back fails, but do log the rollback failure + RollbackFailed = ex => Reporter.WriteLine(string.Format(LocalizableStrings.RollBackFailedMessage, ex.Message)) + }; + transaction.Run(context => a(context)); + } } } diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadInstallCommandParser.cs b/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadInstallCommandParser.cs index a7bbd03af121..f0323853dd79 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadInstallCommandParser.cs +++ b/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadInstallCommandParser.cs @@ -61,6 +61,7 @@ internal static void AddWorkloadInstallCommandOptions(CliCommand command) command.AddWorkloadCommandNuGetRestoreActionConfigOptions(); command.Options.Add(CommonOptions.VerbosityOption); command.Options.Add(SkipSignCheckOption); + command.Options.Add(InstallingWorkloadCommandParser.WorkloadSetVersionOption); } } } diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadManifestUpdater.cs b/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadManifestUpdater.cs index a7fe27c39f50..bb91d359cdab 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadManifestUpdater.cs +++ b/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadManifestUpdater.cs @@ -11,6 +11,7 @@ using Microsoft.Extensions.EnvironmentAbstractions; using Microsoft.NET.Sdk.WorkloadManifestReader; using NuGet.Common; +using NuGet.Packaging; using NuGet.Versioning; namespace Microsoft.DotNet.Workloads.Workload.Install @@ -72,12 +73,25 @@ private static WorkloadManifestUpdater GetInstance(string userProfileDir) return new WorkloadManifestUpdater(reporter, workloadResolver, nugetPackageDownloader, userProfileDir, workloadRecordRepo, installer); } - public async Task UpdateAdvertisingManifestsAsync(bool includePreviews, DirectoryPath? offlineCache = null) + public async Task UpdateAdvertisingManifestsAsync(bool includePreviews, bool useWorkloadSets = false, DirectoryPath? offlineCache = null) { - // this updates all the manifests - var manifests = _workloadResolver.GetInstalledManifests(); - await Task.WhenAll(manifests.Select(manifest => UpdateAdvertisingManifestAsync(manifest, includePreviews, offlineCache))).ConfigureAwait(false); - WriteUpdatableWorkloadsFile(); + if (useWorkloadSets) + { + await UpdateManifestWithVersionAsync("Microsoft.NET.Workloads", includePreviews, _sdkFeatureBand, null, offlineCache); + } + else + { + // this updates all the manifests + var manifests = _workloadResolver.GetInstalledManifests(); + await Task.WhenAll(manifests.Select(manifest => UpdateAdvertisingManifestAsync(manifest, includePreviews, offlineCache))).ConfigureAwait(false); + WriteUpdatableWorkloadsFile(); + } + } + + public async void DownloadWorkloadSet(string version, DirectoryPath? offlineCache = null) + { + var correctedVersion = WorkloadSetVersionToWorkloadSetPackageVersion(version); + await UpdateManifestWithVersionAsync("Microsoft.NET.Workloads", includePreviews: true, _sdkFeatureBand, new NuGetVersion(correctedVersion), offlineCache); } public async static Task BackgroundUpdateAdvertisingManifestsAsync(string userProfileDir) @@ -99,7 +113,7 @@ public async Task BackgroundUpdateAdvertisingManifestsWhenRequiredAsync() AdManifestSentinelIsDueForUpdate() && UpdatedAdManifestPackagesExistAsync().GetAwaiter().GetResult()) { - await UpdateAdvertisingManifestsAsync(false); + await UpdateAdvertisingManifestsAsync(false, ShouldUseWorkloadSetMode(_sdkFeatureBand, _userProfileDir)); var sentinelPath = GetAdvertisingManifestSentinelPath(_sdkFeatureBand); if (File.Exists(sentinelPath)) { @@ -112,6 +126,13 @@ public async Task BackgroundUpdateAdvertisingManifestsWhenRequiredAsync() } } + public static bool ShouldUseWorkloadSetMode(SdkFeatureBand sdkFeatureBand, string dotnetDir) + { + string path = Path.Combine(WorkloadInstallType.GetInstallStateFolder(sdkFeatureBand, dotnetDir), "default.json"); + var installStateContents = File.Exists(path) ? InstallStateContents.FromString(File.ReadAllText(path)) : new InstallStateContents(); + return installStateContents.UseWorkloadSets ?? false; + } + private void WriteUpdatableWorkloadsFile() { var installedWorkloads = _workloadRecordRepo.GetInstalledWorkloads(_sdkFeatureBand); @@ -134,6 +155,22 @@ public void DeleteUpdatableWorkloadsFile() } } + public static string WorkloadSetVersionToWorkloadSetPackageVersion(string setVersion) + { + var nugetVersion = new NuGetVersion(setVersion); + var patch = nugetVersion.Revision; + var release = string.IsNullOrWhiteSpace(nugetVersion.Release) ? string.Empty : $"-{nugetVersion.Release}"; + return $"{nugetVersion.Major}.{nugetVersion.Patch}.{patch}{release}"; + } + + public static string WorkloadSetPackageVersionToWorkloadSetVersion(SdkFeatureBand sdkFeatureBand, string packageVersion) + { + var nugetVersion = new NuGetVersion(packageVersion); + var patch = nugetVersion.Patch > 0 ? $".{nugetVersion.Patch}" : string.Empty; + var release = string.IsNullOrWhiteSpace(nugetVersion.Release) ? string.Empty : $"-{nugetVersion.Release}"; + return $"{sdkFeatureBand.Major}.{sdkFeatureBand.Minor}.{nugetVersion.Minor}{patch}{release}"; + } + public static void AdvertiseWorkloadUpdates() { try @@ -196,7 +233,7 @@ public IEnumerable GetUpdatableWorkloadsToAdvertise(IEnumerable CalculateManifestRollbacks(string rollbackDefinitionFilePath) { var currentManifestIds = GetInstalledManifestIds(); - var manifestRollbacks = ParseRollbackDefinitionFile(rollbackDefinitionFilePath); + var manifestRollbacks = ParseRollbackDefinitionFile(rollbackDefinitionFilePath, _sdkFeatureBand); var unrecognizedManifestIds = manifestRollbacks.Where(rollbackManifest => !currentManifestIds.Contains(rollbackManifest.Id)); if (unrecognizedManifestIds.Any()) @@ -205,7 +242,12 @@ public IEnumerable CalculateManifestRollbacks(string roll manifestRollbacks = manifestRollbacks.Where(rollbackManifest => currentManifestIds.Contains(rollbackManifest.Id)); } - var manifestUpdates = manifestRollbacks.Select(manifest => + return CalculateManifestRollbacks(manifestRollbacks); + } + + private IEnumerable CalculateManifestRollbacks(IEnumerable<(ManifestId Id, ManifestVersionWithBand ManifestWithBand)> versionUpdates) + { + var manifestUpdates = versionUpdates.Select(manifest => { var (id, (version, band)) = manifest; var (installedVersion, installedBand) = GetInstalledManifestVersion(id); @@ -261,64 +303,60 @@ public async Task> GetManifestPackageDownloadsAsyn private IEnumerable GetInstalledManifestIds() => _workloadResolver.GetInstalledManifests().Select(manifest => new ManifestId(manifest.Id)); - private async Task UpdateAdvertisingManifestAsync(WorkloadManifestInfo manifest, bool includePreviews, DirectoryPath? offlineCache = null) + private async Task UpdateManifestWithVersionAsync(string id, bool includePreviews, SdkFeatureBand band, NuGetVersion packageVersion = null, DirectoryPath? offlineCache = null) { + var manifestId = new ManifestId(id); string packagePath = null; - var manifestId = new ManifestId(manifest.Id); try { - SdkFeatureBand? currentFeatureBand = null; - var fallbackFeatureBand = new SdkFeatureBand(manifest.ManifestFeatureBand); - // The bands should be checked in the order defined here. - SdkFeatureBand[] bands = [_sdkFeatureBand, fallbackFeatureBand]; - var success = false; - // Use Distinct to eliminate bands that are the same. - foreach (var band in bands.Distinct()) + var manifestPackageId = _workloadManifestInstaller.GetManifestPackageId(manifestId, band); + try + { + // If an offline cache is present, use that. Otherwise, try to acquire the package online. + packagePath = offlineCache != null ? + Directory.GetFiles(offlineCache.Value.Value) + .Where(path => + path.EndsWith(".nupkg") && + Path.GetFileName(path).StartsWith(manifestPackageId.ToString(), StringComparison.OrdinalIgnoreCase) && + (packageVersion == null || path.Contains(packageVersion.ToString()))) + .Max() : + await _nugetPackageDownloader.DownloadPackageAsync(manifestPackageId, packageVersion: packageVersion, packageSourceLocation: _packageSourceLocation, includePreview: includePreviews); + } + catch (NuGetPackageNotFoundException) { - var manifestPackageId = _workloadManifestInstaller.GetManifestPackageId(manifestId, band); - currentFeatureBand = band; - - try - { - // If an offline cache is present, use that. Otherwise, try to acquire the package online. - packagePath = offlineCache != null ? - Directory.GetFiles(offlineCache.Value.Value) - .Where(path => path.EndsWith(".nupkg") && Path.GetFileName(path).StartsWith(manifestPackageId.ToString(), StringComparison.OrdinalIgnoreCase)) - .Max() : - await _nugetPackageDownloader.DownloadPackageAsync(manifestPackageId, packageSourceLocation: _packageSourceLocation, includePreview: includePreviews); - - if (packagePath != null) - { - success = true; - break; - } - } - catch (NuGetPackageNotFoundException) - { - } } - if (!success) + if (packagePath is null) { - _reporter.WriteLine(LocalizableStrings.AdManifestPackageDoesNotExist, manifestId); - return; + return false; } var adManifestPath = GetAdvertisingManifestPath(_sdkFeatureBand, manifestId); await _workloadManifestInstaller.ExtractManifestAsync(packagePath, adManifestPath); // add file that contains the advertised manifest feature band so GetAdvertisingManifestVersionAndWorkloads will use correct feature band, regardless of if rollback occurred or not - File.WriteAllText(Path.Combine(adManifestPath, "AdvertisedManifestFeatureBand.txt"), currentFeatureBand.ToString()); + File.WriteAllText(Path.Combine(adManifestPath, "AdvertisedManifestFeatureBand.txt"), band.ToString()); + + if (id.Equals("Microsoft.NET.Workloads")) + { + // Create version file later used as part of installing the workload set in the file-based installer and in the msi-based installer + using PackageArchiveReader packageReader = new(packagePath); + var downloadedPackageVersion = packageReader.NuspecReader.GetVersion(); + var workloadSetVersion = WorkloadSetPackageVersionToWorkloadSetVersion(_sdkFeatureBand, downloadedPackageVersion.ToString()); + File.WriteAllText(Path.Combine(adManifestPath, Constants.workloadSetVersionFileName), workloadSetVersion); + } if (_displayManifestUpdates) { _reporter.WriteLine(LocalizableStrings.AdManifestUpdated, manifestId); } + return true; } catch (Exception e) { _reporter.WriteLine(LocalizableStrings.FailedAdManifestUpdate, manifestId, e.Message); + return false; } finally { @@ -343,6 +381,22 @@ private async Task UpdateAdvertisingManifestAsync(WorkloadManifestInfo manifest, } } + private async Task UpdateAdvertisingManifestAsync(WorkloadManifestInfo manifest, bool includePreviews, DirectoryPath? offlineCache = null) + { + var fallbackFeatureBand = new SdkFeatureBand(manifest.ManifestFeatureBand); + // The bands should be checked in the order defined here. + SdkFeatureBand[] bands = [_sdkFeatureBand, fallbackFeatureBand]; + foreach (var band in bands.Distinct()) + { + if (await UpdateManifestWithVersionAsync(manifest.Id, includePreviews, band, null, offlineCache)) + { + return; + } + } + + _reporter.WriteLine(LocalizableStrings.AdManifestPackageDoesNotExist, manifest.Id); + } + private (ManifestVersionWithBand ManifestWithBand, WorkloadCollection Workloads)? GetAdvertisingManifestVersionAndWorkloads(ManifestId manifestId) { var manifestPath = Path.Combine(GetAdvertisingManifestPath(_sdkFeatureBand, manifestId), "WorkloadManifest.json"); @@ -419,7 +473,41 @@ private async Task NewerManifestPackageExists(ManifestId manifest) } } - private IEnumerable<(ManifestId Id, ManifestVersionWithBand ManifestWithBand)> ParseRollbackDefinitionFile(string rollbackDefinitionFilePath) + public IEnumerable ParseRollbackDefinitionFiles(IEnumerable rollbackFilePaths) + { + if (rollbackFilePaths.Count() == 1) + { + return CalculateManifestRollbacks(rollbackFilePaths.Single()); + } + + var currentManifestIds = GetInstalledManifestIds(); + // Create a single workload set that includes all the others + List<(ManifestId, ManifestVersionWithBand)> fullSet = new(); + foreach (var rollbackFile in rollbackFilePaths) + { + var rollbacks = ParseRollbackDefinitionFile(rollbackFile, _sdkFeatureBand); + + var unrecognizedManifestIds = rollbacks.Where(rollbackManifest => !currentManifestIds.Contains(rollbackManifest.Id)); + if (unrecognizedManifestIds.Any()) + { + _reporter.WriteLine(string.Format(LocalizableStrings.RollbackDefinitionContainsExtraneousManifestIds, rollbackFile, string.Join(" ", unrecognizedManifestIds)).Yellow()); + rollbacks = rollbacks.Where(rollbackManifest => currentManifestIds.Contains(rollbackManifest.Id)); + } + + fullSet.AddRange(rollbacks); + } + + var reducedFullSet = fullSet.DistinctBy<(ManifestId, ManifestVersionWithBand), ManifestId>(update => update.Item1).ToList(); + if (fullSet.Count != reducedFullSet.Count) + { + var duplicates = reducedFullSet.Where(manifest => fullSet.Where(m => m.Item1.Equals(manifest.Item1)).Count() > 1); + throw new ArgumentException("There were duplicates of the following manifests between the workload set files: " + string.Join(", ", duplicates)); + } + + return CalculateManifestRollbacks(fullSet); + } + + private static IEnumerable<(ManifestId Id, ManifestVersionWithBand ManifestWithBand)> ParseRollbackDefinitionFile(string rollbackDefinitionFilePath, SdkFeatureBand featureBand) { string fileContent; @@ -439,7 +527,7 @@ private async Task NewerManifestPackageExists(ManifestId manifest) } } - var versions = WorkloadSet.FromJson(fileContent, _sdkFeatureBand).ManifestVersions; + var versions = WorkloadSet.FromJson(fileContent, featureBand).ManifestVersions; return versions.Select(kvp => (kvp.Key, new ManifestVersionWithBand(kvp.Value.Version, kvp.Value.FeatureBand))); } diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.cs.xlf b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.cs.xlf index 720a2aefe836..16d19acff6fe 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.cs.xlf +++ b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.cs.xlf @@ -182,6 +182,11 @@ Odebírání {0}. + + Installing workload version {0}. + Installing workload version {0}. + + Failed to establish a trust relationship with parent process ({0}). Nepovedlo se navázat vztah důvěryhodnosti s nadřazeným procesem ({0}). @@ -367,6 +372,16 @@ Určete, jestli by budoucí operace úloh měly používat sady úloh nebo volné manifesty. + + Updating workload version from {0} to {1}. + Updating workload version from {0} to {1}. + + + + Update to the specified workload version. + Update to the specified workload version. + + Workload updates are available. Run `dotnet workload list` for more information. Jsou k dispozici aktualizace úloh. Pokud chcete získat další informace, spusťte `dotnet workload list`. diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.de.xlf b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.de.xlf index f412e4e21584..3684392e233f 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.de.xlf +++ b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.de.xlf @@ -182,6 +182,11 @@ {0} wird entfernt + + Installing workload version {0}. + Installing workload version {0}. + + Failed to establish a trust relationship with parent process ({0}). Fehler beim Einrichten einer Vertrauensbeziehung mit dem übergeordneten Prozess ({0}). @@ -367,6 +372,16 @@ Hiermit wird gesteuert, ob zukünftige Workloadvorgänge Workloadsätze oder lose Manifeste verwenden sollen. + + Updating workload version from {0} to {1}. + Updating workload version from {0} to {1}. + + + + Update to the specified workload version. + Update to the specified workload version. + + Workload updates are available. Run `dotnet workload list` for more information. Es sind Workloadupdates verfügbar. Um weitere Informationen zu erhalten, führen Sie `dotnet workload list` aus. diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.es.xlf b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.es.xlf index 661379832a29..b85299b2a989 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.es.xlf +++ b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.es.xlf @@ -182,6 +182,11 @@ Quitando {0} + + Installing workload version {0}. + Installing workload version {0}. + + Failed to establish a trust relationship with parent process ({0}). No se ha podido establecer una relación de confianza con el proceso primario ({0}). @@ -367,6 +372,16 @@ Controle si las operaciones de carga de trabajo futuras deben usar conjuntos de cargas de trabajo o manifiestos flexibles. + + Updating workload version from {0} to {1}. + Updating workload version from {0} to {1}. + + + + Update to the specified workload version. + Update to the specified workload version. + + Workload updates are available. Run `dotnet workload list` for more information. Hay actualizaciones de carga de trabajo disponibles. Ejecute "dotnet workload list" para obtener más información. diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.fr.xlf b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.fr.xlf index 1462fa793077..22f5c5ee64e0 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.fr.xlf +++ b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.fr.xlf @@ -182,6 +182,11 @@ Suppression de {0} + + Installing workload version {0}. + Installing workload version {0}. + + Failed to establish a trust relationship with parent process ({0}). Impossible d’établir une relation de confiance avec le processus parent ({0}). @@ -367,6 +372,16 @@ Contrôlez si les futures opérations de charge de travail doivent utiliser des ensembles de charges de travail ou des manifestes lâches. + + Updating workload version from {0} to {1}. + Updating workload version from {0} to {1}. + + + + Update to the specified workload version. + Update to the specified workload version. + + Workload updates are available. Run `dotnet workload list` for more information. Des mises à jour de la charge de travail sont disponibles. Exécutez `dotnet workload list` pour plus d’informations. diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.it.xlf b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.it.xlf index 2f9f807b9fa6..0f2b45bf6774 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.it.xlf +++ b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.it.xlf @@ -182,6 +182,11 @@ Rimozione di {0} + + Installing workload version {0}. + Installing workload version {0}. + + Failed to establish a trust relationship with parent process ({0}). Impossibile stabilire una relazione di attendibilità con il processo padre ({0}). @@ -367,6 +372,16 @@ Controllare se le operazioni future del carico di lavoro devono usare set di carichi di lavoro o manifesti separati. + + Updating workload version from {0} to {1}. + Updating workload version from {0} to {1}. + + + + Update to the specified workload version. + Update to the specified workload version. + + Workload updates are available. Run `dotnet workload list` for more information. Sono disponibili aggiornamenti del carico di lavoro. Per altre informazioni, eseguire `dotnet workload list`. diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.ja.xlf b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.ja.xlf index 93aa4172e61b..029a1695d244 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.ja.xlf +++ b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.ja.xlf @@ -182,6 +182,11 @@ {0} を削除しています + + Installing workload version {0}. + Installing workload version {0}. + + Failed to establish a trust relationship with parent process ({0}). 親プロセス ({0}) との信頼関係を確立できませんでした。 @@ -367,6 +372,16 @@ 将来のワークロード操作でワークロード セットを使用するか、ルーズ マニフェストを使用するかを制御します。 + + Updating workload version from {0} to {1}. + Updating workload version from {0} to {1}. + + + + Update to the specified workload version. + Update to the specified workload version. + + Workload updates are available. Run `dotnet workload list` for more information. ワークロードの更新が利用可能です。詳細については、`dotnet workload list` を実行してください。 diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.ko.xlf b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.ko.xlf index dcfa49035630..928954891bfd 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.ko.xlf +++ b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.ko.xlf @@ -182,6 +182,11 @@ {0} 제거 중 + + Installing workload version {0}. + Installing workload version {0}. + + Failed to establish a trust relationship with parent process ({0}). 부모 프로세스({0})와 신뢰 관계를 설정하지 못했습니다. @@ -367,6 +372,16 @@ 향후 워크로드 작업에서 워크로드 집합을 사용할지, 매니페스트를 완화할지를 제어합니다. + + Updating workload version from {0} to {1}. + Updating workload version from {0} to {1}. + + + + Update to the specified workload version. + Update to the specified workload version. + + Workload updates are available. Run `dotnet workload list` for more information. 워크로드 업데이트를 사용할 수 있습니다. 자세한 내용을 보려면 `dotnet workload list`을 실행하세요. diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.pl.xlf b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.pl.xlf index 6325acdc4b93..b2fc232366d5 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.pl.xlf +++ b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.pl.xlf @@ -182,6 +182,11 @@ Usuwanie {0} + + Installing workload version {0}. + Installing workload version {0}. + + Failed to establish a trust relationship with parent process ({0}). Nie można ustanowić relacji zaufania z procesem nadrzędnym ({0}). @@ -367,6 +372,16 @@ Określ, czy przyszłe operacje związane z obciążeniami powinny wykorzystywać zestawy obciążeń, czy luźne manifesty. + + Updating workload version from {0} to {1}. + Updating workload version from {0} to {1}. + + + + Update to the specified workload version. + Update to the specified workload version. + + Workload updates are available. Run `dotnet workload list` for more information. Dostępne są aktualizacje obciążenia. Uruchom polecenie `dotnet workload list`, aby uzyskać więcej informacji. diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.pt-BR.xlf b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.pt-BR.xlf index 8c03cca867d1..f28bacbe2588 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.pt-BR.xlf +++ b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.pt-BR.xlf @@ -182,6 +182,11 @@ Removendo {0} + + Installing workload version {0}. + Installing workload version {0}. + + Failed to establish a trust relationship with parent process ({0}). Falha ao estabelecer uma relação de confiança com o processo pai ({0}). @@ -367,6 +372,16 @@ Controle se as operações de carga de trabalho futuras devem usar conjuntos de carga de trabalho ou manifestos flexíveis. + + Updating workload version from {0} to {1}. + Updating workload version from {0} to {1}. + + + + Update to the specified workload version. + Update to the specified workload version. + + Workload updates are available. Run `dotnet workload list` for more information. As atualizações de carga de trabalho estão disponíveis. Execute `dotnet workload list` para obter mais informações. diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.ru.xlf b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.ru.xlf index b4940992ccc1..ed236e9b4434 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.ru.xlf +++ b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.ru.xlf @@ -182,6 +182,11 @@ Идет удаление {0} + + Installing workload version {0}. + Installing workload version {0}. + + Failed to establish a trust relationship with parent process ({0}). Не удалось установить отношение доверия с родительским процессом ({0}). @@ -367,6 +372,16 @@ Укажите, должны ли будущие операции рабочей нагрузки использовать наборы рабочей нагрузки или свободные манифесты. + + Updating workload version from {0} to {1}. + Updating workload version from {0} to {1}. + + + + Update to the specified workload version. + Update to the specified workload version. + + Workload updates are available. Run `dotnet workload list` for more information. Доступны обновления рабочей нагрузки. Для получения дополнительных сведений запустите `dotnet workload list`. diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.tr.xlf b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.tr.xlf index 3691d8eb871c..8bfc444dd456 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.tr.xlf +++ b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.tr.xlf @@ -182,6 +182,11 @@ {0} kaldırılıyor + + Installing workload version {0}. + Installing workload version {0}. + + Failed to establish a trust relationship with parent process ({0}). Üst işlemle ({0}) bir güven ilişkisi kurulamadı. @@ -367,6 +372,16 @@ Gelecekteki iş yükü işlemlerinin iş yükü kümelerini mi yoksa gevşek bildirimleri mi kullanması gerektiğini kontrol edin. + + Updating workload version from {0} to {1}. + Updating workload version from {0} to {1}. + + + + Update to the specified workload version. + Update to the specified workload version. + + Workload updates are available. Run `dotnet workload list` for more information. İş yükü güncelleştirmeleri var. Daha fazla bilgi için `dotnet workload list` çalıştırın. diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.zh-Hans.xlf b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.zh-Hans.xlf index 60dce47d2925..afc5f7b4f3d7 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.zh-Hans.xlf +++ b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.zh-Hans.xlf @@ -182,6 +182,11 @@ 正在删除 {0} + + Installing workload version {0}. + Installing workload version {0}. + + Failed to establish a trust relationship with parent process ({0}). 未能建立与父进程({0})之间的信任关系。 @@ -367,6 +372,16 @@ 控制未来的工作负载操作应该使用工作负载集还是松散清单。 + + Updating workload version from {0} to {1}. + Updating workload version from {0} to {1}. + + + + Update to the specified workload version. + Update to the specified workload version. + + Workload updates are available. Run `dotnet workload list` for more information. 有可用的工作负载更新。有关详细信息,请运行 `dotnet workload list`。 diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.zh-Hant.xlf b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.zh-Hant.xlf index 4921d99e1aab..b1c547749558 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.zh-Hant.xlf +++ b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.zh-Hant.xlf @@ -182,6 +182,11 @@ 正在移除 {0} + + Installing workload version {0}. + Installing workload version {0}. + + Failed to establish a trust relationship with parent process ({0}). 無法與父處理序 ({0}) 建立信任關係。 @@ -367,6 +372,16 @@ 控制未來的工作負載作業應該使用工作負載集合還是鬆散資訊清單。 + + Updating workload version from {0} to {1}. + Updating workload version from {0} to {1}. + + + + Update to the specified workload version. + Update to the specified workload version. + + Workload updates are available. Run `dotnet workload list` for more information. 有可用的工作負載更新。如需詳細資訊,請執行 `dotnet workload list`。 diff --git a/src/Cli/dotnet/commands/dotnet-workload/list/LocalizableStrings.resx b/src/Cli/dotnet/commands/dotnet-workload/list/LocalizableStrings.resx index b7a929ae43e1..dbb30fdcd881 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/list/LocalizableStrings.resx +++ b/src/Cli/dotnet/commands/dotnet-workload/list/LocalizableStrings.resx @@ -128,4 +128,7 @@ Use `dotnet workload search` to find additional workloads to install. {Locked="dotnet workload search"} + + Workload version: {0} + \ No newline at end of file diff --git a/src/Cli/dotnet/commands/dotnet-workload/list/WorkloadListCommand.cs b/src/Cli/dotnet/commands/dotnet-workload/list/WorkloadListCommand.cs index 2a3d861b517e..93dcc18f90f3 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/list/WorkloadListCommand.cs +++ b/src/Cli/dotnet/commands/dotnet-workload/list/WorkloadListCommand.cs @@ -20,7 +20,7 @@ internal class WorkloadListCommand : WorkloadCommandBase private readonly bool _includePreviews; private readonly bool _machineReadableOption; private readonly IWorkloadManifestUpdater _workloadManifestUpdater; - private readonly IWorkloadInfoHelper _workloadListHelper; + private readonly WorkloadInfoHelper _workloadListHelper; public WorkloadListCommand( ParseResult parseResult, @@ -93,6 +93,13 @@ public override int Execute() table.PrintRows(installedWorkloads.AsEnumerable(), l => Reporter.WriteLine(l)); + var installState = InstallStateContents.FromPath(Path.Combine(WorkloadInstallType.GetInstallStateFolder(_workloadListHelper._currentSdkFeatureBand, _workloadListHelper.DotnetPath), "default.json")); + if (installState.UseWorkloadSets == true) + { + Reporter.WriteLine(); + Reporter.WriteLine(string.Format(LocalizableStrings.WorkloadSetVersion, _workloadListHelper.WorkloadResolver.GetWorkloadVersion() ?? "unknown")); + } + Reporter.WriteLine(); Reporter.WriteLine(LocalizableStrings.WorkloadListFooter); Reporter.WriteLine(); @@ -110,6 +117,7 @@ public override int Execute() internal IEnumerable GetUpdateAvailable(IEnumerable installedList) { + // This was an internal partner ask, and they do not need to support workload sets. _workloadManifestUpdater.UpdateAdvertisingManifestsAsync(_includePreviews).Wait(); var manifestsToUpdate = _workloadManifestUpdater.CalculateManifestUpdates(); diff --git a/src/Cli/dotnet/commands/dotnet-workload/list/xlf/LocalizableStrings.cs.xlf b/src/Cli/dotnet/commands/dotnet-workload/list/xlf/LocalizableStrings.cs.xlf index dbc5c5d7eb39..66284edd36c8 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/list/xlf/LocalizableStrings.cs.xlf +++ b/src/Cli/dotnet/commands/dotnet-workload/list/xlf/LocalizableStrings.cs.xlf @@ -12,6 +12,11 @@ Pokud chcete najít další úlohy, které se mají nainstalovat, použijte `dotnet workload search`. {Locked="dotnet workload search"} + + Workload version: {0} + Workload version: {0} + + Updates are available for the following workload(s): {0}. Run `dotnet workload update` to get the latest. Aktualizace jsou k dispozici pro následující úlohy: {0}. Pokud chcete získat nejnovější verzi, spusťte aktualizaci úlohy dotnet (`dotnet workload update`). diff --git a/src/Cli/dotnet/commands/dotnet-workload/list/xlf/LocalizableStrings.de.xlf b/src/Cli/dotnet/commands/dotnet-workload/list/xlf/LocalizableStrings.de.xlf index 63bfadefbaed..df59aa34bd89 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/list/xlf/LocalizableStrings.de.xlf +++ b/src/Cli/dotnet/commands/dotnet-workload/list/xlf/LocalizableStrings.de.xlf @@ -12,6 +12,11 @@ Verwenden Sie „dotnet workload search“, um zusätzliche zu installierende Workloads zu finden. {Locked="dotnet workload search"} + + Workload version: {0} + Workload version: {0} + + Updates are available for the following workload(s): {0}. Run `dotnet workload update` to get the latest. Updates sind für die folgenden Workloads verfügbar: {0}. Führen Sie „dotnet workload update“ aus, um die neueste Updates zu erhalten. diff --git a/src/Cli/dotnet/commands/dotnet-workload/list/xlf/LocalizableStrings.es.xlf b/src/Cli/dotnet/commands/dotnet-workload/list/xlf/LocalizableStrings.es.xlf index 6a235d3dceb3..80f0a9398d5e 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/list/xlf/LocalizableStrings.es.xlf +++ b/src/Cli/dotnet/commands/dotnet-workload/list/xlf/LocalizableStrings.es.xlf @@ -12,6 +12,11 @@ Use "dotnet workload search" para buscar cargas de trabajo adicionales para instalar. {Locked="dotnet workload search"} + + Workload version: {0} + Workload version: {0} + + Updates are available for the following workload(s): {0}. Run `dotnet workload update` to get the latest. Hay actualizaciones disponibles para las siguientes cargas de trabajo: {0}. Ejecute "dotnet workload update" para obtener la versión más reciente. diff --git a/src/Cli/dotnet/commands/dotnet-workload/list/xlf/LocalizableStrings.fr.xlf b/src/Cli/dotnet/commands/dotnet-workload/list/xlf/LocalizableStrings.fr.xlf index 7bb2a04f586e..3f4c46ae97ca 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/list/xlf/LocalizableStrings.fr.xlf +++ b/src/Cli/dotnet/commands/dotnet-workload/list/xlf/LocalizableStrings.fr.xlf @@ -12,6 +12,11 @@ Utilisez `dotnet workload search` pour rechercher d’autres charges de travail à installer. {Locked="dotnet workload search"} + + Workload version: {0} + Workload version: {0} + + Updates are available for the following workload(s): {0}. Run `dotnet workload update` to get the latest. Des mises à jour sont disponibles pour les charges de travail suivantes : {0}. Exécutez `dotnet workload update` pour obtenir la dernière version. diff --git a/src/Cli/dotnet/commands/dotnet-workload/list/xlf/LocalizableStrings.it.xlf b/src/Cli/dotnet/commands/dotnet-workload/list/xlf/LocalizableStrings.it.xlf index 1cbcf50e4808..caa33f6a522b 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/list/xlf/LocalizableStrings.it.xlf +++ b/src/Cli/dotnet/commands/dotnet-workload/list/xlf/LocalizableStrings.it.xlf @@ -12,6 +12,11 @@ Utilizzare la `dotnet workload search` per trovare i carichi di lavoro aggiuntivi da installare. {Locked="dotnet workload search"} + + Workload version: {0} + Workload version: {0} + + Updates are available for the following workload(s): {0}. Run `dotnet workload update` to get the latest. Gli aggiornamenti sono disponibili per i carichi di lavoro seguenti: {0}. Per ottenere la versione più recente, eseguire 'dotnet workload update'. diff --git a/src/Cli/dotnet/commands/dotnet-workload/list/xlf/LocalizableStrings.ja.xlf b/src/Cli/dotnet/commands/dotnet-workload/list/xlf/LocalizableStrings.ja.xlf index 2b121428078f..58eaa7887ce4 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/list/xlf/LocalizableStrings.ja.xlf +++ b/src/Cli/dotnet/commands/dotnet-workload/list/xlf/LocalizableStrings.ja.xlf @@ -12,6 +12,11 @@ `dotnet workload search` を使用して追加ワークロードを検出し、インストールします。 {Locked="dotnet workload search"} + + Workload version: {0} + Workload version: {0} + + Updates are available for the following workload(s): {0}. Run `dotnet workload update` to get the latest. 次のワークロードについて更新プログラムを入手可能です: {0}。最新版を取得するには、`dotnet workload update` を実行します。 diff --git a/src/Cli/dotnet/commands/dotnet-workload/list/xlf/LocalizableStrings.ko.xlf b/src/Cli/dotnet/commands/dotnet-workload/list/xlf/LocalizableStrings.ko.xlf index bc25fba551b4..7b93e295c32c 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/list/xlf/LocalizableStrings.ko.xlf +++ b/src/Cli/dotnet/commands/dotnet-workload/list/xlf/LocalizableStrings.ko.xlf @@ -12,6 +12,11 @@ `dotnet workload search`을 사용하여 설치할 추가 워크로드를 찾습니다. {Locked="dotnet workload search"} + + Workload version: {0} + Workload version: {0} + + Updates are available for the following workload(s): {0}. Run `dotnet workload update` to get the latest. 다음 워크로드에 대한 업데이트를 사용할 수 있습니다. {0}. 최신 버전을 받으려면 `dotnet workload update`를 실행하세요. diff --git a/src/Cli/dotnet/commands/dotnet-workload/list/xlf/LocalizableStrings.pl.xlf b/src/Cli/dotnet/commands/dotnet-workload/list/xlf/LocalizableStrings.pl.xlf index d831a98b9807..6dab03cac48a 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/list/xlf/LocalizableStrings.pl.xlf +++ b/src/Cli/dotnet/commands/dotnet-workload/list/xlf/LocalizableStrings.pl.xlf @@ -12,6 +12,11 @@ Użyj polecenia „dotnet workload search”, aby znaleźć dodatkowe obciążenia do zainstalowania. {Locked="dotnet workload search"} + + Workload version: {0} + Workload version: {0} + + Updates are available for the following workload(s): {0}. Run `dotnet workload update` to get the latest. Aktualizacje są dostępne dla następujących obciążeń: {0}. Uruchom polecenie `dotnet workload update`, aby uzyskać najnowszą wersję. diff --git a/src/Cli/dotnet/commands/dotnet-workload/list/xlf/LocalizableStrings.pt-BR.xlf b/src/Cli/dotnet/commands/dotnet-workload/list/xlf/LocalizableStrings.pt-BR.xlf index b9246e9d8a9b..bfd92a1420d3 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/list/xlf/LocalizableStrings.pt-BR.xlf +++ b/src/Cli/dotnet/commands/dotnet-workload/list/xlf/LocalizableStrings.pt-BR.xlf @@ -12,6 +12,11 @@ Use `dotnet workload search` para encontrar cargas de trabalho adicionais a serem instaladas. {Locked="dotnet workload search"} + + Workload version: {0} + Workload version: {0} + + Updates are available for the following workload(s): {0}. Run `dotnet workload update` to get the latest. As atualizações estão disponíveis para as seguintes cargas de trabalho(s): {0}. Execute `dotnet workload update` para obter o mais recente. diff --git a/src/Cli/dotnet/commands/dotnet-workload/list/xlf/LocalizableStrings.ru.xlf b/src/Cli/dotnet/commands/dotnet-workload/list/xlf/LocalizableStrings.ru.xlf index 7ddcbf47db14..8001bc04e0f0 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/list/xlf/LocalizableStrings.ru.xlf +++ b/src/Cli/dotnet/commands/dotnet-workload/list/xlf/LocalizableStrings.ru.xlf @@ -12,6 +12,11 @@ Используйте `dotnet workload search`, чтобы найти дополнительные рабочие нагрузки для установки. {Locked="dotnet workload search"} + + Workload version: {0} + Workload version: {0} + + Updates are available for the following workload(s): {0}. Run `dotnet workload update` to get the latest. Обновления доступны для следующих рабочих нагрузок: {0}. Чтобы получить последнюю версию, запустите `dotnet workload update`. diff --git a/src/Cli/dotnet/commands/dotnet-workload/list/xlf/LocalizableStrings.tr.xlf b/src/Cli/dotnet/commands/dotnet-workload/list/xlf/LocalizableStrings.tr.xlf index 9ea3ecc06a29..3b0ac98ba2d2 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/list/xlf/LocalizableStrings.tr.xlf +++ b/src/Cli/dotnet/commands/dotnet-workload/list/xlf/LocalizableStrings.tr.xlf @@ -12,6 +12,11 @@ Yüklenecek ek iş yüklerini bulmak için `dotnet workload search` kullanın. {Locked="dotnet workload search"} + + Workload version: {0} + Workload version: {0} + + Updates are available for the following workload(s): {0}. Run `dotnet workload update` to get the latest. Şu iş yükleri için güncelleştirmeler var: {0}. En son sürümü almak için `dotnet workload update` çalıştırın. diff --git a/src/Cli/dotnet/commands/dotnet-workload/list/xlf/LocalizableStrings.zh-Hans.xlf b/src/Cli/dotnet/commands/dotnet-workload/list/xlf/LocalizableStrings.zh-Hans.xlf index ae6dab8c46d0..35e9756641e4 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/list/xlf/LocalizableStrings.zh-Hans.xlf +++ b/src/Cli/dotnet/commands/dotnet-workload/list/xlf/LocalizableStrings.zh-Hans.xlf @@ -12,6 +12,11 @@ 使用`dotnet workload search`查找要安装的其他工作负载。 {Locked="dotnet workload search"} + + Workload version: {0} + Workload version: {0} + + Updates are available for the following workload(s): {0}. Run `dotnet workload update` to get the latest. 以下工作负载有可用的更新: {0}。请运行 `dotnet workload update` 以获取最新版本。 diff --git a/src/Cli/dotnet/commands/dotnet-workload/list/xlf/LocalizableStrings.zh-Hant.xlf b/src/Cli/dotnet/commands/dotnet-workload/list/xlf/LocalizableStrings.zh-Hant.xlf index 8365be692fe5..424fdf614a55 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/list/xlf/LocalizableStrings.zh-Hant.xlf +++ b/src/Cli/dotnet/commands/dotnet-workload/list/xlf/LocalizableStrings.zh-Hant.xlf @@ -12,6 +12,11 @@ 使用 `dotnet workload search` 尋找其他要安裝的工作負載。 {Locked="dotnet workload search"} + + Workload version: {0} + Workload version: {0} + + Updates are available for the following workload(s): {0}. Run `dotnet workload update` to get the latest. 以下工作負載有可用的更新: {0}。執行 `dotnet workload update` 以取得最新更新。 diff --git a/src/Cli/dotnet/commands/dotnet-workload/update/LocalizableStrings.resx b/src/Cli/dotnet/commands/dotnet-workload/update/LocalizableStrings.resx index 77709f82d798..58a400e6ede6 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/update/LocalizableStrings.resx +++ b/src/Cli/dotnet/commands/dotnet-workload/update/LocalizableStrings.resx @@ -156,4 +156,7 @@ Update workloads based on specified rollback definition file. + + Updating to a rollback file is not compatible with workload sets. Install and Update will now use loose manifests. To update to a specific workload version, use --version. + \ No newline at end of file diff --git a/src/Cli/dotnet/commands/dotnet-workload/update/WorkloadUpdateCommand.cs b/src/Cli/dotnet/commands/dotnet-workload/update/WorkloadUpdateCommand.cs index bf09bce1112b..0d14b1fc84f7 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/update/WorkloadUpdateCommand.cs +++ b/src/Cli/dotnet/commands/dotnet-workload/update/WorkloadUpdateCommand.cs @@ -37,6 +37,7 @@ public WorkloadUpdateCommand( tempDirPath: tempDirPath) { + _workloadSetVersion = parseResult.GetValue(InstallingWorkloadCommandParser.WorkloadSetVersionOption); _fromPreviousSdk = parseResult.GetValue(WorkloadUpdateCommandParser.FromPreviousSdkOption); _adManifestOnlyOption = parseResult.GetValue(WorkloadUpdateCommandParser.AdManifestOnlyOption); _printRollbackDefinitionOnly = parseResult.GetValue(WorkloadUpdateCommandParser.PrintRollbackOption); @@ -73,7 +74,13 @@ public override int Execute() } else if (_adManifestOnlyOption) { - _workloadManifestUpdater.UpdateAdvertisingManifestsAsync(_includePreviews, string.IsNullOrWhiteSpace(_fromCacheOption) ? null : new DirectoryPath(_fromCacheOption)).Wait(); + _workloadManifestUpdater.UpdateAdvertisingManifestsAsync( + _includePreviews, + ShouldUseWorkloadSetMode(_sdkFeatureBand, _dotnetPath), + string.IsNullOrWhiteSpace(_fromCacheOption) ? + null : + new DirectoryPath(_fromCacheOption)) + .Wait(); Reporter.WriteLine(); Reporter.WriteLine(LocalizableStrings.WorkloadUpdateAdManifestsSucceeded); } @@ -105,7 +112,19 @@ public override int Execute() { try { - UpdateWorkloads(_includePreviews, string.IsNullOrWhiteSpace(_fromCacheOption) ? null : new DirectoryPath(_fromCacheOption)); + DirectoryPath? offlineCache = string.IsNullOrWhiteSpace(_fromCacheOption) ? null : new DirectoryPath(_fromCacheOption); + if (string.IsNullOrWhiteSpace(_workloadSetVersion)) + { + CalculateManifestUpdatesAndUpdateWorkloads(_includePreviews, offlineCache); + } + else + { + RunInNewTransaction(context => + { + var manifestUpdates = HandleWorkloadUpdateFromVersion(context, offlineCache); + UpdateWorkloads(false, manifestUpdates, offlineCache, context); + }); + } } catch (Exception e) { @@ -118,29 +137,55 @@ public override int Execute() return _workloadInstaller.ExitCode; } - public void UpdateWorkloads(bool includePreviews = false, DirectoryPath? offlineCache = null) + public void CalculateManifestUpdatesAndUpdateWorkloads(bool includePreviews = false, DirectoryPath? offlineCache = null) { Reporter.WriteLine(); + var useRollback = !string.IsNullOrWhiteSpace(_fromRollbackDefinition); + var useWorkloadSets = ShouldUseWorkloadSetMode(_sdkFeatureBand, _dotnetPath); + + if (useRollback && useWorkloadSets) + { + // Rollback files are only for loose manifests. Update the mode to be loose manifests. + Reporter.WriteLine(LocalizableStrings.UpdateFromRollbackSwitchesModeToLooseManifests); + _workloadInstaller.UpdateInstallMode(_sdkFeatureBand, false); + useWorkloadSets = false; + } + var workloadIds = GetUpdatableWorkloads(); WriteSDKInstallRecordsForVSWorkloads(workloadIds); - _workloadManifestUpdater.UpdateAdvertisingManifestsAsync(includePreviews, offlineCache).Wait(); + _workloadManifestUpdater.UpdateAdvertisingManifestsAsync(includePreviews, useWorkloadSets, offlineCache).Wait(); - var useRollback = !string.IsNullOrWhiteSpace(_fromRollbackDefinition); + IEnumerable manifestsToUpdate; + RunInNewTransaction(context => + { + if (useWorkloadSets) + { + manifestsToUpdate = InstallWorkloadSet(context); + } + else + { + manifestsToUpdate = useRollback ? _workloadManifestUpdater.CalculateManifestRollbacks(_fromRollbackDefinition) : + _workloadManifestUpdater.CalculateManifestUpdates().Select(m => m.ManifestUpdate); + } - var manifestsToUpdate = useRollback ? - _workloadManifestUpdater.CalculateManifestRollbacks(_fromRollbackDefinition) : - _workloadManifestUpdater.CalculateManifestUpdates().Select(m => m.ManifestUpdate); + UpdateWorkloads(useRollback, manifestsToUpdate, offlineCache, context); + + Reporter.WriteLine(); + Reporter.WriteLine(string.Format(LocalizableStrings.UpdateSucceeded, string.Join(" ", workloadIds))); + Reporter.WriteLine(); + }); + } - UpdateWorkloadsWithInstallRecord(_sdkFeatureBand, manifestsToUpdate, useRollback, offlineCache); + private void UpdateWorkloads(bool useRollback, IEnumerable manifestsToUpdate, DirectoryPath? offlineCache, ITransactionContext context) + { + var workloadIds = GetUpdatableWorkloads(); + + UpdateWorkloadsWithInstallRecord(_sdkFeatureBand, manifestsToUpdate, useRollback, context, offlineCache); WorkloadInstallCommand.TryRunGarbageCollection(_workloadInstaller, Reporter, Verbosity, workloadSetVersion => _workloadResolverFactory.CreateForWorkloadSet(_dotnetPath, _sdkVersion.ToString(), _userProfileDir, workloadSetVersion), offlineCache); _workloadManifestUpdater.DeleteUpdatableWorkloadsFile(); - - Reporter.WriteLine(); - Reporter.WriteLine(string.Format(LocalizableStrings.UpdateSucceeded, string.Join(" ", workloadIds))); - Reporter.WriteLine(); } private void WriteSDKInstallRecordsForVSWorkloads(IEnumerable updateableWorkloads) @@ -157,23 +202,11 @@ private void UpdateWorkloadsWithInstallRecord( SdkFeatureBand sdkFeatureBand, IEnumerable manifestsToUpdate, bool useRollback, + ITransactionContext context, DirectoryPath? offlineCache = null) { - - var transaction = new CliTransaction(); - - transaction.RollbackStarted = () => - { - Reporter.WriteLine(LocalizableStrings.RollingBackInstall); - }; - // Don't hide the original error if roll back fails, but do log the rollback failure - transaction.RollbackFailed = ex => - { - Reporter.WriteLine(string.Format(LocalizableStrings.RollBackFailedMessage, ex.Message)); - }; - - transaction.Run( - action: context => + context.Run( + action: () => { foreach (var manifestUpdate in manifestsToUpdate) { @@ -189,6 +222,8 @@ private void UpdateWorkloadsWithInstallRecord( _workloadInstaller.RemoveManifestsFromInstallState(sdkFeatureBand); } + _workloadInstaller.AdjustWorkloadSetInInstallState(sdkFeatureBand, string.IsNullOrWhiteSpace(_workloadSetVersion) ? null : _workloadSetVersion); + _workloadResolver.RefreshWorkloadManifests(); var workloads = GetUpdatableWorkloads(); @@ -232,5 +267,21 @@ private IEnumerable GetUpdatableWorkloads() return workloads; } + + private void RunInNewTransaction(Action a) + { + var transaction = new CliTransaction(); + transaction.RollbackStarted = () => + { + Reporter.WriteLine(LocalizableStrings.RollingBackInstall); + }; + // Don't hide the original error if roll back fails, but do log the rollback failure + transaction.RollbackFailed = ex => + { + Reporter.WriteLine(string.Format(LocalizableStrings.RollBackFailedMessage, ex.Message)); + }; + + transaction.Run(context => a(context)); + } } } diff --git a/src/Cli/dotnet/commands/dotnet-workload/update/WorkloadUpdateCommandParser.cs b/src/Cli/dotnet/commands/dotnet-workload/update/WorkloadUpdateCommandParser.cs index fe068fc74b70..787189013bb4 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/update/WorkloadUpdateCommandParser.cs +++ b/src/Cli/dotnet/commands/dotnet-workload/update/WorkloadUpdateCommandParser.cs @@ -43,6 +43,7 @@ private static CliCommand ConstructCommand() command.Options.Add(TempDirOption); command.Options.Add(FromPreviousSdkOption); command.Options.Add(AdManifestOnlyOption); + command.Options.Add(InstallingWorkloadCommandParser.WorkloadSetVersionOption); command.AddWorkloadCommandNuGetRestoreActionConfigOptions(); command.Options.Add(CommonOptions.VerbosityOption); command.Options.Add(PrintRollbackOption); diff --git a/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.cs.xlf b/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.cs.xlf index 0d57937d6b65..0cc940248598 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.cs.xlf +++ b/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.cs.xlf @@ -42,6 +42,11 @@ Aktualizace úlohy {0} neproběhla úspěšně z následujícího důvodu: + + Updating to a rollback file is not compatible with workload sets. Install and Update will now use loose manifests. To update to a specific workload version, use --version. + Updating to a rollback file is not compatible with workload sets. Install and Update will now use loose manifests. To update to a specific workload version, use --version. + + Successfully updated workload(s): {0}. Úlohy se úspěšně aktualizovaly: {0}. diff --git a/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.de.xlf b/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.de.xlf index 7fe3755912f5..eec11ddc74e8 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.de.xlf +++ b/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.de.xlf @@ -42,6 +42,11 @@ Fehler beim Update der Workload "{0}". Ursache: + + Updating to a rollback file is not compatible with workload sets. Install and Update will now use loose manifests. To update to a specific workload version, use --version. + Updating to a rollback file is not compatible with workload sets. Install and Update will now use loose manifests. To update to a specific workload version, use --version. + + Successfully updated workload(s): {0}. Workload(s) erfolgreich aktualisiert: {0}. diff --git a/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.es.xlf b/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.es.xlf index 61eacfcb2be0..5fd3c603769c 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.es.xlf +++ b/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.es.xlf @@ -42,6 +42,11 @@ La carga de trabajo "{0}" no se pudo actualizar debido a lo siguiente: + + Updating to a rollback file is not compatible with workload sets. Install and Update will now use loose manifests. To update to a specific workload version, use --version. + Updating to a rollback file is not compatible with workload sets. Install and Update will now use loose manifests. To update to a specific workload version, use --version. + + Successfully updated workload(s): {0}. La(s) carga(s) de trabajo se ha(n) actualizado correctamente: {0} diff --git a/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.fr.xlf b/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.fr.xlf index ffa504cef235..767a9f67a439 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.fr.xlf +++ b/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.fr.xlf @@ -42,6 +42,11 @@ Échec de la mise à jour de la charge de travail '{0}' pour la ou les raisons suivantes : + + Updating to a rollback file is not compatible with workload sets. Install and Update will now use loose manifests. To update to a specific workload version, use --version. + Updating to a rollback file is not compatible with workload sets. Install and Update will now use loose manifests. To update to a specific workload version, use --version. + + Successfully updated workload(s): {0}. Mise à jour réussie du charge de travail : {0}. diff --git a/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.it.xlf b/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.it.xlf index 0d9403d54fe4..f3d40db0dc9b 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.it.xlf +++ b/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.it.xlf @@ -42,6 +42,11 @@ L'aggiornamento del carico di lavoro '{0}' non è riuscito. Motivi: + + Updating to a rollback file is not compatible with workload sets. Install and Update will now use loose manifests. To update to a specific workload version, use --version. + Updating to a rollback file is not compatible with workload sets. Install and Update will now use loose manifests. To update to a specific workload version, use --version. + + Successfully updated workload(s): {0}. I carichi di lavoro sono stati aggiornati: {0}. diff --git a/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.ja.xlf b/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.ja.xlf index a3555167d604..27a35287eee3 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.ja.xlf +++ b/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.ja.xlf @@ -42,6 +42,11 @@ ワークロード '{0}' を更新できませんでした。原因: + + Updating to a rollback file is not compatible with workload sets. Install and Update will now use loose manifests. To update to a specific workload version, use --version. + Updating to a rollback file is not compatible with workload sets. Install and Update will now use loose manifests. To update to a specific workload version, use --version. + + Successfully updated workload(s): {0}. ワークロード {0} が正常に更新されました。 diff --git a/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.ko.xlf b/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.ko.xlf index 1f9d499812c4..2dcea44b5860 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.ko.xlf +++ b/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.ko.xlf @@ -42,6 +42,11 @@ 다음으로 인해 워크로드 '{0}'을(를) 업데이트하지 못했습니다. + + Updating to a rollback file is not compatible with workload sets. Install and Update will now use loose manifests. To update to a specific workload version, use --version. + Updating to a rollback file is not compatible with workload sets. Install and Update will now use loose manifests. To update to a specific workload version, use --version. + + Successfully updated workload(s): {0}. 워크로드를 업데이트했습니다. {0}. diff --git a/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.pl.xlf b/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.pl.xlf index 222d233a4c10..96c9a3f67371 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.pl.xlf +++ b/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.pl.xlf @@ -42,6 +42,11 @@ Aktualizacja obciążenia „{0}” nie powiodła się z następującego powodu: + + Updating to a rollback file is not compatible with workload sets. Install and Update will now use loose manifests. To update to a specific workload version, use --version. + Updating to a rollback file is not compatible with workload sets. Install and Update will now use loose manifests. To update to a specific workload version, use --version. + + Successfully updated workload(s): {0}. Pomyślnie zaktualizowano pakiety robocze: {0}. diff --git a/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.pt-BR.xlf b/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.pt-BR.xlf index c5db7d56ba3e..235954d94f87 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.pt-BR.xlf +++ b/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.pt-BR.xlf @@ -42,6 +42,11 @@ Falha ao atualizar a carga de trabalho '{0}' devido ao seguinte motivo: + + Updating to a rollback file is not compatible with workload sets. Install and Update will now use loose manifests. To update to a specific workload version, use --version. + Updating to a rollback file is not compatible with workload sets. Install and Update will now use loose manifests. To update to a specific workload version, use --version. + + Successfully updated workload(s): {0}. Carga(s) de trabalho atualizada(s) com êxito: {0}. diff --git a/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.ru.xlf b/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.ru.xlf index f65de634659b..b3b7e621533e 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.ru.xlf +++ b/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.ru.xlf @@ -42,6 +42,11 @@ Не удалось обновить рабочую нагрузку "{0}" по следующей причине: + + Updating to a rollback file is not compatible with workload sets. Install and Update will now use loose manifests. To update to a specific workload version, use --version. + Updating to a rollback file is not compatible with workload sets. Install and Update will now use loose manifests. To update to a specific workload version, use --version. + + Successfully updated workload(s): {0}. Рабочие нагрузки успешно обновлены: {0}. diff --git a/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.tr.xlf b/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.tr.xlf index 6c7f77da9566..670f5d690df9 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.tr.xlf +++ b/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.tr.xlf @@ -42,6 +42,11 @@ Aşağıdaki nedenlerle '{0}' iş yükü güncelleştirilemedi: + + Updating to a rollback file is not compatible with workload sets. Install and Update will now use loose manifests. To update to a specific workload version, use --version. + Updating to a rollback file is not compatible with workload sets. Install and Update will now use loose manifests. To update to a specific workload version, use --version. + + Successfully updated workload(s): {0}. İş yükleri başarıyla güncelleştirildi: {0}. diff --git a/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.zh-Hans.xlf b/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.zh-Hans.xlf index 3c981c1c23c4..3beaaebc1acf 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.zh-Hans.xlf +++ b/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.zh-Hans.xlf @@ -42,6 +42,11 @@ 工作负载“{0}”因以下原因而未能更新: + + Updating to a rollback file is not compatible with workload sets. Install and Update will now use loose manifests. To update to a specific workload version, use --version. + Updating to a rollback file is not compatible with workload sets. Install and Update will now use loose manifests. To update to a specific workload version, use --version. + + Successfully updated workload(s): {0}. 已成功更新工作负载: {0}。 diff --git a/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.zh-Hant.xlf b/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.zh-Hant.xlf index 20dab178f25e..914b6a666c8a 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.zh-Hant.xlf +++ b/src/Cli/dotnet/commands/dotnet-workload/update/xlf/LocalizableStrings.zh-Hant.xlf @@ -42,6 +42,11 @@ 因為下列原因,所以無法更新工作負載 '{0}': + + Updating to a rollback file is not compatible with workload sets. Install and Update will now use loose manifests. To update to a specific workload version, use --version. + Updating to a rollback file is not compatible with workload sets. Install and Update will now use loose manifests. To update to a specific workload version, use --version. + + Successfully updated workload(s): {0}. 已成功更新工作負載: {0}。 diff --git a/src/Microsoft.DotNet.TemplateLocator/Microsoft.DotNet.TemplateLocator.csproj b/src/Microsoft.DotNet.TemplateLocator/Microsoft.DotNet.TemplateLocator.csproj index 20f6d73e1473..84bd3a16954c 100644 --- a/src/Microsoft.DotNet.TemplateLocator/Microsoft.DotNet.TemplateLocator.csproj +++ b/src/Microsoft.DotNet.TemplateLocator/Microsoft.DotNet.TemplateLocator.csproj @@ -51,6 +51,7 @@ + diff --git a/src/Resolvers/Microsoft.DotNet.MSBuildSdkResolver/Microsoft.DotNet.MSBuildSdkResolver.csproj b/src/Resolvers/Microsoft.DotNet.MSBuildSdkResolver/Microsoft.DotNet.MSBuildSdkResolver.csproj index 77a9cf79660b..600d5decb5e7 100644 --- a/src/Resolvers/Microsoft.DotNet.MSBuildSdkResolver/Microsoft.DotNet.MSBuildSdkResolver.csproj +++ b/src/Resolvers/Microsoft.DotNet.MSBuildSdkResolver/Microsoft.DotNet.MSBuildSdkResolver.csproj @@ -72,6 +72,7 @@ + diff --git a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/IWorkloadManifestProvider.cs b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/IWorkloadManifestProvider.cs index a3acabf746e2..f6a5e9676624 100644 --- a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/IWorkloadManifestProvider.cs +++ b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/IWorkloadManifestProvider.cs @@ -14,6 +14,8 @@ public interface IWorkloadManifestProvider string GetSdkFeatureBand(); + string? GetWorkloadVersion(); + Dictionary GetAvailableWorkloadSets(); } } diff --git a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/IWorkloadResolver.cs b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/IWorkloadResolver.cs index 283aaffd387f..05a86f60d3b5 100644 --- a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/IWorkloadResolver.cs +++ b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/IWorkloadResolver.cs @@ -19,6 +19,7 @@ public interface IWorkloadResolver string GetManifestVersion(string manifestId); IEnumerable GetInstalledManifests(); string GetSdkFeatureBand(); + string? GetWorkloadVersion(); IEnumerable GetUpdatedWorkloads(WorkloadResolver advertisingManifestResolver, IEnumerable installedWorkloads); WorkloadManifest GetManifestFromWorkload(WorkloadId workloadId); 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 bfeb093643bd..afc2aa64034c 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 @@ -21,6 +21,7 @@ + diff --git a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/SdkDirectoryWorkloadManifestProvider.InstallStateReader.cs b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/SdkDirectoryWorkloadManifestProvider.InstallStateReader.cs deleted file mode 100644 index 7579778f6a46..000000000000 --- a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/SdkDirectoryWorkloadManifestProvider.InstallStateReader.cs +++ /dev/null @@ -1,104 +0,0 @@ -// 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.NET.Sdk.Localization; -using static Microsoft.NET.Sdk.WorkloadManifestReader.WorkloadManifestReader; -using Microsoft.Deployment.DotNet.Releases; - -#if USE_SYSTEM_TEXT_JSON -using System.Text.Json; -#else -using Newtonsoft.Json; -using JsonTokenType = Newtonsoft.Json.JsonToken; -#endif - -namespace Microsoft.NET.Sdk.WorkloadManifestReader -{ - public partial class SdkDirectoryWorkloadManifestProvider - { - public class InstallState - { - public string? WorkloadSetVersion { get; set; } - public WorkloadSet? Manifests { get; set; } - } - - public static class InstallStateReader - { - public static InstallState ReadInstallState(string installStatePath) - { - using var fileStream = File.OpenRead(installStatePath); - -#if USE_SYSTEM_TEXT_JSON - var readerOptions = new JsonReaderOptions - { - AllowTrailingCommas = true, - CommentHandling = JsonCommentHandling.Skip - }; - var reader = new Utf8JsonStreamReader(fileStream, readerOptions); -#else - using var textReader = new StreamReader(fileStream, System.Text.Encoding.UTF8, true); - using var jsonReader = new JsonTextReader(textReader); - - var reader = new Utf8JsonStreamReader(jsonReader); -#endif - - InstallState installState = new(); - - JsonReader.ConsumeToken(ref reader, JsonTokenType.StartObject); - while (reader.Read()) - { - switch (reader.TokenType) - { - case JsonTokenType.PropertyName: - var propName = reader.GetString(); - if (string.Equals("workloadVersion", propName, StringComparison.OrdinalIgnoreCase)) - { - installState.WorkloadSetVersion = JsonReader.ReadString(ref reader); - } - else if (string.Equals("manifests", propName, StringComparison.OrdinalIgnoreCase)) - { - installState.Manifests = ReadManifests(ref reader); - } - else - { - JsonReader.ConsumeValue(ref reader); - } - break; - - case JsonTokenType.EndObject: - return installState; - default: - throw new JsonFormatException(Strings.UnexpectedTokenAtOffset, reader.TokenType, reader.TokenStartIndex); - } - } - - throw new JsonFormatException(Strings.IncompleteDocument); - } - - static WorkloadSet ReadManifests(ref Utf8JsonStreamReader reader) - { - JsonReader.ConsumeToken(ref reader, JsonTokenType.StartObject); - Dictionary workloadSetDict = new(); - - while (reader.Read()) - { - switch (reader.TokenType) - { - case JsonTokenType.PropertyName: - var propName = reader.GetString(); - var propValue = JsonReader.ReadString(ref reader); - workloadSetDict[propName] = propValue; - break; - case JsonTokenType.EndObject: - return WorkloadSet.FromDictionaryForJson(workloadSetDict, new SdkFeatureBand(new ReleaseVersion(0,0,0))); - default: - throw new JsonFormatException(Strings.UnexpectedTokenAtOffset, reader.TokenType, reader.TokenStartIndex); - } - } - throw new JsonFormatException(Strings.IncompleteDocument); - } - } - } -} - diff --git a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/SdkDirectoryWorkloadManifestProvider.cs b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/SdkDirectoryWorkloadManifestProvider.cs index 95e51e8a6e6a..eeefd7dee1c7 100644 --- a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/SdkDirectoryWorkloadManifestProvider.cs +++ b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/SdkDirectoryWorkloadManifestProvider.cs @@ -1,12 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; using System.Security.Cryptography; -using System.Text; using Microsoft.Deployment.DotNet.Releases; using Microsoft.DotNet.Cli; using Microsoft.DotNet.Workloads.Workload; @@ -136,15 +131,15 @@ public void RefreshWorkloadManifests() var installStateFilePath = Path.Combine(WorkloadInstallType.GetInstallStateFolder(_sdkVersionBand, _sdkRootPath), "default.json"); if (File.Exists(installStateFilePath)) { - var installState = InstallStateReader.ReadInstallState(installStateFilePath); - if (!string.IsNullOrEmpty(installState.WorkloadSetVersion)) + var installState = InstallStateContents.FromPath(installStateFilePath); + if (!string.IsNullOrEmpty(installState.WorkloadVersion)) { - if (!availableWorkloadSets.TryGetValue(installState.WorkloadSetVersion!, out _workloadSet)) + if (!availableWorkloadSets.TryGetValue(installState.WorkloadVersion!, out _workloadSet)) { - throw new FileNotFoundException(string.Format(Strings.WorkloadVersionFromInstallStateNotFound, installState.WorkloadSetVersion, installStateFilePath)); + throw new FileNotFoundException(string.Format(Strings.WorkloadVersionFromInstallStateNotFound, installState.WorkloadVersion, installStateFilePath)); } } - _manifestsFromInstallState = installState.Manifests; + _manifestsFromInstallState = installState.Manifests is null ? new WorkloadSet() : WorkloadSet.FromDictionaryForJson(installState.Manifests, _sdkVersionBand); _installStateFilePath = installStateFilePath; } } @@ -157,13 +152,18 @@ public void RefreshWorkloadManifests() } } - public string GetWorkloadVersion() + public string? GetWorkloadVersion() { if (_workloadSet?.Version is not null) { return _workloadSet?.Version!; } + if (InstallStateContents.FromPath(Path.Combine(WorkloadInstallType.GetInstallStateFolder(_sdkVersionBand, _sdkRootPath), "default.json")).UseWorkloadSets == true) + { + return null; + } + using (SHA256 sha256Hash = SHA256.Create()) { byte[] bytes = sha256Hash.ComputeHash(Encoding.UTF8.GetBytes(string.Join(";", diff --git a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/SdkFeatureBand.cs b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/SdkFeatureBand.cs index e496e9d757d9..0ce6d4eed582 100644 --- a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/SdkFeatureBand.cs +++ b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/SdkFeatureBand.cs @@ -30,6 +30,9 @@ public SdkFeatureBand(ReleaseVersion version) } } + public int Major => _featureBand.Major; + public int Minor => _featureBand.Minor; + public bool Equals(SdkFeatureBand other) { return _featureBand.Equals(other._featureBand); diff --git a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/TempDirectoryWorkloadManifestProvider.cs b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/TempDirectoryWorkloadManifestProvider.cs index b45a463771b2..a764fb2d9fb5 100644 --- a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/TempDirectoryWorkloadManifestProvider.cs +++ b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/TempDirectoryWorkloadManifestProvider.cs @@ -54,6 +54,7 @@ public IEnumerable GetManifestDirectories() } public string GetSdkFeatureBand() => _sdkVersionBand; + public string? GetWorkloadVersion() => _sdkVersionBand.ToString() + ".2"; public Dictionary GetAvailableWorkloadSets() => new(); } } diff --git a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/WorkloadResolver.cs b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/WorkloadResolver.cs index 040a0f7a1495..81fdae3f4c2d 100644 --- a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/WorkloadResolver.cs +++ b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/WorkloadResolver.cs @@ -109,6 +109,8 @@ public void RefreshWorkloadManifests() ComposeWorkloadManifests(); } + public string? GetWorkloadVersion() => _manifestProvider.GetWorkloadVersion(); + private void LoadManifestsFromProvider(IWorkloadManifestProvider manifestProvider) { foreach (var readableManifest in manifestProvider.GetManifests()) @@ -741,6 +743,7 @@ public void RefreshWorkloadManifests() { } public Dictionary GetAvailableWorkloadSets() => new(); public IEnumerable GetManifests() => Enumerable.Empty(); public string GetSdkFeatureBand() => _sdkFeatureBand; + public string? GetWorkloadVersion() => _sdkFeatureBand.ToString() + ".2"; } } 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 1c16e13b948b..e1e67af18320 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 @@ -90,6 +90,7 @@ + diff --git a/src/Tests/Microsoft.NET.Sdk.WorkloadManifestReader.Tests/FakeManifestProvider.cs b/src/Tests/Microsoft.NET.Sdk.WorkloadManifestReader.Tests/FakeManifestProvider.cs index 92ddb64a0829..7c8fa003a368 100644 --- a/src/Tests/Microsoft.NET.Sdk.WorkloadManifestReader.Tests/FakeManifestProvider.cs +++ b/src/Tests/Microsoft.NET.Sdk.WorkloadManifestReader.Tests/FakeManifestProvider.cs @@ -40,6 +40,7 @@ public IEnumerable GetManifests() public string GetSdkFeatureBand() => "8.0.100"; public Dictionary GetAvailableWorkloadSets() => throw new NotImplementedException(); + public string? GetWorkloadVersion() => "8.0.100.2"; } internal class InMemoryFakeManifestProvider : IWorkloadManifestProvider, IEnumerable<(string id, string content)> @@ -66,5 +67,6 @@ public IEnumerable GetManifests() IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException(); public string GetSdkFeatureBand() => "8.0.100"; public Dictionary GetAvailableWorkloadSets() => throw new NotImplementedException(); + public string? GetWorkloadVersion() => "8.0.100.2"; } } diff --git a/src/Tests/Microsoft.NET.Sdk.WorkloadManifestReader.Tests/SdkDirectoryWorkloadManifestProviderTests.cs b/src/Tests/Microsoft.NET.Sdk.WorkloadManifestReader.Tests/SdkDirectoryWorkloadManifestProviderTests.cs index 13a4f6d3570b..44bf3f961184 100644 --- a/src/Tests/Microsoft.NET.Sdk.WorkloadManifestReader.Tests/SdkDirectoryWorkloadManifestProviderTests.cs +++ b/src/Tests/Microsoft.NET.Sdk.WorkloadManifestReader.Tests/SdkDirectoryWorkloadManifestProviderTests.cs @@ -48,7 +48,7 @@ public void ItShouldReturnTheWorkloadVersion(bool useWorkloadSet) { ""ios"": ""11.0.2/8.0.100"", ""android"": ""33.0.2-rc.1/8.0.200"", - ""maui"": ""15.0.1-rc.456/8.0.200-rc.2"", + ""maui"": ""15.0.1-rc.456/8.0.200-rc.2"" } "); } @@ -494,7 +494,7 @@ public void ItUsesWorkloadSetFromGlobalJson() { "sdk": { "version": "8.0.200", - "workloadversion": "8.0.201" + "workloadVersion": "8.0.201" }, "msbuild-sdks": { "Microsoft.DotNet.Arcade.Sdk": "7.0.0-beta.23254.2", @@ -536,7 +536,7 @@ public void ItFailsIfWorkloadSetFromGlobalJsonIsNotInstalled() { "sdk": { "version": "8.0.200", - "workloadversion": "8.0.201" + "workloadVersion": "8.0.201" }, "msbuild-sdks": { "Microsoft.DotNet.Arcade.Sdk": "7.0.0-beta.23254.2", @@ -565,7 +565,7 @@ public void ItFailsIfGlobalJsonIsMalformed() File.WriteAllText(globalJsonPath, """ { "sdk": { - "workloadversion": [ "8.0.202" ] + "workloadVersion": [ "8.0.202" ] } } """); @@ -862,7 +862,7 @@ public void ItFallsBackForManifestNotInInstallState() """ { "manifests": { - "ios": "12.0.1/8.0.200", + "ios": "12.0.1/8.0.200" } } """); @@ -885,7 +885,7 @@ public void GlobalJsonOverridesInstallState() { "sdk": { "version": "8.0.200", - "workloadversion": "8.0.201" + "workloadVersion": "8.0.201" }, "msbuild-sdks": { "Microsoft.DotNet.Arcade.Sdk": "7.0.0-beta.23254.2", diff --git a/src/Tests/dotnet-workload-install.Tests/GivenWorkloadManifestUpdater.cs b/src/Tests/dotnet-workload-install.Tests/GivenWorkloadManifestUpdater.cs index 029d28fd9ee6..1a12d4ccb8b9 100644 --- a/src/Tests/dotnet-workload-install.Tests/GivenWorkloadManifestUpdater.cs +++ b/src/Tests/dotnet-workload-install.Tests/GivenWorkloadManifestUpdater.cs @@ -558,7 +558,7 @@ public void GivenWorkloadManifestUpdateItChoosesHighestManifestVersionInCache() var installationRepo = new MockInstallationRecordRepository(); var installer = new MockPackWorkloadInstaller(dotnetRoot); var manifestUpdater = new WorkloadManifestUpdater(_reporter, workloadResolver, nugetDownloader, testDir, installationRepo, installer); - manifestUpdater.UpdateAdvertisingManifestsAsync(false, new DirectoryPath(offlineCache)).Wait(); + manifestUpdater.UpdateAdvertisingManifestsAsync(false, false, new DirectoryPath(offlineCache)).Wait(); // We should have chosen the higher version manifest package to install/ extract installer.ExtractCallParams.Count().Should().Be(1); diff --git a/src/Tests/dotnet-workload-install.Tests/MockManifestProvider.cs b/src/Tests/dotnet-workload-install.Tests/MockManifestProvider.cs index f3298b0d654b..935fb9addfe8 100644 --- a/src/Tests/dotnet-workload-install.Tests/MockManifestProvider.cs +++ b/src/Tests/dotnet-workload-install.Tests/MockManifestProvider.cs @@ -48,5 +48,6 @@ public IEnumerable GetManifests() } public string GetSdkFeatureBand() => SdkFeatureBand.ToString(); + public string GetWorkloadVersion() => SdkFeatureBand.ToString() + ".2"; } } diff --git a/src/Tests/dotnet-workload-install.Tests/MockPackWorkloadInstaller.cs b/src/Tests/dotnet-workload-install.Tests/MockPackWorkloadInstaller.cs index 37a204706607..fb4b3924dea5 100644 --- a/src/Tests/dotnet-workload-install.Tests/MockPackWorkloadInstaller.cs +++ b/src/Tests/dotnet-workload-install.Tests/MockPackWorkloadInstaller.cs @@ -8,6 +8,7 @@ using Microsoft.NET.Sdk.WorkloadManifestReader; using Microsoft.DotNet.ToolPackage; using Microsoft.DotNet.Workloads.Workload; +using Microsoft.DotNet.Cli.Utils; namespace Microsoft.DotNet.Cli.Workload.Install.Tests { @@ -24,13 +25,14 @@ internal class MockPackWorkloadInstaller : IInstaller public bool FailingGarbageCollection; private readonly string FailingPack; private readonly string _dotnetDir; + private string workloadSetContents; public IWorkloadResolver WorkloadResolver { get; set; } public int ExitCode => 0; public MockPackWorkloadInstaller(string dotnetDir, string failingWorkload = null, string failingPack = null, bool failingRollback = false, IList installedWorkloads = null, - IList installedPacks = null, bool failingGarbageCollection = false) + IList installedPacks = null, bool failingGarbageCollection = false, string workloadSetContents = "") { InstallationRecordRepository = new MockInstallationRecordRepository(failingWorkload, installedWorkloads); FailingRollback = failingRollback; @@ -38,6 +40,7 @@ public MockPackWorkloadInstaller(string dotnetDir, string failingWorkload = null FailingPack = failingPack; FailingGarbageCollection = failingGarbageCollection; _dotnetDir = dotnetDir; + this.workloadSetContents = workloadSetContents; } IEnumerable GetPacksForWorkloads(IEnumerable workloadIds) @@ -61,6 +64,17 @@ public void UpdateInstallMode(SdkFeatureBand sdkFeatureBand, bool newMode) throw new NotImplementedException(); } + public void AdjustWorkloadSetInInstallState(SdkFeatureBand sdkFeatureBand, string workloadVersion) + { + var installStatePath = Path.Combine(Path.GetTempPath(), "dotnetTestPath", "metadata", "workloads", sdkFeatureBand.ToString(), "InstallState", "default.json"); + var contents = InstallStateContents.FromPath(installStatePath); + contents.WorkloadVersion = workloadVersion; + if (File.Exists(installStatePath)) + { + File.WriteAllText(installStatePath, contents.ToString()); + } + } + public void InstallWorkloads(IEnumerable workloadIds, SdkFeatureBand sdkFeatureBand, ITransactionContext transactionContext, DirectoryPath? offlineCache = null) { List packs = new List(); @@ -91,6 +105,14 @@ public void InstallWorkloads(IEnumerable workloadIds, SdkFeatureBand }); } + public string InstallWorkloadSet(ITransactionContext context, string advertisingPackagePath) + { + var version = Path.GetFileName(Path.GetDirectoryName(advertisingPackagePath ?? string.Empty)); + Directory.CreateDirectory(advertisingPackagePath); + File.WriteAllText(Path.Combine(advertisingPackagePath, Constants.workloadSetVersionFileName), version); + return Path.GetDirectoryName(advertisingPackagePath ?? string.Empty); + } + public void RepairWorkloads(IEnumerable workloadIds, SdkFeatureBand sdkFeatureBand, DirectoryPath? offlineCache = null) => throw new NotImplementedException(); public void GarbageCollect(Func getResolverForWorkloadSet, DirectoryPath? offlineCache = null, bool cleanAllPacks = false) diff --git a/src/Tests/dotnet-workload-install.Tests/MockWorkloadManifestUpdater.cs b/src/Tests/dotnet-workload-install.Tests/MockWorkloadManifestUpdater.cs index 6a3d90073c64..3b7a0275a2e6 100644 --- a/src/Tests/dotnet-workload-install.Tests/MockWorkloadManifestUpdater.cs +++ b/src/Tests/dotnet-workload-install.Tests/MockWorkloadManifestUpdater.cs @@ -13,13 +13,15 @@ internal class MockWorkloadManifestUpdater : IWorkloadManifestUpdater public int CalculateManifestUpdatesCallCount = 0; public int GetManifestPackageDownloadsCallCount = 0; private readonly IEnumerable _manifestUpdates; + private bool _fromWorkloadSet; - public MockWorkloadManifestUpdater(IEnumerable manifestUpdates = null) + public MockWorkloadManifestUpdater(IEnumerable manifestUpdates = null, bool fromWorkloadSet = false) { _manifestUpdates = manifestUpdates ?? new List(); + _fromWorkloadSet = fromWorkloadSet; } - public Task UpdateAdvertisingManifestsAsync(bool includePreview, DirectoryPath? cachePath = null) + public Task UpdateAdvertisingManifestsAsync(bool includePreview, bool useWorkloadSets = false, DirectoryPath? cachePath = null) { UpdateAdvertisingManifestsCallCount++; return Task.CompletedTask; @@ -42,11 +44,19 @@ public Task> GetManifestPackageDownloadsAsync(bool public IEnumerable CalculateManifestRollbacks(string rollbackDefinitionFilePath) { + if (_fromWorkloadSet && !rollbackDefinitionFilePath.EndsWith("installed.workloadset.json")) + { + throw new Exception("Should be updating or installing via workload set."); + } + return _manifestUpdates.Select(t => t.ManifestUpdate); } public Task BackgroundUpdateAdvertisingManifestsWhenRequiredAsync() => throw new NotImplementedException(); public IEnumerable GetUpdatableWorkloadsToAdvertise(IEnumerable installedWorkloads) => throw new NotImplementedException(); public void DeleteUpdatableWorkloadsFile() { } + + public void DownloadWorkloadSet(string version, DirectoryPath? offlineCache) => throw new NotImplementedException(); + public IEnumerable ParseRollbackDefinitionFiles(IEnumerable files) => _manifestUpdates.Select(t => t.ManifestUpdate); } } diff --git a/src/Tests/dotnet-workload-install.Tests/WorkloadGarbageCollectionTests.cs b/src/Tests/dotnet-workload-install.Tests/WorkloadGarbageCollectionTests.cs index 7946e7e3ce5a..0cd4e2a2fe21 100644 --- a/src/Tests/dotnet-workload-install.Tests/WorkloadGarbageCollectionTests.cs +++ b/src/Tests/dotnet-workload-install.Tests/WorkloadGarbageCollectionTests.cs @@ -200,7 +200,7 @@ public void GarbageCollectManifestsWithInstallState() """ { "manifests": { - "testmanifest": "2.0.0/6.0.300", + "testmanifest": "2.0.0/6.0.300" } } """); diff --git a/src/Tests/dotnet-workload-search.Tests/MockWorkloadResolver.cs b/src/Tests/dotnet-workload-search.Tests/MockWorkloadResolver.cs index 0a769ee235ea..5a9bdd9cb706 100644 --- a/src/Tests/dotnet-workload-search.Tests/MockWorkloadResolver.cs +++ b/src/Tests/dotnet-workload-search.Tests/MockWorkloadResolver.cs @@ -17,17 +17,18 @@ public MockWorkloadResolver(IEnumerable available public IEnumerable GetAvailableWorkloads() => _availableWorkloads; public IEnumerable GetInstalledWorkloadPacksOfKind(WorkloadPackKind kind) => throw new NotImplementedException(); - public IEnumerable GetPacksInWorkload(WorkloadId workloadId) => throw new NotImplementedException(); + public IEnumerable GetPacksInWorkload(WorkloadId workloadId) => Array.Empty(); public IEnumerable GetExtendedWorkloads(IEnumerable workloadIds) => throw new NotImplementedException(); public ISet GetWorkloadSuggestionForMissingPacks(IList packId, out ISet unsatisfiablePacks) => throw new NotImplementedException(); - public void RefreshWorkloadManifests() => throw new NotImplementedException(); + public void RefreshWorkloadManifests() { } public WorkloadResolver.PackInfo TryGetPackInfo(WorkloadPackId packId) => throw new NotImplementedException(); public bool IsPlatformIncompatibleWorkload(WorkloadId workloadId) => throw new NotImplementedException(); public string GetManifestVersion(string manifestId) => throw new NotImplementedException(); public IEnumerable GetInstalledManifests() => throw new NotImplementedException(); public IWorkloadResolver CreateOverlayResolver(IWorkloadManifestProvider overlayManifestProvider) => throw new NotImplementedException(); public string GetSdkFeatureBand() => "12.0.400"; + public string GetWorkloadVersion() => "12.0.400.2"; public IEnumerable GetUpdatedWorkloads(WorkloadResolver advertisingManifestResolver, IEnumerable installedWorkloads) => throw new NotImplementedException(); WorkloadResolver IWorkloadResolver.CreateOverlayResolver(IWorkloadManifestProvider overlayManifestProvider) => throw new NotImplementedException(); WorkloadManifest IWorkloadResolver.GetManifestFromWorkload(WorkloadId workloadId) => throw new NotImplementedException(); diff --git a/src/Tests/dotnet-workload-update.Tests/GivenDotnetWorkloadUpdate.cs b/src/Tests/dotnet-workload-update.Tests/GivenDotnetWorkloadUpdate.cs index 896569702333..ef980e0ba4d4 100644 --- a/src/Tests/dotnet-workload-update.Tests/GivenDotnetWorkloadUpdate.cs +++ b/src/Tests/dotnet-workload-update.Tests/GivenDotnetWorkloadUpdate.cs @@ -13,6 +13,7 @@ using Microsoft.DotNet.Cli.Utils; using static Microsoft.NET.Sdk.WorkloadManifestReader.WorkloadResolver; using System.Text.Json; +using Microsoft.DotNet.Cli.Workload.Search.Tests; namespace Microsoft.DotNet.Cli.Workload.Update.Tests { @@ -190,6 +191,64 @@ public void GivenWorkloadUpdateItUpdatesOutOfDatePacks() installer.InstalledPacks.Where(pack => pack.Id.ToString().Contains("Android")).Count().Should().Be(8); } + [Theory] + [InlineData(true)] + [InlineData(false)] + public void UpdateViaWorkloadSet(bool upgrade) + { + var versionNumber = "8.0.0"; + var workloadSetContents = @" +{ +""android"": ""2.3.4/8.0.200"" +} +"; + var nugetPackageDownloader = new MockNuGetPackageDownloader(); + var workloadResolver = new MockWorkloadResolver(new WorkloadInfo[] { new WorkloadInfo(new WorkloadId("android"), string.Empty) }); + var workloadInstaller = new MockPackWorkloadInstaller( + Path.Combine(Path.GetTempPath(), "dotnetTestPat", "userProfileDir"), + installedWorkloads: new List() { new WorkloadId("android")}, + workloadSetContents: workloadSetContents) + { + WorkloadResolver = workloadResolver + }; + var oldVersion = upgrade ? "2.3.2" : "2.3.6"; + var workloadManifestUpdater = new MockWorkloadManifestUpdater( + manifestUpdates: new ManifestUpdateWithWorkloads[] { + new ManifestUpdateWithWorkloads(new ManifestVersionUpdate(new ManifestId("android"), new ManifestVersion(oldVersion), "8.0.200", new ManifestVersion("2.3.4"), "8.0.200"), Enumerable.Empty>().ToDictionary()) + }, + fromWorkloadSet: true); + var resolverFactory = new MockWorkloadResolverFactory(Path.Combine(Path.GetTempPath(), "dotnetTestPath"), versionNumber, workloadResolver, "userProfileDir"); + var updateCommand = new WorkloadUpdateCommand(Parser.Instance.Parse("dotnet workload update"), Reporter.Output, resolverFactory, workloadInstaller, nugetPackageDownloader, workloadManifestUpdater); + + var installStatePath = Path.Combine(Path.GetTempPath(), "dotnetTestPath", "metadata", "workloads", versionNumber, "InstallState", "default.json"); + var contents = new InstallStateContents(); + contents.UseWorkloadSets = true; + var versionFile = Path.Combine("userProfileDir", "sdk-advertising", "8.0.0", "microsoft.net.workloads", Constants.workloadSetVersionFileName); + try + { + Directory.CreateDirectory(Path.GetDirectoryName(installStatePath)); + File.WriteAllText(installStatePath, contents.ToString()); + updateCommand.Execute(); + File.Exists(versionFile).Should().BeTrue(); + File.ReadAllText(versionFile).Should().Be("8.0.0"); + } + finally + { + if (File.Exists(versionFile)) + { + File.Delete(versionFile); + } + + if (File.Exists(installStatePath)) + { + File.Delete(installStatePath); + } + } + + workloadInstaller.InstalledManifests.Count.Should().Be(1); + workloadInstaller.InstalledManifests[0].manifestUpdate.NewVersion.ToString().Should().Be("2.3.4"); + } + [Fact] public void GivenWorkloadUpdateItRollsBackOnFailedUpdate() { @@ -332,7 +391,7 @@ public void ApplyRollbackAcrossFeatureBand(string existingSdkFeatureBand, string }; (var dotnetPath, var updateCommand, var packInstaller, _, _, _) = GetTestInstallers(parseResult, manifestUpdates: manifestsToUpdate, sdkVersion: "6.0.300", identifier: existingSdkFeatureBand + newSdkFeatureBand, installedFeatureBand: existingSdkFeatureBand); - updateCommand.UpdateWorkloads(); + updateCommand.CalculateManifestUpdatesAndUpdateWorkloads(); packInstaller.InstalledManifests[0].manifestUpdate.ManifestId.Should().Be(manifestsToUpdate[0].ManifestUpdate.ManifestId); packInstaller.InstalledManifests[0].manifestUpdate.NewVersion.Should().Be(manifestsToUpdate[0].ManifestUpdate.NewVersion); @@ -362,7 +421,7 @@ public void ApplyRollbackWithMultipleManifestsAcrossFeatureBand() }; (_, var updateCommand, var packInstaller, _, _, _) = GetTestInstallers(parseResult, manifestUpdates: manifestsToUpdate, sdkVersion: "6.0.300", installedFeatureBand: "6.0.300"); - updateCommand.UpdateWorkloads(); + updateCommand.CalculateManifestUpdatesAndUpdateWorkloads(); packInstaller.InstalledManifests[0].manifestUpdate.ManifestId.Should().Be(manifestsToUpdate[0].ManifestUpdate.ManifestId); packInstaller.InstalledManifests[0].manifestUpdate.NewVersion.Should().Be(manifestsToUpdate[0].ManifestUpdate.NewVersion);