Skip to content
1 change: 1 addition & 0 deletions src/Cli/dotnet/CommandLineInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#nullable disable

using Microsoft.DotNet.Cli.Commands;
using Microsoft.DotNet.Cli.Commands.Workload;
using Microsoft.DotNet.Cli.Utils;
using LocalizableStrings = Microsoft.DotNet.Cli.Utils.LocalizableStrings;
Expand Down
4 changes: 4 additions & 0 deletions src/Cli/dotnet/Commands/CliCommandStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -2471,6 +2471,10 @@ To display a value, specify the corresponding command-line option without provid
<value>Workload version {0}, which was specified in {1}, was not found. Run "dotnet workload restore" to install this workload version.</value>
<comment>{Locked="dotnet workload restore"}</comment>
</data>
<data name="WorkloadsNotInstalledRunWorkloadUpdate" xml:space="preserve">
<value>Run "dotnet workload update" to install the workloads for this SDK.</value>
<comment>{Locked="dotnet workload update"}</comment>
</data>
<data name="WorkloadSetVersionOptionDescription" xml:space="preserve">
<value>A workload version to display or one or more workloads and their versions joined by the '@' character.</value>
</data>
Expand Down
35 changes: 34 additions & 1 deletion src/Cli/dotnet/Commands/Workload/WorkloadInfoHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,25 @@ internal static string GetWorkloadsVersion(WorkloadInfoHelper? workloadInfoHelpe
internal void ShowWorkloadsInfo(IReporter? reporter = null, string? dotnetDir = null, bool showVersion = true)
{
reporter ??= Reporter.Output;

// Get the error message if manifests are missing
// We need to call GetManifests() first to ensure _exceptionToThrow is populated
// if manifests from a workload set are missing
string? manifestError = null;
if (ManifestProvider is SdkDirectoryWorkloadManifestProvider sdkProvider)
{
// Calling GetManifests().Count() forces enumeration which populates the error state
try
{
_ = ManifestProvider.GetManifests().Count();
}
catch
{
// Ignore errors here - we'll get the message via GetManifestErrorMessage
}
manifestError = sdkProvider.GetManifestErrorMessage();
}

var versionInfo = ManifestProvider.GetWorkloadVersion();

void WriteUpdateModeAndAnyError(string indent = "")
Expand All @@ -133,9 +152,23 @@ void WriteUpdateModeAndAnyError(string indent = "")
: CliCommandStrings.WorkloadManifestInstallationConfigurationLooseManifests;
reporter.WriteLine(indent + configurationMessage);

// Show the specific error message if manifests are missing
if (!versionInfo.IsInstalled && manifestError != null)
{
reporter.WriteLine(indent + manifestError);
}

if (!versionInfo.IsInstalled)
{
reporter.WriteLine(indent + string.Format(CliCommandStrings.WorkloadSetFromGlobalJsonNotInstalled, versionInfo.Version, versionInfo.GlobalJsonPath));
if (versionInfo.GlobalJsonPath != null)
{
reporter.WriteLine(indent + string.Format(CliCommandStrings.WorkloadSetFromGlobalJsonNotInstalled, versionInfo.Version, versionInfo.GlobalJsonPath));
}
else if (manifestError == null)
{
// Workload set is installed but manifests are missing - only show generic message if we don't have a specific error
reporter.WriteLine(indent + CliCommandStrings.WorkloadsNotInstalledRunWorkloadUpdate);
}
}
else if (versionInfo.WorkloadSetsEnabledWithoutWorkloadSet)
{
Expand Down
5 changes: 5 additions & 0 deletions src/Cli/dotnet/Commands/xlf/CliCommandStrings.cs.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Cli/dotnet/Commands/xlf/CliCommandStrings.de.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Cli/dotnet/Commands/xlf/CliCommandStrings.es.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Cli/dotnet/Commands/xlf/CliCommandStrings.fr.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Cli/dotnet/Commands/xlf/CliCommandStrings.it.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Cli/dotnet/Commands/xlf/CliCommandStrings.ja.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Cli/dotnet/Commands/xlf/CliCommandStrings.ko.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Cli/dotnet/Commands/xlf/CliCommandStrings.pl.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Cli/dotnet/Commands/xlf/CliCommandStrings.pt-BR.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Cli/dotnet/Commands/xlf/CliCommandStrings.ru.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Cli/dotnet/Commands/xlf/CliCommandStrings.tr.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hans.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hant.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,15 @@ void ThrowExceptionIfManifestsNotAvailable()
}
}

/// <summary>
/// Gets the error message if manifests from a workload set are missing.
/// Returns null if all manifests are available.
/// </summary>
public string? GetManifestErrorMessage()
{
return _exceptionToThrow?.Message;
}

public WorkloadVersionInfo GetWorkloadVersion()
{
if (_globalJsonWorkloadSetVersion != null)
Expand All @@ -260,11 +269,11 @@ public WorkloadVersionInfo GetWorkloadVersion()
GlobalJsonSpecifiesWorkloadSets: _globalJsonSpecifiedWorkloadSets);
}

