diff --git a/src/Cli/dotnet/Commands/CliCommandStrings.resx b/src/Cli/dotnet/Commands/CliCommandStrings.resx
index 74001a13dfe9..d34d4f76f184 100644
--- a/src/Cli/dotnet/Commands/CliCommandStrings.resx
+++ b/src/Cli/dotnet/Commands/CliCommandStrings.resx
@@ -1,17 +1,17 @@
-
@@ -2724,4 +2724,8 @@ Proceed?
duration:
+
+ Workload set version {0} has missing manifests likely removed by package management. Run "dotnet workload repair" to fix this.
+ {0} is the workload set version. {Locked="dotnet workload repair"}
+
diff --git a/src/Cli/dotnet/Commands/Workload/Install/FileBasedInstaller.cs b/src/Cli/dotnet/Commands/Workload/Install/FileBasedInstaller.cs
index 99681825c16f..9328fd2f9f32 100644
--- a/src/Cli/dotnet/Commands/Workload/Install/FileBasedInstaller.cs
+++ b/src/Cli/dotnet/Commands/Workload/Install/FileBasedInstaller.cs
@@ -5,6 +5,7 @@
using System.Collections.Concurrent;
using System.Text.Json;
+using Microsoft.DotNet.Cli.Commands.Workload;
using Microsoft.DotNet.Cli.Commands.Workload.Config;
using Microsoft.DotNet.Cli.Commands.Workload.Install.WorkloadInstallRecords;
using Microsoft.DotNet.Cli.Extensions;
@@ -642,7 +643,7 @@ public IEnumerable GetWorkloadHistoryRecords(string sdkFe
public void Shutdown()
{
// Perform any additional cleanup here that's intended to run at the end of the command, regardless
- // of success or failure. For file based installs, there shouldn't be any additional work to
+ // of success or failure. For file based installs, there shouldn't be any additional work to
// perform.
}
diff --git a/src/Cli/dotnet/Commands/Workload/Install/WorkloadInstallerFactory.cs b/src/Cli/dotnet/Commands/Workload/Install/WorkloadInstallerFactory.cs
index 6ad1553257f2..e260bfa90fa7 100644
--- a/src/Cli/dotnet/Commands/Workload/Install/WorkloadInstallerFactory.cs
+++ b/src/Cli/dotnet/Commands/Workload/Install/WorkloadInstallerFactory.cs
@@ -3,6 +3,7 @@
#nullable disable
+using Microsoft.DotNet.Cli.Commands.Workload;
using Microsoft.DotNet.Cli.NuGetPackageDownloader;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.Configurer;
@@ -48,7 +49,7 @@ public static IInstaller GetWorkloadInstaller(
userProfileDir ??= CliFolderPathCalculator.DotnetUserProfileFolderPath;
- return new FileBasedInstaller(
+ var installer = new FileBasedInstaller(
reporter,
sdkFeatureBand,
workloadResolver,
@@ -59,6 +60,25 @@ public static IInstaller GetWorkloadInstaller(
verbosity: verbosity,
packageSourceLocation: packageSourceLocation,
restoreActionConfig: restoreActionConfig);
+
+ // Attach corruption repairer to recover from corrupt workload sets
+ if (nugetPackageDownloader is not null &&
+ workloadResolver?.GetWorkloadManifestProvider() is SdkDirectoryWorkloadManifestProvider sdkProvider &&
+ sdkProvider.CorruptionRepairer is null)
+ {
+ sdkProvider.CorruptionRepairer = new WorkloadManifestCorruptionRepairer(
+ reporter,
+ installer,
+ workloadResolver,
+ sdkFeatureBand,
+ dotnetDir,
+ userProfileDir,
+ nugetPackageDownloader,
+ packageSourceLocation,
+ verbosity);
+ }
+
+ return installer;
}
private static bool CanWriteToDotnetRoot(string dotnetDir = null)
diff --git a/src/Cli/dotnet/Commands/Workload/Repair/WorkloadRepairCommand.cs b/src/Cli/dotnet/Commands/Workload/Repair/WorkloadRepairCommand.cs
index 46fee7742ebb..7712399f3eee 100644
--- a/src/Cli/dotnet/Commands/Workload/Repair/WorkloadRepairCommand.cs
+++ b/src/Cli/dotnet/Commands/Workload/Repair/WorkloadRepairCommand.cs
@@ -69,7 +69,8 @@ public override int Execute()
{
Reporter.WriteLine();
- var workloadIds = _workloadInstaller.GetWorkloadInstallationRecordRepository().GetInstalledWorkloads(new SdkFeatureBand(_sdkVersion));
+ var sdkFeatureBand = new SdkFeatureBand(_sdkVersion);
+ var workloadIds = _workloadInstaller.GetWorkloadInstallationRecordRepository().GetInstalledWorkloads(sdkFeatureBand);
if (!workloadIds.Any())
{
@@ -79,7 +80,7 @@ public override int Execute()
Reporter.WriteLine(string.Format(CliCommandStrings.RepairingWorkloads, string.Join(" ", workloadIds)));
- ReinstallWorkloadsBasedOnCurrentManifests(workloadIds, new SdkFeatureBand(_sdkVersion));
+ ReinstallWorkloadsBasedOnCurrentManifests(workloadIds, sdkFeatureBand);
WorkloadInstallCommand.TryRunGarbageCollection(_workloadInstaller, Reporter, Verbosity, workloadSetVersion => _workloadResolverFactory.CreateForWorkloadSet(_dotnetPath, _sdkVersion.ToString(), _userProfileDir, workloadSetVersion));
@@ -105,4 +106,5 @@ private void ReinstallWorkloadsBasedOnCurrentManifests(IEnumerable w
{
_workloadInstaller.RepairWorkloads(workloadIds, sdkFeatureBand);
}
+
}
diff --git a/src/Cli/dotnet/Commands/Workload/WorkloadHistoryRecorder.cs b/src/Cli/dotnet/Commands/Workload/WorkloadHistoryRecorder.cs
index ab64b7539912..0690f5e12347 100644
--- a/src/Cli/dotnet/Commands/Workload/WorkloadHistoryRecorder.cs
+++ b/src/Cli/dotnet/Commands/Workload/WorkloadHistoryRecorder.cs
@@ -54,6 +54,10 @@ public void Run(Action workloadAction)
private WorkloadHistoryState GetWorkloadState()
{
var resolver = _workloadResolverFunc();
+ if (resolver.GetWorkloadManifestProvider() is SdkDirectoryWorkloadManifestProvider sdkProvider)
+ {
+ sdkProvider.CorruptionFailureMode = ManifestCorruptionFailureMode.Ignore;
+ }
var currentWorkloadVersion = resolver.GetWorkloadVersion().Version;
return new WorkloadHistoryState()
{
diff --git a/src/Cli/dotnet/Commands/Workload/WorkloadManifestCorruptionRepairer.cs b/src/Cli/dotnet/Commands/Workload/WorkloadManifestCorruptionRepairer.cs
new file mode 100644
index 000000000000..28bfe766a5f0
--- /dev/null
+++ b/src/Cli/dotnet/Commands/Workload/WorkloadManifestCorruptionRepairer.cs
@@ -0,0 +1,121 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Diagnostics.CodeAnalysis;
+using Microsoft.DotNet.Cli;
+using Microsoft.DotNet.Cli.Commands.Workload.Install;
+using Microsoft.DotNet.Cli.NuGetPackageDownloader;
+using Microsoft.DotNet.Cli.Utils;
+using Microsoft.NET.Sdk.WorkloadManifestReader;
+
+namespace Microsoft.DotNet.Cli.Commands.Workload;
+
+internal sealed class WorkloadManifestCorruptionRepairer : IWorkloadManifestCorruptionRepairer
+{
+ private readonly IReporter _reporter;
+ private readonly IInstaller _workloadInstaller;
+ private readonly IWorkloadResolver _workloadResolver;
+ private readonly SdkFeatureBand _sdkFeatureBand;
+ private readonly string _dotnetPath;
+ private readonly string _userProfileDir;
+ private readonly INuGetPackageDownloader? _packageDownloader;
+ private readonly PackageSourceLocation? _packageSourceLocation;
+ private readonly VerbosityOptions _verbosity;
+
+ private bool _checked;
+
+ public WorkloadManifestCorruptionRepairer(
+ IReporter reporter,
+ IInstaller workloadInstaller,
+ IWorkloadResolver workloadResolver,
+ SdkFeatureBand sdkFeatureBand,
+ string dotnetPath,
+ string userProfileDir,
+ INuGetPackageDownloader? packageDownloader,
+ PackageSourceLocation? packageSourceLocation,
+ VerbosityOptions verbosity)
+ {
+ _reporter = reporter ?? NullReporter.Instance;
+ _workloadInstaller = workloadInstaller;
+ _workloadResolver = workloadResolver;
+ _sdkFeatureBand = sdkFeatureBand;
+ _dotnetPath = dotnetPath;
+ _userProfileDir = userProfileDir;
+ _packageDownloader = packageDownloader;
+ _packageSourceLocation = packageSourceLocation;
+ _verbosity = verbosity;
+ }
+
+ public void EnsureManifestsHealthy(ManifestCorruptionFailureMode failureMode)
+ {
+ if (_checked)
+ {
+ return;
+ }
+
+ _checked = true;
+
+ if (failureMode == ManifestCorruptionFailureMode.Ignore)
+ {
+ return;
+ }
+
+ // Get the workload set directly from the provider - it was already resolved during construction
+ // and doesn't require reading the install state file again
+ var provider = _workloadResolver.GetWorkloadManifestProvider() as SdkDirectoryWorkloadManifestProvider;
+ var workloadSet = provider?.ResolvedWorkloadSet;
+
+ if (workloadSet is null)
+ {
+ // No workload set is being used
+ return;
+ }
+
+ if (!provider?.HasMissingManifests(workloadSet) ?? true)
+ {
+ return;
+ }
+
+ if (failureMode == ManifestCorruptionFailureMode.Throw)
+ {
+ throw new InvalidOperationException(string.Format(CliCommandStrings.WorkloadSetHasMissingManifests, workloadSet.Version));
+ }
+
+ _reporter.WriteLine($"Repairing workload set {workloadSet.Version}...");
+ CliTransaction.RunNew(context => RepairCorruptWorkloadSet(context, workloadSet));
+ }
+
+
+
+ private void RepairCorruptWorkloadSet(ITransactionContext context, WorkloadSet workloadSet)
+ {
+ var manifestUpdates = CreateManifestUpdatesFromWorkloadSet(workloadSet);
+
+ foreach (var manifestUpdate in manifestUpdates)
+ {
+ _workloadInstaller.InstallWorkloadManifest(manifestUpdate, context);
+ }
+
+ }
+
+ [MemberNotNull(nameof(_packageDownloader))]
+ private IEnumerable CreateManifestUpdatesFromWorkloadSet(WorkloadSet workloadSet)
+ {
+ if (_packageDownloader is null)
+ {
+ throw new InvalidOperationException("Package downloader is required to repair workload manifests.");
+ }
+
+ var manifestUpdater = new WorkloadManifestUpdater(
+ _reporter,
+ _workloadResolver,
+ _packageDownloader,
+ _userProfileDir,
+ _workloadInstaller.GetWorkloadInstallationRecordRepository(),
+ _workloadInstaller,
+ _packageSourceLocation,
+ displayManifestUpdates: _verbosity >= VerbosityOptions.detailed);
+
+ return manifestUpdater.CalculateManifestUpdatesForWorkloadSet(workloadSet);
+ }
+}
diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.cs.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.cs.xlf
index 69f0ea0788e1..1512b9be7de3 100644
--- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.cs.xlf
+++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.cs.xlf
@@ -4074,6 +4074,11 @@ Pokud chcete zobrazit hodnotu, zadejte odpovídající volbu příkazového řá
Verze {0} úlohy, která byla zadána v {1}, nebyla nalezena. Spuštěním příkazu dotnet workload restore nainstalujte tuto verzi úlohy.{Locked="dotnet workload restore"}
+
+ Workload set version {0} has missing manifests likely removed by package management. Run "dotnet workload repair" to fix this.
+ Workload set version {0} has missing manifests likely removed by package management. Run "dotnet workload repair" to fix this.
+ {0} is the workload set version. {Locked="dotnet workload repair"}
+ A workload version to display or one or more workloads and their versions joined by the '@' character.Verze úlohy, která se má zobrazit, nebo jedna nebo více úloh a jejich verze spojené znakem @.
diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.de.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.de.xlf
index e3a05f3c7545..6648f17aedbe 100644
--- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.de.xlf
+++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.de.xlf
@@ -4074,6 +4074,11 @@ Um einen Wert anzuzeigen, geben Sie die entsprechende Befehlszeilenoption an, oh
Die Arbeitsauslastungsversion {0}, die in {1} angegeben wurde, wurde nicht gefunden. Führen Sie „dotnet workload restore“ aus, um diese Workloadversion zu installieren.{Locked="dotnet workload restore"}
+
+ Workload set version {0} has missing manifests likely removed by package management. Run "dotnet workload repair" to fix this.
+ Workload set version {0} has missing manifests likely removed by package management. Run "dotnet workload repair" to fix this.
+ {0} is the workload set version. {Locked="dotnet workload repair"}
+ A workload version to display or one or more workloads and their versions joined by the '@' character.Eine Workloadversion zum Anzeigen oder mindestens eine Workload und deren Versionen, die mit dem Zeichen "@" verknüpft sind.
diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.es.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.es.xlf
index 82aa42c23b79..93d84a383f4e 100644
--- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.es.xlf
+++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.es.xlf
@@ -4074,6 +4074,11 @@ Para mostrar un valor, especifique la opción de línea de comandos correspondie
No se encontró la versión de carga de trabajo {0}, que se especificó en {1}. Ejecuta "dotnet workload restore" para instalar esta versión de carga de trabajo.{Locked="dotnet workload restore"}
+
+ Workload set version {0} has missing manifests likely removed by package management. Run "dotnet workload repair" to fix this.
+ Workload set version {0} has missing manifests likely removed by package management. Run "dotnet workload repair" to fix this.
+ {0} is the workload set version. {Locked="dotnet workload repair"}
+ A workload version to display or one or more workloads and their versions joined by the '@' character.Una versión de carga de trabajo para mostrar o una o varias cargas de trabajo y sus versiones unidas por el carácter "@".
diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.fr.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.fr.xlf
index b6da938fbb90..b2524264859d 100644
--- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.fr.xlf
+++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.fr.xlf
@@ -4074,6 +4074,11 @@ Pour afficher une valeur, spécifiez l’option de ligne de commande corresponda
La version de charge de travail {0}, qui a été spécifiée dans {1}, est introuvable. Exécutez « dotnet workload restore » pour installer cette version de charge de travail.{Locked="dotnet workload restore"}
+
+ Workload set version {0} has missing manifests likely removed by package management. Run "dotnet workload repair" to fix this.
+ Workload set version {0} has missing manifests likely removed by package management. Run "dotnet workload repair" to fix this.
+ {0} is the workload set version. {Locked="dotnet workload repair"}
+ A workload version to display or one or more workloads and their versions joined by the '@' character.Version de charge de travail à afficher ou une ou plusieurs charges de travail et leurs versions jointes par le caractère « @ ».
diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.it.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.it.xlf
index d8ded9eb3783..512bf4612264 100644
--- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.it.xlf
+++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.it.xlf
@@ -4074,6 +4074,11 @@ Per visualizzare un valore, specifica l'opzione della riga di comando corrispond
La versione del carico di lavoro {0}, specificata in {1}, non è stata trovata. Eseguire "dotnet workload restore" per installare questa versione del carico di lavoro.{Locked="dotnet workload restore"}
+
+ Workload set version {0} has missing manifests likely removed by package management. Run "dotnet workload repair" to fix this.
+ Workload set version {0} has missing manifests likely removed by package management. Run "dotnet workload repair" to fix this.
+ {0} is the workload set version. {Locked="dotnet workload repair"}
+ A workload version to display or one or more workloads and their versions joined by the '@' character.Una versione del carico di lavoro da visualizzare oppure uno o più carichi di lavoro e le relative versioni unite dal carattere '@'.
diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ja.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ja.xlf
index 373d63a6721b..aa044bf2b8c3 100644
--- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ja.xlf
+++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ja.xlf
@@ -4074,6 +4074,11 @@ To display a value, specify the corresponding command-line option without provid
{1} で指定されたワークロード バージョン {0} が見つかりませんでした。"dotnet workload restore" を実行して、このワークロード バージョンをインストールします。{Locked="dotnet workload restore"}
+
+ Workload set version {0} has missing manifests likely removed by package management. Run "dotnet workload repair" to fix this.
+ Workload set version {0} has missing manifests likely removed by package management. Run "dotnet workload repair" to fix this.
+ {0} is the workload set version. {Locked="dotnet workload repair"}
+ A workload version to display or one or more workloads and their versions joined by the '@' character.表示するワークロードのバージョン、または '@' 文字で結合された 1 つ以上のワークロードとそのバージョン。
diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ko.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ko.xlf
index 6935ae03eb40..7290a2610e18 100644
--- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ko.xlf
+++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ko.xlf
@@ -4074,6 +4074,11 @@ To display a value, specify the corresponding command-line option without provid
{1}에 지정된 워크로드 버전 {0}을(를) 찾을 수 없습니다. "dotnet workload restore"을 실행하여 이 워크로드 버전을 설치합니다.{Locked="dotnet workload restore"}
+
+ Workload set version {0} has missing manifests likely removed by package management. Run "dotnet workload repair" to fix this.
+ Workload set version {0} has missing manifests likely removed by package management. Run "dotnet workload repair" to fix this.
+ {0} is the workload set version. {Locked="dotnet workload repair"}
+ A workload version to display or one or more workloads and their versions joined by the '@' character.표시할 워크로드 버전 또는 '@' 문자로 결합된 하나 이상의 워크로드와 해당 버전입니다.
diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pl.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pl.xlf
index 952dc2ec304c..5ef1ec80a17e 100644
--- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pl.xlf
+++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pl.xlf
@@ -4074,6 +4074,11 @@ Aby wyświetlić wartość, należy podać odpowiednią opcję wiersza poleceń
Nie znaleziono wersji obciążenia {0} określonej w kontenerze {1}. Uruchom polecenie „dotnet workload restore”, aby zainstalować tę wersję obciążenia.{Locked="dotnet workload restore"}
+
+ Workload set version {0} has missing manifests likely removed by package management. Run "dotnet workload repair" to fix this.
+ Workload set version {0} has missing manifests likely removed by package management. Run "dotnet workload repair" to fix this.
+ {0} is the workload set version. {Locked="dotnet workload repair"}
+ A workload version to display or one or more workloads and their versions joined by the '@' character.Wersja obciążenia do wyświetlenia lub jedno lub więcej obciążeń i ich wersji połączonych znakiem „@”.
diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pt-BR.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pt-BR.xlf
index c0d1afd99daf..0e7a0f908f2b 100644
--- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pt-BR.xlf
+++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pt-BR.xlf
@@ -4074,6 +4074,11 @@ Para exibir um valor, especifique a opção de linha de comando correspondente s
A versão da carga de trabalho {0}, especificada em {1}, não foi localizada. Execute "dotnet workload restore" para instalar esta versão da carga de trabalho.{Locked="dotnet workload restore"}
+
+ Workload set version {0} has missing manifests likely removed by package management. Run "dotnet workload repair" to fix this.
+ Workload set version {0} has missing manifests likely removed by package management. Run "dotnet workload repair" to fix this.
+ {0} is the workload set version. {Locked="dotnet workload repair"}
+ A workload version to display or one or more workloads and their versions joined by the '@' character.Uma versão de carga de trabalho para exibir ou uma ou mais cargas de trabalho e suas versões unidas pelo caractere ''@''.
diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ru.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ru.xlf
index e0611a0d0423..cfb075bb07df 100644
--- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ru.xlf
+++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ru.xlf
@@ -4075,6 +4075,11 @@ To display a value, specify the corresponding command-line option without provid
Версия рабочей нагрузки {0}, указанная в {1}, не найдена. Запустите команду "dotnet workload restore", чтобы установить эту версию рабочей нагрузки.{Locked="dotnet workload restore"}
+
+ Workload set version {0} has missing manifests likely removed by package management. Run "dotnet workload repair" to fix this.
+ Workload set version {0} has missing manifests likely removed by package management. Run "dotnet workload repair" to fix this.
+ {0} is the workload set version. {Locked="dotnet workload repair"}
+ A workload version to display or one or more workloads and their versions joined by the '@' character.Версия рабочей нагрузки для отображения или одной или нескольких рабочих нагрузок и их версий, соединенных символом "@".
diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.tr.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.tr.xlf
index 24d365bdb0e0..195553f39fa9 100644
--- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.tr.xlf
+++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.tr.xlf
@@ -4074,6 +4074,11 @@ Bir değeri görüntülemek için, bir değer sağlamadan ilgili komut satırı
{1} konumunda belirtilen {0} iş yükü sürümü bulunamadı. Bu iş yükü sürümünü yüklemek için "dotnet workload restore" komutunu çalıştırın.{Locked="dotnet workload restore"}
+
+ Workload set version {0} has missing manifests likely removed by package management. Run "dotnet workload repair" to fix this.
+ Workload set version {0} has missing manifests likely removed by package management. Run "dotnet workload repair" to fix this.
+ {0} is the workload set version. {Locked="dotnet workload repair"}
+ A workload version to display or one or more workloads and their versions joined by the '@' character.Görüntülenecek bir iş yükü sürümü veya bir veya daha fazla iş yükü ve bunların '@' karakteriyle birleştirilen sürümleri.
diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hans.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hans.xlf
index f5d749e1d50d..a9bf482bb05e 100644
--- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hans.xlf
+++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hans.xlf
@@ -4074,6 +4074,11 @@ To display a value, specify the corresponding command-line option without provid
找不到在 {1} 中指定的工作负载版本 {0}。运行“dotnet workload restore”以安装此工作负载版本。{Locked="dotnet workload restore"}
+
+ Workload set version {0} has missing manifests likely removed by package management. Run "dotnet workload repair" to fix this.
+ Workload set version {0} has missing manifests likely removed by package management. Run "dotnet workload repair" to fix this.
+ {0} is the workload set version. {Locked="dotnet workload repair"}
+ A workload version to display or one or more workloads and their versions joined by the '@' character.要显示的工作负载版本,或一个或多个工作负载,并且其版本由 ‘@’ 字符联接。
diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hant.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hant.xlf
index 93e2b84eaee7..39969f14662e 100644
--- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hant.xlf
+++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hant.xlf
@@ -4074,6 +4074,11 @@ To display a value, specify the corresponding command-line option without provid
找不到 {1} 中指定的工作負載版本 {0}。執行 "dotnet workload restore" 以安裝此工作負載版本。{Locked="dotnet workload restore"}
+
+ Workload set version {0} has missing manifests likely removed by package management. Run "dotnet workload repair" to fix this.
+ Workload set version {0} has missing manifests likely removed by package management. Run "dotnet workload repair" to fix this.
+ {0} is the workload set version. {Locked="dotnet workload repair"}
+ A workload version to display or one or more workloads and their versions joined by the '@' character.要顯示的工作負載版本,或是一或多個工作負載及其由 '@' 字元連接的版本。
diff --git a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/IWorkloadManifestCorruptionRepairer.cs b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/IWorkloadManifestCorruptionRepairer.cs
new file mode 100644
index 000000000000..4c3a9e907ba9
--- /dev/null
+++ b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/IWorkloadManifestCorruptionRepairer.cs
@@ -0,0 +1,18 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.NET.Sdk.WorkloadManifestReader
+{
+ ///
+ /// Provides a hook for the CLI layer to detect and repair corrupt workload manifest installations
+ /// before the manifests are loaded by the resolver.
+ ///
+ public interface IWorkloadManifestCorruptionRepairer
+ {
+ ///
+ /// Ensures that the manifests required by the current resolver are present and healthy.
+ ///
+ /// How to handle corruption if detected.
+ void EnsureManifestsHealthy(ManifestCorruptionFailureMode failureMode);
+ }
+}
diff --git a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/IWorkloadManifestProvider.cs b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/IWorkloadManifestProvider.cs
index 4a2df9d5ecaa..887b170fcd04 100644
--- a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/IWorkloadManifestProvider.cs
+++ b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/IWorkloadManifestProvider.cs
@@ -3,6 +3,30 @@
namespace Microsoft.NET.Sdk.WorkloadManifestReader
{
+ ///
+ /// Specifies how the manifest provider should handle corrupt or missing workload manifests.
+ ///
+ public enum ManifestCorruptionFailureMode
+ {
+ ///
+ /// Attempt to repair using the CorruptionRepairer if available, otherwise throw.
+ /// This is the default mode for commands that modify workloads.
+ ///
+ Repair,
+
+ ///
+ /// Throw a helpful error message suggesting how to fix the issue.
+ /// Use this for read-only/info commands.
+ ///
+ Throw,
+
+ ///
+ /// Silently ignore missing manifests and continue.
+ /// Use this for history recording or other scenarios where missing manifests are acceptable.
+ ///
+ Ignore
+ }
+
///
/// This abstracts out the process of locating and loading a set of manifests to be loaded into a
/// workload manifest resolver and resolved into a single coherent model.
diff --git a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/SdkDirectoryWorkloadManifestProvider.cs b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/SdkDirectoryWorkloadManifestProvider.cs
index c3da2743abc3..7329564dd6bf 100644
--- a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/SdkDirectoryWorkloadManifestProvider.cs
+++ b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/SdkDirectoryWorkloadManifestProvider.cs
@@ -6,6 +6,7 @@
using Microsoft.DotNet.Cli;
using Microsoft.DotNet.Cli.Commands.Workload;
using Microsoft.NET.Sdk.Localization;
+using Microsoft.DotNet.Cli.Commands;
using static Microsoft.NET.Sdk.WorkloadManifestReader.IWorkloadManifestProvider;
namespace Microsoft.NET.Sdk.WorkloadManifestReader
@@ -31,6 +32,24 @@ public partial class SdkDirectoryWorkloadManifestProvider : IWorkloadManifestPro
private bool _useManifestsFromInstallState = true;
private bool? _globalJsonSpecifiedWorkloadSets = null;
+ ///
+ /// Optional hook that allows the CLI to ensure workload manifests are available (and repaired if necessary)
+ /// before this provider attempts to enumerate them.
+ ///
+ public IWorkloadManifestCorruptionRepairer? CorruptionRepairer { get; set; }
+
+ ///
+ /// Specifies how this provider should handle corrupt or missing workload manifests.
+ /// Default is .
+ ///
+ public ManifestCorruptionFailureMode CorruptionFailureMode { get; set; } = ManifestCorruptionFailureMode.Repair;
+
+ ///
+ /// Gets the resolved workload set, if any. This is populated during construction/refresh
+ /// and does not trigger corruption checking.
+ ///
+ public WorkloadSet? ResolvedWorkloadSet => _workloadSet;
+
// This will be non-null if there is an error loading manifests that should be thrown when they need to be accessed.
// We delay throwing the error so that in the case where global.json specifies a workload set that isn't installed,
// we can successfully construct a resolver and install that workload set
@@ -247,6 +266,19 @@ void ThrowExceptionIfManifestsNotAvailable()
public WorkloadVersionInfo GetWorkloadVersion()
{
+ if (CorruptionRepairer != null)
+ {
+ CorruptionRepairer.EnsureManifestsHealthy(CorruptionFailureMode);
+ }
+ else if (_workloadSet != null && CorruptionFailureMode != ManifestCorruptionFailureMode.Ignore)
+ {
+ // No repairer attached - check for missing manifests and throw a helpful error
+ if (HasMissingManifests(_workloadSet))
+ {
+ throw new InvalidOperationException(string.Format(Strings.WorkloadSetHasMissingManifests, _workloadSet.Version));
+ }
+ }
+
if (_globalJsonWorkloadSetVersion != null)
{
// _exceptionToThrow is set to null here if and only if the workload set is not installed.
@@ -290,6 +322,18 @@ public WorkloadVersionInfo GetWorkloadVersion()
public IEnumerable GetManifests()
{
+ if (CorruptionRepairer != null)
+ {
+ CorruptionRepairer.EnsureManifestsHealthy(CorruptionFailureMode);
+ }
+ else if (_workloadSet != null && CorruptionFailureMode != ManifestCorruptionFailureMode.Ignore)
+ {
+ // No repairer attached - check for missing manifests and throw a helpful error
+ if (HasMissingManifests(_workloadSet))
+ {
+ throw new InvalidOperationException(string.Format(Strings.WorkloadSetHasMissingManifests, _workloadSet.Version));
+ }
+ }
ThrowExceptionIfManifestsNotAvailable();
// Scan manifest directories
@@ -363,6 +407,10 @@ void ProbeDirectory(string manifestDirectory, string featureBand)
var manifestDirectory = GetManifestDirectoryFromSpecifier(manifestSpecifier);
if (manifestDirectory == null)
{
+ if (CorruptionFailureMode == ManifestCorruptionFailureMode.Ignore)
+ {
+ continue;
+ }
throw new FileNotFoundException(string.Format(Strings.ManifestFromWorkloadSetNotFound, manifestSpecifier.ToString(), _workloadSet.Version));
}
AddManifest(manifestSpecifier.Id.ToString(), manifestDirectory, manifestSpecifier.FeatureBand.ToString(), kvp.Value.Version.ToString());
@@ -380,6 +428,10 @@ void ProbeDirectory(string manifestDirectory, string featureBand)
var manifestDirectory = GetManifestDirectoryFromSpecifier(manifestSpecifier);
if (manifestDirectory == null)
{
+ if (CorruptionFailureMode == ManifestCorruptionFailureMode.Ignore)
+ {
+ continue;
+ }
throw new FileNotFoundException(string.Format(Strings.ManifestFromInstallStateNotFound, manifestSpecifier.ToString(), _installStateFilePath));
}
AddManifest(manifestSpecifier.Id.ToString(), manifestDirectory, manifestSpecifier.FeatureBand.ToString(), kvp.Value.Version.ToString());
@@ -510,6 +562,23 @@ void ProbeDirectory(string manifestDirectory, string featureBand)
return null;
}
+ ///
+ /// Checks if the workload set has any manifests that are missing from disk.
+ /// This checks all manifest roots (including user-local installs).
+ ///
+ public bool HasMissingManifests(WorkloadSet workloadSet)
+ {
+ foreach (var manifestEntry in workloadSet.ManifestVersions)
+ {
+ var manifestSpecifier = new ManifestSpecifier(manifestEntry.Key, manifestEntry.Value.Version, manifestEntry.Value.FeatureBand);
+ if (GetManifestDirectoryFromSpecifier(manifestSpecifier) == null)
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
///
/// Returns installed workload sets that are available for this SDK (ie are in the same feature band)
///
@@ -538,7 +607,7 @@ Dictionary GetAvailableWorkloadSetsInternal(SdkFeatureBand?
}
else
{
- // Get workload sets for all feature bands
+ // Get workload sets for all feature bands
foreach (var featureBandDirectory in Directory.GetDirectories(manifestRoot))
{
AddWorkloadSetsForFeatureBand(availableWorkloadSets, featureBandDirectory);
diff --git a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/Strings.resx b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/Strings.resx
index cf418daa75cb..e583daad115c 100644
--- a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/Strings.resx
+++ b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/Strings.resx
@@ -1,17 +1,17 @@
-
@@ -213,5 +213,9 @@
No manifest with ID {0} exists.
+
+ Workload set version {0} has missing manifests likely removed by package management. Run "dotnet workload repair" to fix this.
+ {Locked="dotnet workload repair"}
+
diff --git a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/xlf/Strings.cs.xlf b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/xlf/Strings.cs.xlf
index 1891cc4ff30b..e52c35088d8a 100644
--- a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/xlf/Strings.cs.xlf
+++ b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/xlf/Strings.cs.xlf
@@ -142,6 +142,11 @@
Nevyřešený cíl {0} pro přesměrování úlohy {1} v manifestu {2} [{3}]
+
+ Workload set version {0} has missing manifests likely removed by package management. Run "dotnet workload repair" to fix this.
+ Workload set version {0} has missing manifests likely removed by package management. Run "dotnet workload repair" to fix this.
+ {Locked="dotnet workload repair"}
+ Workload version {0}, which was specified in {1}, was not found. Run "dotnet workload restore" to install this workload version.Verze {0} úlohy, která byla zadána v {1}, nebyla nalezena. Spuštěním příkazu dotnet workload restore nainstalujte tuto verzi úlohy.
diff --git a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/xlf/Strings.de.xlf b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/xlf/Strings.de.xlf
index aa35c3838c22..c1b3de9e9b04 100644
--- a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/xlf/Strings.de.xlf
+++ b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/xlf/Strings.de.xlf
@@ -142,6 +142,11 @@
Nicht aufgelöstes Ziel „{0}“ für die Workloadumleitung „{1}“ im Manifest „{2}“ [{3}]
+
+ Workload set version {0} has missing manifests likely removed by package management. Run "dotnet workload repair" to fix this.
+ Workload set version {0} has missing manifests likely removed by package management. Run "dotnet workload repair" to fix this.
+ {Locked="dotnet workload repair"}
+ Workload version {0}, which was specified in {1}, was not found. Run "dotnet workload restore" to install this workload version.Die Arbeitsauslastungsversion {0}, die in {1} angegeben wurde, wurde nicht gefunden. Führen Sie „dotnet workload restore“ aus, um diese Workloadversion zu installieren.
diff --git a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/xlf/Strings.es.xlf b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/xlf/Strings.es.xlf
index 485e6e0a0cf8..91127d3307bf 100644
--- a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/xlf/Strings.es.xlf
+++ b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/xlf/Strings.es.xlf
@@ -142,6 +142,11 @@
Destino sin resolver '{0}' para redirección de carga de trabajo '{1}' en el manifiesto '{2}' [{3}]
+
+ Workload set version {0} has missing manifests likely removed by package management. Run "dotnet workload repair" to fix this.
+ Workload set version {0} has missing manifests likely removed by package management. Run "dotnet workload repair" to fix this.
+ {Locked="dotnet workload repair"}
+ Workload version {0}, which was specified in {1}, was not found. Run "dotnet workload restore" to install this workload version.No se encontró la versión de carga de trabajo {0}, que se especificó en {1}. Ejecuta "dotnet workload restore" para instalar esta versión de carga de trabajo.
diff --git a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/xlf/Strings.fr.xlf b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/xlf/Strings.fr.xlf
index 0ae7f6800bd3..550221d36e00 100644
--- a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/xlf/Strings.fr.xlf
+++ b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/xlf/Strings.fr.xlf
@@ -142,6 +142,11 @@
Cible non résolue « {0} » pour la redirection de charge de travail « {1} » dans le manifeste « {2} » [{3}]
+
+ Workload set version {0} has missing manifests likely removed by package management. Run "dotnet workload repair" to fix this.
+ Workload set version {0} has missing manifests likely removed by package management. Run "dotnet workload repair" to fix this.
+ {Locked="dotnet workload repair"}
+ Workload version {0}, which was specified in {1}, was not found. Run "dotnet workload restore" to install this workload version.La version de charge de travail {0}, qui a été spécifiée dans {1}, est introuvable. Exécutez « dotnet workload restore » pour installer cette version de charge de travail.
diff --git a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/xlf/Strings.it.xlf b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/xlf/Strings.it.xlf
index 7c666799e412..b2124effaba4 100644
--- a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/xlf/Strings.it.xlf
+++ b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/xlf/Strings.it.xlf
@@ -142,6 +142,11 @@
Destinazione non risolta '{0}' per il reindirizzamento del carico di lavoro '{1}' nel manifesto '{2}' [{3}]
+
+ Workload set version {0} has missing manifests likely removed by package management. Run "dotnet workload repair" to fix this.
+ Workload set version {0} has missing manifests likely removed by package management. Run "dotnet workload repair" to fix this.
+ {Locked="dotnet workload repair"}
+ Workload version {0}, which was specified in {1}, was not found. Run "dotnet workload restore" to install this workload version.La versione del carico di lavoro {0}, specificata in {1}, non è stata trovata. Eseguire "dotnet workload restore" per installare questa versione del carico di lavoro.
diff --git a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/xlf/Strings.ja.xlf b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/xlf/Strings.ja.xlf
index cc172807eaa9..5254999963fa 100644
--- a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/xlf/Strings.ja.xlf
+++ b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/xlf/Strings.ja.xlf
@@ -142,6 +142,11 @@
マニフェスト '{2}' [{3}] 内のワークロード リダイレクト '{1}' に対する未解決のターゲット '{0}'
+
+ Workload set version {0} has missing manifests likely removed by package management. Run "dotnet workload repair" to fix this.
+ Workload set version {0} has missing manifests likely removed by package management. Run "dotnet workload repair" to fix this.
+ {Locked="dotnet workload repair"}
+ Workload version {0}, which was specified in {1}, was not found. Run "dotnet workload restore" to install this workload version.{1} で指定されたワークロード バージョン {0} が見つかりませんでした。"dotnet workload restore" を実行して、このワークロード バージョンをインストールします。
diff --git a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/xlf/Strings.ko.xlf b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/xlf/Strings.ko.xlf
index 385531b1c48b..06a3d8b1e20b 100644
--- a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/xlf/Strings.ko.xlf
+++ b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/xlf/Strings.ko.xlf
@@ -142,6 +142,11 @@
매니페스트 '{2}' [{3}]의 워크로드 리디렉션 '{1}'에 대한 확인되지 않는 대상 '{0}'
+
+ Workload set version {0} has missing manifests likely removed by package management. Run "dotnet workload repair" to fix this.
+ Workload set version {0} has missing manifests likely removed by package management. Run "dotnet workload repair" to fix this.
+ {Locked="dotnet workload repair"}
+ Workload version {0}, which was specified in {1}, was not found. Run "dotnet workload restore" to install this workload version.{1}에 지정된 워크로드 버전 {0}을(를) 찾을 수 없습니다. "dotnet workload restore"을 실행하여 이 워크로드 버전을 설치합니다.
diff --git a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/xlf/Strings.pl.xlf b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/xlf/Strings.pl.xlf
index 2f5dc549ddf0..2fb7cda88a37 100644
--- a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/xlf/Strings.pl.xlf
+++ b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/xlf/Strings.pl.xlf
@@ -142,6 +142,11 @@
Nierozpoznany element docelowy „{0}” przekierowania obciążenia „{1}” w manifeście „{2}” [{3}]
+
+ Workload set version {0} has missing manifests likely removed by package management. Run "dotnet workload repair" to fix this.
+ Workload set version {0} has missing manifests likely removed by package management. Run "dotnet workload repair" to fix this.
+ {Locked="dotnet workload repair"}
+ Workload version {0}, which was specified in {1}, was not found. Run "dotnet workload restore" to install this workload version.Nie znaleziono wersji obciążenia {0} określonej w kontenerze {1}. Uruchom polecenie „dotnet workload restore”, aby zainstalować tę wersję obciążenia.
diff --git a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/xlf/Strings.pt-BR.xlf b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/xlf/Strings.pt-BR.xlf
index f5f0331a25ec..19aa8c2b6001 100644
--- a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/xlf/Strings.pt-BR.xlf
+++ b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/xlf/Strings.pt-BR.xlf
@@ -142,6 +142,11 @@
Destino '{0}' não resolvido para o redirecionamento de carga de trabalho '{1}' no manifesto '{2}' [{3}]
+
+ Workload set version {0} has missing manifests likely removed by package management. Run "dotnet workload repair" to fix this.
+ Workload set version {0} has missing manifests likely removed by package management. Run "dotnet workload repair" to fix this.
+ {Locked="dotnet workload repair"}
+ Workload version {0}, which was specified in {1}, was not found. Run "dotnet workload restore" to install this workload version.A versão da carga de trabalho {0}, especificada em {1}, não foi localizada. Execute "dotnet workload restore" para instalar esta versão da carga de trabalho.
diff --git a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/xlf/Strings.ru.xlf b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/xlf/Strings.ru.xlf
index bf88a6af7bbe..01bf7048e135 100644
--- a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/xlf/Strings.ru.xlf
+++ b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/xlf/Strings.ru.xlf
@@ -142,6 +142,11 @@
Неразрешенный целевой объект "{0}" для перенаправления рабочей нагрузки "{1}" в манифесте "{2}" [{3}]
+
+ Workload set version {0} has missing manifests likely removed by package management. Run "dotnet workload repair" to fix this.
+ Workload set version {0} has missing manifests likely removed by package management. Run "dotnet workload repair" to fix this.
+ {Locked="dotnet workload repair"}
+ Workload version {0}, which was specified in {1}, was not found. Run "dotnet workload restore" to install this workload version.Версия рабочей нагрузки {0}, указанная в {1}, не найдена. Запустите команду "dotnet workload restore", чтобы установить эту версию рабочей нагрузки.
diff --git a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/xlf/Strings.tr.xlf b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/xlf/Strings.tr.xlf
index 786f001a3435..15e1e6425967 100644
--- a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/xlf/Strings.tr.xlf
+++ b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/xlf/Strings.tr.xlf
@@ -142,6 +142,11 @@
'{2}' [{3}] bildirimindeki '{1}' iş akışı yeniden yönlendirmesi için '{0}' hedefi çözümlenemedi
+
+ Workload set version {0} has missing manifests likely removed by package management. Run "dotnet workload repair" to fix this.
+ Workload set version {0} has missing manifests likely removed by package management. Run "dotnet workload repair" to fix this.
+ {Locked="dotnet workload repair"}
+ Workload version {0}, which was specified in {1}, was not found. Run "dotnet workload restore" to install this workload version.{1} konumunda belirtilen {0} iş yükü sürümü bulunamadı. Bu iş yükü sürümünü yüklemek için "dotnet workload restore" komutunu çalıştırın.
diff --git a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/xlf/Strings.zh-Hans.xlf b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/xlf/Strings.zh-Hans.xlf
index 911a2731d284..5770b450720b 100644
--- a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/xlf/Strings.zh-Hans.xlf
+++ b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/xlf/Strings.zh-Hans.xlf
@@ -142,6 +142,11 @@
工作负载未解析的目标“{0}”重定向到清单“{2}”[{3}] 中的“{1}”
+
+ Workload set version {0} has missing manifests likely removed by package management. Run "dotnet workload repair" to fix this.
+ Workload set version {0} has missing manifests likely removed by package management. Run "dotnet workload repair" to fix this.
+ {Locked="dotnet workload repair"}
+ Workload version {0}, which was specified in {1}, was not found. Run "dotnet workload restore" to install this workload version.找不到在 {1} 中指定的工作负载版本 {0}。运行“dotnet workload restore”以安装此工作负载版本。
diff --git a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/xlf/Strings.zh-Hant.xlf b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/xlf/Strings.zh-Hant.xlf
index 423e6216ecca..2435cecf1b39 100644
--- a/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/xlf/Strings.zh-Hant.xlf
+++ b/src/Resolvers/Microsoft.NET.Sdk.WorkloadManifestReader/xlf/Strings.zh-Hant.xlf
@@ -142,6 +142,11 @@
資訊清單 '{2}' [{3}] 中工作負載重新導向 '{1}' 的未解析目標 '{0}'
+
+ Workload set version {0} has missing manifests likely removed by package management. Run "dotnet workload repair" to fix this.
+ Workload set version {0} has missing manifests likely removed by package management. Run "dotnet workload repair" to fix this.
+ {Locked="dotnet workload repair"}
+ Workload version {0}, which was specified in {1}, was not found. Run "dotnet workload restore" to install this workload version.找不到 {1} 中指定的工作負載版本 {0}。執行 "dotnet workload restore" 以安裝此工作負載版本。
diff --git a/test/Microsoft.NET.Sdk.WorkloadManifestReader.Tests/SdkDirectoryWorkloadManifestProviderTests.cs b/test/Microsoft.NET.Sdk.WorkloadManifestReader.Tests/SdkDirectoryWorkloadManifestProviderTests.cs
index cbb52e8040c1..7ad32c244356 100644
--- a/test/Microsoft.NET.Sdk.WorkloadManifestReader.Tests/SdkDirectoryWorkloadManifestProviderTests.cs
+++ b/test/Microsoft.NET.Sdk.WorkloadManifestReader.Tests/SdkDirectoryWorkloadManifestProviderTests.cs
@@ -436,7 +436,7 @@ public void ItThrowsIfManifestFromWorkloadSetIsNotFound()
var sdkDirectoryWorkloadManifestProvider
= new SdkDirectoryWorkloadManifestProvider(sdkRootPath: _fakeDotnetRootDirectory, sdkVersion: "8.0.200", userProfileDir: null, globalJsonPath: null);
- Assert.Throws(() => GetManifestContents(sdkDirectoryWorkloadManifestProvider).ToList());
+ Assert.Throws(() => GetManifestContents(sdkDirectoryWorkloadManifestProvider).ToList());
}
[Fact]
@@ -710,9 +710,9 @@ public void ItFailsIfManifestFromWorkloadSetFromInstallStateIsNotInstalled()
var sdkDirectoryWorkloadManifestProvider
= new SdkDirectoryWorkloadManifestProvider(sdkRootPath: _fakeDotnetRootDirectory, sdkVersion: "8.0.200", userProfileDir: null, globalJsonPath: null);
- var ex = Assert.Throws(() => sdkDirectoryWorkloadManifestProvider.GetManifests().ToList());
+ var ex = Assert.Throws(() => sdkDirectoryWorkloadManifestProvider.GetManifests().ToList());
- ex.Message.Should().Be(string.Format(Strings.ManifestFromWorkloadSetNotFound, "ios: 11.0.2/8.0.100", "8.0.201"));
+ ex.Message.Should().Be(string.Format(Strings.WorkloadSetHasMissingManifests, "8.0.201"));
}
[Fact]
diff --git a/test/dotnet.Tests/CommandTests/Workload/Install/CorruptWorkloadSetTestHelper.cs b/test/dotnet.Tests/CommandTests/Workload/Install/CorruptWorkloadSetTestHelper.cs
new file mode 100644
index 000000000000..e42c4ea223cd
--- /dev/null
+++ b/test/dotnet.Tests/CommandTests/Workload/Install/CorruptWorkloadSetTestHelper.cs
@@ -0,0 +1,114 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using ManifestReaderTests;
+using Microsoft.DotNet.Cli.Commands.Workload;
+using Microsoft.DotNet.Cli.Commands.Workload.Install;
+using Microsoft.DotNet.Cli.NuGetPackageDownloader;
+using Microsoft.DotNet.Cli.Utils;
+using Microsoft.NET.Sdk.WorkloadManifestReader;
+
+namespace Microsoft.DotNet.Cli.Workload.Install.Tests
+{
+ ///
+ /// Test helper for setting up corrupt workload set scenarios.
+ ///
+ internal static class CorruptWorkloadSetTestHelper
+ {
+ ///
+ /// Sets up a corrupt workload set scenario where manifests are missing but workload set is configured.
+ /// This simulates package managers deleting manifests during SDK updates.
+ /// Returns a real SdkDirectoryWorkloadManifestProvider so the corruption repairer can be attached.
+ ///
+ public static (string dotnetRoot, string userProfileDir, MockPackWorkloadInstaller mockInstaller, IWorkloadResolver workloadResolver, SdkDirectoryWorkloadManifestProvider manifestProvider)
+ SetupCorruptWorkloadSet(
+ TestAssetsManager testAssetsManager,
+ bool userLocal,
+ out string sdkFeatureVersion)
+ {
+ var testDirectory = testAssetsManager.CreateTestDirectory(identifier: userLocal ? "userlocal" : "default").Path;
+ var dotnetRoot = Path.Combine(testDirectory, "dotnet");
+ var userProfileDir = Path.Combine(testDirectory, "user-profile");
+ sdkFeatureVersion = "6.0.100";
+ var workloadSetVersion = "6.0.100";
+
+ // Create workload set contents JSON for the current (corrupt) version
+ var workloadSetJson = """
+{
+ "xamarin-android-build": "8.4.7/6.0.100",
+ "xamarin-ios-sdk": "10.0.1/6.0.100"
+}
+""";
+
+ // Create workload set contents for the updated version
+ var workloadSetJsonUpdated = """
+{
+ "xamarin-android-build": "8.4.8/6.0.100",
+ "xamarin-ios-sdk": "10.0.2/6.0.100"
+}
+""";
+
+ // Create workload set contents for the mock installer
+ var workloadSetContents = new Dictionary
+ {
+ [workloadSetVersion] = workloadSetJson,
+ ["6.0.101"] = workloadSetJsonUpdated
+ };
+
+ // Set up mock installer with workload set support
+ // Note: Don't pre-populate installedWorkloads - the test focuses on manifest repair, not workload installation
+ var mockInstaller = new MockPackWorkloadInstaller(
+ dotnetDir: dotnetRoot,
+ installedWorkloads: new List(),
+ workloadSetContents: workloadSetContents);
+
+ string installRoot = userLocal ? userProfileDir : dotnetRoot;
+ if (userLocal)
+ {
+ WorkloadFileBasedInstall.SetUserLocal(dotnetRoot, sdkFeatureVersion);
+ }
+
+ // Create install state with workload set version
+ var installStateDir = Path.Combine(installRoot, "metadata", "workloads", RuntimeInformation.ProcessArchitecture.ToString(), sdkFeatureVersion, "InstallState");
+ Directory.CreateDirectory(installStateDir);
+ var installStatePath = Path.Combine(installStateDir, "default.json");
+ var installState = new InstallStateContents
+ {
+ UseWorkloadSets = true,
+ WorkloadVersion = workloadSetVersion,
+ Manifests = new Dictionary
+ {
+ ["xamarin-android-build"] = "8.4.7",
+ ["xamarin-ios-sdk"] = "10.0.1"
+ }
+ };
+ File.WriteAllText(installStatePath, installState.ToString());
+
+ // Create workload set folder so the real provider can find it
+ var workloadSetsRoot = Path.Combine(dotnetRoot, "sdk-manifests", sdkFeatureVersion, "workloadsets", workloadSetVersion);
+ Directory.CreateDirectory(workloadSetsRoot);
+ File.WriteAllText(Path.Combine(workloadSetsRoot, "workloadset.workloadset.json"), workloadSetJson);
+
+ // Create mock manifest directories but WITHOUT manifest files to simulate ruined install
+ var manifestRoot = Path.Combine(dotnetRoot, "sdk-manifests", sdkFeatureVersion);
+ var androidManifestDir = Path.Combine(manifestRoot, "xamarin-android-build", "8.4.7");
+ var iosManifestDir = Path.Combine(manifestRoot, "xamarin-ios-sdk", "10.0.1");
+ Directory.CreateDirectory(androidManifestDir);
+ Directory.CreateDirectory(iosManifestDir);
+
+ // Verify manifests don't exist (simulating the ruined install)
+ if (File.Exists(Path.Combine(androidManifestDir, "WorkloadManifest.json")) ||
+ File.Exists(Path.Combine(iosManifestDir, "WorkloadManifest.json")))
+ {
+ throw new InvalidOperationException("Test setup failed: manifest files should not exist");
+ }
+
+ // Create a real SdkDirectoryWorkloadManifestProvider
+ var manifestProvider = new SdkDirectoryWorkloadManifestProvider(dotnetRoot, sdkFeatureVersion, userProfileDir, globalJsonPath: null);
+ var workloadResolver = WorkloadResolver.Create(manifestProvider, dotnetRoot, sdkFeatureVersion, userProfileDir);
+ mockInstaller.WorkloadResolver = workloadResolver;
+
+ return (dotnetRoot, userProfileDir, mockInstaller, workloadResolver, manifestProvider);
+ }
+ }
+}
diff --git a/test/dotnet.Tests/CommandTests/Workload/Install/MockPackWorkloadInstaller.cs b/test/dotnet.Tests/CommandTests/Workload/Install/MockPackWorkloadInstaller.cs
index c1e7354d9d21..9c7bed6b7e9a 100644
--- a/test/dotnet.Tests/CommandTests/Workload/Install/MockPackWorkloadInstaller.cs
+++ b/test/dotnet.Tests/CommandTests/Workload/Install/MockPackWorkloadInstaller.cs
@@ -139,7 +139,11 @@ public IEnumerable GetWorkloadHistoryRecords(string sdkFe
return HistoryRecords;
}
- public void RepairWorkloads(IEnumerable workloadIds, SdkFeatureBand sdkFeatureBand, DirectoryPath? offlineCache = null) => throw new NotImplementedException();
+ public void RepairWorkloads(IEnumerable workloadIds, SdkFeatureBand sdkFeatureBand, DirectoryPath? offlineCache = null)
+ {
+ // Repair is essentially a reinstall of existing workloads
+ CliTransaction.RunNew(context => InstallWorkloads(workloadIds, sdkFeatureBand, context, offlineCache));
+ }
public void GarbageCollect(Func getResolverForWorkloadSet, DirectoryPath? offlineCache = null, bool cleanAllPacks = false)
{
@@ -160,6 +164,28 @@ public IWorkloadInstallationRecordRepository GetWorkloadInstallationRecordReposi
public void InstallWorkloadManifest(ManifestVersionUpdate manifestUpdate, ITransactionContext transactionContext, DirectoryPath? offlineCache = null)
{
InstalledManifests.Add((manifestUpdate, offlineCache));
+
+ // Also create the actual manifest file on disk so that SdkDirectoryWorkloadManifestProvider can find it
+ if (_dotnetDir != null)
+ {
+ var manifestDir = Path.Combine(_dotnetDir, "sdk-manifests", manifestUpdate.NewFeatureBand,
+ manifestUpdate.ManifestId.ToString(), manifestUpdate.NewVersion.ToString());
+ Directory.CreateDirectory(manifestDir);
+
+ var manifestPath = Path.Combine(manifestDir, "WorkloadManifest.json");
+ if (!File.Exists(manifestPath))
+ {
+ // Write a minimal manifest file
+ string manifestContents = $$"""
+{
+ "version": "{{manifestUpdate.NewVersion}}",
+ "workloads": {},
+ "packs": {}
+}
+""";
+ File.WriteAllText(manifestPath, manifestContents);
+ }
+ }
}
public IEnumerable GetDownloads(IEnumerable workloadIds, SdkFeatureBand sdkFeatureBand, bool includeInstalledItems)
diff --git a/test/dotnet.Tests/CommandTests/Workload/Repair/GivenDotnetWorkloadRepair.cs b/test/dotnet.Tests/CommandTests/Workload/Repair/GivenDotnetWorkloadRepair.cs
index 4dc46351eae2..6d6d89fb7149 100644
--- a/test/dotnet.Tests/CommandTests/Workload/Repair/GivenDotnetWorkloadRepair.cs
+++ b/test/dotnet.Tests/CommandTests/Workload/Repair/GivenDotnetWorkloadRepair.cs
@@ -1,17 +1,18 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-#nullable disable
-
using System.CommandLine;
using System.Text.Json;
using ManifestReaderTests;
using Microsoft.DotNet.Cli.Commands;
+using Microsoft.DotNet.Cli.Commands.Workload;
using Microsoft.DotNet.Cli.Commands.Workload.Install;
using Microsoft.DotNet.Cli.Commands.Workload.Repair;
using Microsoft.DotNet.Cli.NuGetPackageDownloader;
+using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.Cli.Workload.Install.Tests;
using Microsoft.NET.Sdk.WorkloadManifestReader;
+using static Microsoft.NET.Sdk.WorkloadManifestReader.IWorkloadManifestProvider;
namespace Microsoft.DotNet.Cli.Workload.Repair.Tests
{
@@ -85,7 +86,7 @@ public void GivenExtraPacksInstalledRepairGarbageCollects(bool userLocal)
// Add extra pack dirs and records
var extraPackRecordPath = Path.Combine(installRoot, "metadata", "workloads", "InstalledPacks", "v1", "Test.Pack.A", "1.0.0", sdkFeatureVersion);
- Directory.CreateDirectory(Path.GetDirectoryName(extraPackRecordPath));
+ Directory.CreateDirectory(Path.GetDirectoryName(extraPackRecordPath)!);
var extraPackPath = Path.Combine(installRoot, "packs", "Test.Pack.A", "1.0.0");
Directory.CreateDirectory(extraPackPath);
var packRecordContents = JsonSerializer.Serialize(new(new WorkloadPackId("Test.Pack.A"), "1.0.0", WorkloadPackKind.Sdk, extraPackPath, "Test.Pack.A"));
@@ -151,5 +152,43 @@ public void GivenMissingPacksRepairFixesInstall(bool userLocal)
Directory.GetDirectories(Path.Combine(installRoot, "packs")).Length.Should().Be(7);
Directory.GetDirectories(Path.Combine(installRoot, "metadata", "workloads", "InstalledPacks", "v1")).Length.Should().Be(8);
}
+
+ [Theory]
+ [InlineData(true)]
+ [InlineData(false)]
+ public void GivenMissingManifestsInWorkloadSetModeRepairReinstallsManifests(bool userLocal)
+ {
+ var (dotnetRoot, userProfileDir, mockInstaller, workloadResolver, manifestProvider) =
+ CorruptWorkloadSetTestHelper.SetupCorruptWorkloadSet(_testAssetsManager, userLocal, out string sdkFeatureVersion);
+
+ mockInstaller.InstalledManifests.Should().HaveCount(0);
+
+ var workloadResolverFactory = new MockWorkloadResolverFactory(dotnetRoot, sdkFeatureVersion, workloadResolver, userProfileDir);
+
+ // Attach the corruption repairer to the manifest provider
+ var nugetDownloader = new MockNuGetPackageDownloader(dotnetRoot, manifestDownload: true);
+ var corruptionRepairer = new WorkloadManifestCorruptionRepairer(
+ _reporter,
+ mockInstaller,
+ workloadResolver,
+ new SdkFeatureBand(sdkFeatureVersion),
+ dotnetRoot,
+ userProfileDir,
+ nugetDownloader,
+ packageSourceLocation: null,
+ VerbosityOptions.detailed);
+ manifestProvider.CorruptionRepairer = corruptionRepairer;
+
+ // Directly trigger the manifest health check and repair
+ corruptionRepairer.EnsureManifestsHealthy(ManifestCorruptionFailureMode.Repair);
+
+ // Verify that manifests were installed by the corruption repairer
+ mockInstaller.InstalledManifests.Should().HaveCount(2, "Manifests should be installed after EnsureManifestsHealthy call");
+ mockInstaller.InstalledManifests.Should().Contain(m => m.manifestUpdate.ManifestId.ToString() == "xamarin-android-build");
+ mockInstaller.InstalledManifests.Should().Contain(m => m.manifestUpdate.ManifestId.ToString() == "xamarin-ios-sdk");
+
+ // Verify the repair process was triggered (the corruption repairer shows this message)
+ _reporter.Lines.Should().Contain(line => line.Contains("Repairing workload set"));
+ }
}
}
diff --git a/test/dotnet.Tests/CommandTests/Workload/Restore/GivenDotnetWorkloadRestore.cs b/test/dotnet.Tests/CommandTests/Workload/Restore/GivenDotnetWorkloadRestore.cs
index 301293b0141f..76b0c8453e00 100644
--- a/test/dotnet.Tests/CommandTests/Workload/Restore/GivenDotnetWorkloadRestore.cs
+++ b/test/dotnet.Tests/CommandTests/Workload/Restore/GivenDotnetWorkloadRestore.cs
@@ -15,7 +15,7 @@ public GivenDotnetWorkloadRestore(ITestOutputHelper log) : base(log)
[Fact]
public void ProjectsThatDoNotSupportWorkloadsAreNotInspected()
{
- if(IsRunningInContainer())
+ if (IsRunningInContainer())
{
// Skipping test in a Helix container environment due to read-only DOTNET_ROOT, which causes workload restore to fail when writing workload metadata.
return;
@@ -38,7 +38,7 @@ public void ProjectsThatDoNotSupportWorkloadsAreNotInspected()
[Fact]
public void ProjectsThatDoNotSupportWorkloadsAndAreTransitivelyReferencedDoNotBreakTheBuild()
{
- if(IsRunningInContainer())
+ if (IsRunningInContainer())
{
// Skipping test in a Helix container environment due to read-only DOTNET_ROOT, which causes workload restore to fail when writing workload metadata.
return;
diff --git a/test/dotnet.Tests/CommandTests/Workload/Search/MockWorkloadResolver.cs b/test/dotnet.Tests/CommandTests/Workload/Search/MockWorkloadResolver.cs
index ca5c48b55f71..52f0dc4f56cd 100644
--- a/test/dotnet.Tests/CommandTests/Workload/Search/MockWorkloadResolver.cs
+++ b/test/dotnet.Tests/CommandTests/Workload/Search/MockWorkloadResolver.cs
@@ -3,6 +3,7 @@
#nullable disable
+using System.Reflection.Metadata.Ecma335;
using Microsoft.NET.Sdk.WorkloadManifestReader;
namespace Microsoft.DotNet.Cli.Workload.Search.Tests
@@ -14,19 +15,22 @@ public class MockWorkloadResolver : IWorkloadResolver
private readonly Func> _getPacksInWorkload;
private readonly Func _getPackInfo;
private readonly Func _getManifest;
+ private readonly IWorkloadManifestProvider _manifestProvider;
public MockWorkloadResolver(
IEnumerable availableWorkloads,
IEnumerable installedManifests = null,
Func> getPacks = null,
Func getPackInfo = null,
- Func getManifest = null)
+ Func getManifest = null,
+ IWorkloadManifestProvider manifestProvider = null)
{
_availableWorkloads = availableWorkloads;
_installedManifests = installedManifests;
_getPacksInWorkload = getPacks;
_getPackInfo = getPackInfo;
_getManifest = getManifest;
+ _manifestProvider = manifestProvider;
}
public IEnumerable GetAvailableWorkloads() => _availableWorkloads;
@@ -48,6 +52,6 @@ public void RefreshWorkloadManifests() { /* noop */ }
public IEnumerable GetUpdatedWorkloads(WorkloadResolver advertisingManifestResolver, IEnumerable installedWorkloads) => throw new NotImplementedException();
WorkloadResolver IWorkloadResolver.CreateOverlayResolver(IWorkloadManifestProvider overlayManifestProvider) => throw new NotImplementedException();
WorkloadManifest IWorkloadResolver.GetManifestFromWorkload(WorkloadId workloadId) => _getManifest?.Invoke(workloadId) ?? throw new NotImplementedException();
- public IWorkloadManifestProvider GetWorkloadManifestProvider() => throw new NotImplementedException();
+ public IWorkloadManifestProvider GetWorkloadManifestProvider() => _manifestProvider ?? throw new NotImplementedException();
}
}
diff --git a/test/dotnet.Tests/CommandTests/Workload/Update/GivenDotnetWorkloadUpdate.cs b/test/dotnet.Tests/CommandTests/Workload/Update/GivenDotnetWorkloadUpdate.cs
index 3f0a820e801d..ad9d1f77b290 100644
--- a/test/dotnet.Tests/CommandTests/Workload/Update/GivenDotnetWorkloadUpdate.cs
+++ b/test/dotnet.Tests/CommandTests/Workload/Update/GivenDotnetWorkloadUpdate.cs
@@ -67,13 +67,15 @@ public void GivenWorkloadUpdateFromHistory()
IEnumerable installedManifests = new List() {
new WorkloadManifestInfo("microsoft.net.sdk.android", "34.0.0-rc.1", "androidDirectory", "8.0.100-rc.1"),
new WorkloadManifestInfo("microsoft.net.sdk.ios", "16.4.8825", "iosDirectory", "8.0.100-rc.1") };
+ var manifestProvider = new MockManifestProvider(Array.Empty()) { SdkFeatureBand = new SdkFeatureBand("8.0.100-rc.1") };
var workloadResolver = new MockWorkloadResolver(
new string[] { "maui-android", "maui-ios" }.Select(s => new WorkloadInfo(new WorkloadId(s), null)),
installedManifests,
id => new List() { new WorkloadPackId(id.ToString() + "-pack") },
id => id.ToString().Contains("android") ? mauiAndroidPack :
- id.ToString().Contains("ios") ? mauiIosPack : null);
+ id.ToString().Contains("ios") ? mauiIosPack : null,
+ manifestProvider: manifestProvider);
IWorkloadResolverFactory mockResolverFactory = new MockWorkloadResolverFactory(
Path.Combine(Path.GetTempPath(), "dotnetTestPath"),
@@ -304,7 +306,8 @@ public void UpdateViaWorkloadSet(bool upgrade, bool? installStateUseWorkloadSet,
}
";
var nugetPackageDownloader = new MockNuGetPackageDownloader();
- var workloadResolver = new MockWorkloadResolver([new WorkloadInfo(new WorkloadId("android"), string.Empty)], getPacks: id => [], installedManifests: []);
+ var manifestProvider = new MockManifestProvider(Array.Empty()) { SdkFeatureBand = new SdkFeatureBand(sdkVersion) };
+ var workloadResolver = new MockWorkloadResolver([new WorkloadInfo(new WorkloadId("android"), string.Empty)], getPacks: id => [], installedManifests: [], manifestProvider: manifestProvider);
var workloadInstaller = new MockPackWorkloadInstaller(
dotnetDir,
installedWorkloads: [new WorkloadId("android")],
@@ -375,13 +378,16 @@ public void GivenWorkloadUpdateItFindsGreatestWorkloadSetWithSpecifiedComponents
WorkloadManifest iosManifest = WorkloadManifest.CreateForTests("Microsoft.NET.Sdk.iOS");
WorkloadManifest macosManifest = WorkloadManifest.CreateForTests("Microsoft.NET.Sdk.macOS");
WorkloadManifest mauiManifest = WorkloadManifest.CreateForTests("Microsoft.NET.Sdk.Maui");
+ var manifestProvider = new MockManifestProvider(Array.Empty()) { SdkFeatureBand = new SdkFeatureBand("9.0.100") };
+
MockWorkloadResolver resolver = new([new WorkloadInfo(new WorkloadId("ios"), ""), new WorkloadInfo(new WorkloadId("macos"), ""), new WorkloadInfo(new WorkloadId("maui"), "")],
installedManifests: [
new WorkloadManifestInfo("Microsoft.NET.Sdk.iOS", "17.4.3", Path.Combine(testDirectory, "iosManifest"), "9.0.100"),
new WorkloadManifestInfo("Microsoft.NET.Sdk.macOS", "14.4.3", Path.Combine(testDirectory, "macosManifest"), "9.0.100"),
new WorkloadManifestInfo("Microsoft.NET.Sdk.Maui", "14.4.3", Path.Combine(testDirectory, "mauiManifest"), "9.0.100")
],
- getManifest: id => id.ToString().Equals("ios") ? iosManifest : id.ToString().Equals("macos") ? macosManifest : mauiManifest);
+ getManifest: id => id.ToString().Equals("ios") ? iosManifest : id.ToString().Equals("macos") ? macosManifest : mauiManifest,
+ manifestProvider: manifestProvider);
MockNuGetPackageDownloader nugetPackageDownloader = new(packageVersions: [new NuGetVersion("9.103.0"), new NuGetVersion("9.102.0"), new NuGetVersion("9.101.0"), new NuGetVersion("9.100.0")]);
WorkloadUpdateCommand command = new(
parseResult,
@@ -654,5 +660,55 @@ public void GivenInvalidVersionInRollbackFileItErrors()
return (dotnetRoot, installManager, installer, workloadResolver, manifestUpdater, nugetDownloader, workloadResolverFactory);
}
+
+ [Theory]
+ [InlineData(true)]
+ [InlineData(false)]
+ public void GivenMissingManifestsInWorkloadSetModeUpdateReinstallsManifests(bool userLocal)
+ {
+ var (dotnetRoot, userProfileDir, mockInstaller, workloadResolver, manifestProvider) =
+ CorruptWorkloadSetTestHelper.SetupCorruptWorkloadSet(_testAssetsManager, userLocal, out string sdkFeatureVersion);
+
+ mockInstaller.InstalledManifests.Should().HaveCount(0);
+
+ var workloadResolverFactory = new MockWorkloadResolverFactory(dotnetRoot, sdkFeatureVersion, workloadResolver, userProfileDir);
+
+ // Attach the corruption repairer to the manifest provider
+ var nugetDownloader = new MockNuGetPackageDownloader(dotnetRoot, manifestDownload: true);
+ manifestProvider.CorruptionRepairer = new WorkloadManifestCorruptionRepairer(
+ _reporter,
+ mockInstaller,
+ workloadResolver,
+ new SdkFeatureBand(sdkFeatureVersion),
+ dotnetRoot,
+ userProfileDir,
+ nugetDownloader,
+ packageSourceLocation: null,
+ VerbosityOptions.detailed);
+
+ // Advertise a NEWER workload set version than what's currently installed (6.0.100)
+ // so that the update command proceeds with manifest installation
+ var workloadManifestUpdater = new MockWorkloadManifestUpdater(
+ manifestUpdates: [
+ new ManifestUpdateWithWorkloads(new ManifestVersionUpdate(new ManifestId("xamarin-android-build"), new ManifestVersion("8.4.8"), "6.0.100"), Enumerable.Empty>().ToDictionary()),
+ new ManifestUpdateWithWorkloads(new ManifestVersionUpdate(new ManifestId("xamarin-ios-sdk"), new ManifestVersion("10.0.2"), "6.0.100"), Enumerable.Empty>().ToDictionary())
+ ],
+ fromWorkloadSet: true, workloadSetVersion: "6.0.101");
+
+ var parseResult = Parser.Parse(new string[] { "dotnet", "workload", "update" });
+
+ // Run update command
+ var updateCommand = new WorkloadUpdateCommand(parseResult, reporter: _reporter, workloadResolverFactory,
+ workloadInstaller: mockInstaller, workloadManifestUpdater: workloadManifestUpdater);
+ updateCommand.Execute();
+
+ // Verify that manifests were reinstalled
+ mockInstaller.InstalledManifests.Should().HaveCount(2);
+ mockInstaller.InstalledManifests.Should().Contain(m => m.manifestUpdate.ManifestId.ToString() == "xamarin-android-build");
+ mockInstaller.InstalledManifests.Should().Contain(m => m.manifestUpdate.ManifestId.ToString() == "xamarin-ios-sdk");
+
+ // Verify command succeeded
+ _reporter.Lines.Should().NotContain(line => line.Contains("failed", StringComparison.OrdinalIgnoreCase));
+ }
}
}