File: Commands\Workload\WorkloadManifestCorruptionRepairer.cs
Web Access
Project: src\src\sdk\src\Cli\dotnet\dotnet.csproj (dotnet)
// 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);
    }
}