ThrowExceptionIfManifestsNotAvailable();

if (_workloadSet?.Version is not null)
{
return new WorkloadVersionInfo(_workloadSet.Version, IsInstalled: true, WorkloadSetsEnabledWithoutWorkloadSet: false, GlobalJsonSpecifiesWorkloadSets: _globalJsonSpecifiedWorkloadSets);
// Return the workload set version, but indicate if manifests are actually installed
// _exceptionToThrow will be set if manifests from the workload set are missing
return new WorkloadVersionInfo(_workloadSet.Version, IsInstalled: _exceptionToThrow == null, WorkloadSetsEnabledWithoutWorkloadSet: false, GlobalJsonSpecifiesWorkloadSets: _globalJsonSpecifiedWorkloadSets);
}

var installStateFilePath = Path.Combine(WorkloadInstallType.GetInstallStateFolder(_sdkVersionBand, _sdkOrUserLocalPath), "default.json");
Expand All @@ -290,8 +299,6 @@ public WorkloadVersionInfo GetWorkloadVersion()

public IEnumerable<ReadableWorkloadManifest> GetManifests()
{
ThrowExceptionIfManifestsNotAvailable();

// Scan manifest directories
var manifestIdsToManifests = new Dictionary<string, ReadableWorkloadManifest>(StringComparer.OrdinalIgnoreCase);

Expand Down Expand Up @@ -363,7 +370,14 @@ void ProbeDirectory(string manifestDirectory, string featureBand)
var manifestDirectory = GetManifestDirectoryFromSpecifier(manifestSpecifier);
if (manifestDirectory == null)
{
throw new FileNotFoundException(string.Format(Strings.ManifestFromWorkloadSetNotFound, manifestSpecifier.ToString(), _workloadSet.Version));
// Manifest from workload set is missing. This can happen after SDK upgrades.
// Store the exception to be thrown when needed, but allow workload commands to proceed
// so they can install the missing manifests.
if (_exceptionToThrow == null)
{
_exceptionToThrow = new FileNotFoundException(string.Format(Strings.ManifestFromWorkloadSetNotFound, manifestSpecifier.ToString(), _workloadSet.Version));
}
continue;
}
AddManifest(manifestSpecifier.Id.ToString(), manifestDirectory, manifestSpecifier.FeatureBand.ToString(), kvp.Value.Version.ToString());
}
Expand All @@ -380,7 +394,14 @@ void ProbeDirectory(string manifestDirectory, string featureBand)
var manifestDirectory = GetManifestDirectoryFromSpecifier(manifestSpecifier);
if (manifestDirectory == null)
{
throw new FileNotFoundException(string.Format(Strings.ManifestFromInstallStateNotFound, manifestSpecifier.ToString(), _installStateFilePath));
// Manifest from install state is missing. This can happen after SDK upgrades.
// Store the exception to be thrown when needed, but allow workload commands to proceed
// so they can install the missing manifests.
if (_exceptionToThrow == null)
{
_exceptionToThrow = new FileNotFoundException(string.Format(Strings.ManifestFromInstallStateNotFound, manifestSpecifier.ToString(), _installStateFilePath));
}
continue;
}
AddManifest(manifestSpecifier.Id.ToString(), manifestDirectory, manifestSpecifier.FeatureBand.ToString(), kvp.Value.Version.ToString());
}
Expand All @@ -400,6 +421,10 @@ void ProbeDirectory(string manifestDirectory, string featureBand)
}
}

// Note: We intentionally do not throw here if manifests from workload sets are missing (_exceptionToThrow != null).
// This allows workload commands (install, update, restore) to proceed and install the missing manifests.
// Info commands will see IsInstalled: false in GetWorkloadVersion() and show appropriate warnings.

// Return manifests in a stable order. Manifests in the KnownWorkloadManifests.txt file will be first, and in the same order they appear in that file.
// Then the rest of the manifests (if any) will be returned in (ordinal case-insensitive) alphabetical order.
return manifestIdsToManifests
Expand Down
Loading