Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
bd4ee3c
Backflow from https://github.com/dotnet/dotnet / 4f66501 build 298387
dotnet-maestro[bot] Jan 22, 2026
b49caac
Update dependencies
dotnet-maestro[bot] Jan 22, 2026
f3177bc
Update dependencies
dotnet-maestro[bot] Jan 22, 2026
b654f1f
Merge release/10.0.1xx into darc-release/10.0.1xx-d0a8b139-9a28-4982-…
dotnet-maestro[bot] Jan 23, 2026
6d409af
Update dependencies
dotnet-maestro[bot] Jan 23, 2026
878d3e2
[release/10.0.1xx] Source code updates from dotnet/dotnet (#52632)
DonnaChen888 Jan 26, 2026
79564b0
Update dependencies
dotnet-maestro[bot] Jan 26, 2026
3ea21da
Update dependencies
dotnet-maestro[bot] Jan 27, 2026
8af133d
Update dependencies
dotnet-maestro[bot] Jan 27, 2026
27df5c7
`workload repair` can recover from corrupt workload sets (#52434)
nagilson Jan 27, 2026
0ce4bc8
Update dependencies
dotnet-maestro[bot] Jan 28, 2026
86310b9
Merge branch 'release/10.0.1xx' into darc-release/10.0.1xx-c9fd6b72-3…
DonnaChen888 Jan 28, 2026
61d729a
[release/10.0.1xx] ProcessFrameworkReferences: use portable ILCompile…
github-actions[bot] Jan 28, 2026
a2fbbf0
[release/10.0.1xx] Source code updates from dotnet/dotnet (#52673)
DonnaChen888 Jan 28, 2026
0f87898
Reset files to release/10.0.2xx
github-actions[bot] Jan 28, 2026
fb0cc3a
Merge branch 'release/10.0.2xx' of https://github.com/dotnet/sdk into…
DonnaChen888 Jan 28, 2026
b9772ba
Merge branch 'release/10.0.2xx' into merge/release/10.0.1xx-to-releas…
DonnaChen888 Jan 28, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/Cli/dotnet/Commands/CliCommandStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -2651,4 +2651,8 @@ Proceed?</value>
<data name="DurationColon" xml:space="preserve">
<value>duration:</value>
</data>
<data name="WorkloadSetHasMissingManifests" xml:space="preserve">
<value>Workload set version {0} has missing manifests likely removed by package management. Run "dotnet workload repair" to fix this.</value>
<comment>{0} is the workload set version. {Locked="dotnet workload repair"}</comment>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -642,7 +643,7 @@ public IEnumerable<WorkloadHistoryRecord> 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.
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -48,7 +49,7 @@ public static IInstaller GetWorkloadInstaller(

userProfileDir ??= CliFolderPathCalculator.DotnetUserProfileFolderPath;

return new FileBasedInstaller(
var installer = new FileBasedInstaller(
reporter,
sdkFeatureBand,
workloadResolver,
Expand All @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,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())
{
Expand All @@ -80,7 +81,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));

Expand All @@ -106,4 +107,5 @@ private void ReinstallWorkloadsBasedOnCurrentManifests(IEnumerable<WorkloadId> w
{
_workloadInstaller.RepairWorkloads(workloadIds, sdkFeatureBand);
}

}
4 changes: 4 additions & 0 deletions src/Cli/dotnet/Commands/Workload/WorkloadHistoryRecorder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
{
Expand Down
121 changes: 121 additions & 0 deletions src/Cli/dotnet/Commands/Workload/WorkloadManifestCorruptionRepairer.cs
Original file line number Diff line number Diff line change
@@ -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<ManifestVersionUpdate> 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);
}
}
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.

Loading
Loading