|
// 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 Microsoft.DotNet.Cli.CommandLine;
using Microsoft.DotNet.Cli.Extensions;
using Microsoft.DotNet.Cli.NuGetPackageDownloader;
using Microsoft.DotNet.Cli.ToolManifest;
using Microsoft.DotNet.Cli.ToolPackage;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.Cli.Utils.Extensions;
using Microsoft.Extensions.EnvironmentAbstractions;
using NuGet.Frameworks;
using NuGet.Versioning;
namespace Microsoft.DotNet.Cli.Commands.Tool.Restore;
internal class ToolRestoreCommand : CommandBase<ToolRestoreCommandDefinition>
{
private readonly string _configFilePath;
private readonly IReporter _errorReporter;
private readonly ILocalToolsResolverCache _localToolsResolverCache;
private readonly IToolManifestFinder _toolManifestFinder;
private readonly IFileSystem _fileSystem;
private readonly IReporter _reporter;
private readonly string[] _sources;
private readonly IToolPackageDownloader _toolPackageDownloader;
private readonly VerbosityOptions _verbosity;
private readonly RestoreActionConfig _restoreActionConfig;
public ToolRestoreCommand(
ParseResult result,
IToolPackageDownloader toolPackageDownloader = null,
IToolManifestFinder toolManifestFinder = null,
ILocalToolsResolverCache localToolsResolverCache = null,
IFileSystem fileSystem = null,
IReporter reporter = null)
: base(result)
{
if (toolPackageDownloader == null)
{
(IToolPackageStore,
IToolPackageStoreQuery,
IToolPackageDownloader downloader) toolPackageStoresAndInstaller
= ToolPackageFactory.CreateToolPackageStoresAndDownloader();
_toolPackageDownloader = toolPackageStoresAndInstaller.downloader;
}
else
{
_toolPackageDownloader = toolPackageDownloader;
}
_toolManifestFinder
= toolManifestFinder
?? new ToolManifestFinder(new DirectoryPath(Directory.GetCurrentDirectory()));
_localToolsResolverCache = localToolsResolverCache ?? new LocalToolsResolverCache();
_fileSystem = fileSystem ?? new FileSystemWrapper();
_reporter = reporter ?? Reporter.Output;
_errorReporter = reporter ?? Reporter.Error;
_configFilePath = result.GetValue(Definition.ConfigOption);
_sources = result.GetValue(Definition.AddSourceOption);
_verbosity = result.GetValue(Definition.VerbosityOption);
if (!result.HasOption(Definition.VerbosityOption) && result.GetValue(Definition.RestoreOptions.InteractiveOption))
{
_verbosity = VerbosityOptions.minimal;
}
_restoreActionConfig = Definition.RestoreOptions.ToRestoreActionConfig(result);
}
public override int Execute()
{
FilePath? customManifestFileLocation = GetCustomManifestFileLocation();
FilePath? configFile = null;
if (!string.IsNullOrEmpty(_configFilePath))
{
configFile = new FilePath(_configFilePath);
}
IReadOnlyCollection<ToolManifestPackage> packagesFromManifest;
try
{
packagesFromManifest = _toolManifestFinder.Find(customManifestFileLocation);
}
catch (ToolManifestCannotBeFoundException e)
{
if (CommandLoggingContext.IsVerbose)
{
_reporter.WriteLine(string.Join(Environment.NewLine, e.VerboseMessage).Yellow());
}
_reporter.WriteLine(e.Message.Yellow());
_reporter.WriteLine(CliCommandStrings.NoToolsWereRestored.Yellow());
return 0;
}
var toolPackageRestorer = new ToolPackageRestorer(
_toolPackageDownloader,
_sources,
overrideSources: [],
_verbosity,
_restoreActionConfig,
_localToolsResolverCache,
_fileSystem);
ToolRestoreResult[] toolRestoreResults =
[.. packagesFromManifest
.AsEnumerable()
.Select(package => toolPackageRestorer.InstallPackage(package, configFile))];
Dictionary<RestoredCommandIdentifier, ToolCommand> downloaded =
toolRestoreResults.Select(result => result.SaveToCache)
.Where(item => item is not null)
.ToDictionary(pair => pair.Value.restoredCommandIdentifier, pair => pair.Value.toolCommand);
EnsureNoCommandNameCollision(downloaded);
_localToolsResolverCache.Save(downloaded);
return PrintConclusionAndReturn(toolRestoreResults);
}
private int PrintConclusionAndReturn(ToolRestoreResult[] toolRestoreResults)
{
if (toolRestoreResults.Any(r => !r.IsSuccess))
{
_reporter.WriteLine();
_errorReporter.WriteLine(string.Join(
Environment.NewLine,
toolRestoreResults.Where(r => !r.IsSuccess).Select(r => r.Message)).Red());
var successMessage = toolRestoreResults.Where(r => r.IsSuccess).Select(r => r.Message);
if (successMessage.Any())
{
_reporter.WriteLine();
_reporter.WriteLine(string.Join(Environment.NewLine, successMessage));
// Display warnings for successful restorations even in partial failure case
var warnings = toolRestoreResults.Where(r => r.IsSuccess && !string.IsNullOrEmpty(r.Warning)).Select(r => r.Warning);
if (warnings.Any())
{
_reporter.WriteLine();
_reporter.WriteLine(string.Join(Environment.NewLine, warnings).Yellow());
}
}
_errorReporter.WriteLine(Environment.NewLine +
(toolRestoreResults.Any(r => r.IsSuccess)
? CliCommandStrings.RestorePartiallyFailed
: CliCommandStrings.RestoreFailed).Red());
return 1;
}
else
{
_reporter.WriteLine(string.Join(Environment.NewLine,
toolRestoreResults.Where(r => r.IsSuccess).Select(r => r.Message)));
// Display warnings for newer versions available
var warnings = toolRestoreResults.Where(r => r.IsSuccess && !string.IsNullOrEmpty(r.Warning)).Select(r => r.Warning);
if (warnings.Any())
{
_reporter.WriteLine();
_reporter.WriteLine(string.Join(Environment.NewLine, warnings).Yellow());
}
_reporter.WriteLine();
_reporter.WriteLine(CliCommandStrings.LocalToolsRestoreWasSuccessful.Green());
return 0;
}
}
private FilePath? GetCustomManifestFileLocation()
{
string customFile = _parseResult.GetValue(Definition.ToolManifestOption);
FilePath? customManifestFileLocation;
if (!string.IsNullOrEmpty(customFile))
{
customManifestFileLocation = new FilePath(customFile);
}
else
{
customManifestFileLocation = null;
}
return customManifestFileLocation;
}
private static void EnsureNoCommandNameCollision(Dictionary<RestoredCommandIdentifier, ToolCommand> dictionary)
{
string[] errors = [.. dictionary
.Select(pair => (pair.Key.PackageId, pair.Key.CommandName))
.GroupBy(packageIdAndCommandName => packageIdAndCommandName.CommandName)
.Where(grouped => grouped.Count() > 1)
.Select(nonUniquePackageIdAndCommandNames =>
string.Format(CliCommandStrings.PackagesCommandNameCollisionConclusion,
string.Join(Environment.NewLine,
nonUniquePackageIdAndCommandNames.Select(
p => "\t" + string.Format(
CliCommandStrings.PackagesCommandNameCollisionForOnePackage,
p.CommandName.Value,
p.PackageId.ToString())))))];
if (errors.Any())
{
throw new ToolPackageException(string.Join(Environment.NewLine, errors));
}
}
public struct ToolRestoreResult
{
public (RestoredCommandIdentifier restoredCommandIdentifier, ToolCommand toolCommand)? SaveToCache { get; }
public bool IsSuccess { get; }
public string Message { get; }
public string Warning { get; }
private ToolRestoreResult(
(RestoredCommandIdentifier, ToolCommand)? saveToCache,
bool isSuccess, string message, string warning = null)
{
if (string.IsNullOrWhiteSpace(message))
{
throw new ArgumentException("message", nameof(message));
}
SaveToCache = saveToCache;
IsSuccess = isSuccess;
Message = message;
Warning = warning;
}
public static ToolRestoreResult Success(
(RestoredCommandIdentifier, ToolCommand)? saveToCache,
string message,
string warning = null)
{
return new ToolRestoreResult(saveToCache, true, message, warning);
}
public static ToolRestoreResult Failure(string message)
{
return new ToolRestoreResult(null, false, message, null);
}
public static ToolRestoreResult Failure(
PackageId packageId,
ToolPackageException toolPackageException)
{
return new ToolRestoreResult(null, false,
string.Format(CliCommandStrings.PackageFailedToRestore,
packageId.ToString(), toolPackageException.ToString()), null);
}
}
}
|