File: Commands\Tool\Install\ToolInstallLocalCommand.cs
Web Access
Project: ..\..\..\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.
 
#nullable disable
 
using System.CommandLine;
using Microsoft.DotNet.Cli.Commands.Tool.Common;
using Microsoft.DotNet.Cli.Commands.Tool.List;
using Microsoft.DotNet.Cli.Commands.Tool.Update;
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;
 
namespace Microsoft.DotNet.Cli.Commands.Tool.Install;
 
internal class ToolInstallLocalCommand : CommandBase
{
    private readonly IToolManifestFinder _toolManifestFinder;
    private readonly IToolManifestEditor _toolManifestEditor;
    private readonly ILocalToolsResolverCache _localToolsResolverCache;
    private readonly ToolInstallLocalInstaller _toolLocalPackageInstaller;
    private readonly IReporter _reporter;
    private readonly PackageId? _packageId;
    private readonly bool _allowPackageDowngrade;
 
    private readonly string _explicitManifestFile;
    private readonly bool _createManifestIfNeeded;
    private readonly bool _allowRollForward;
    private readonly bool _updateAll;
 
    internal RestoreActionConfig restoreActionConfig;
 
    public ToolInstallLocalCommand(
        ParseResult parseResult,
        PackageId? packageId = null,
        IToolPackageDownloader toolPackageDownloader = null,
        IToolManifestFinder toolManifestFinder = null,
        IToolManifestEditor toolManifestEditor = null,
        ILocalToolsResolverCache localToolsResolverCache = null,
        IReporter reporter = null,
        string runtimeJsonPathForTests = null
        )
        : base(parseResult)
    {
        _updateAll = parseResult.GetValue(ToolUpdateCommandParser.UpdateAllOption);
        var packageIdArgument = parseResult.GetValue(ToolInstallCommandParser.PackageIdentityArgument).Id;
        _packageId = packageId ?? (packageIdArgument is not null ? new PackageId(packageIdArgument) : null);
        _explicitManifestFile = parseResult.GetValue(ToolInstallCommandParser.ToolManifestOption);
 
        _createManifestIfNeeded = parseResult.GetValue(ToolInstallCommandParser.CreateManifestIfNeededOption);
 
        _reporter = reporter ?? Reporter.Output;
 
        _toolManifestFinder = toolManifestFinder ??
                              new ToolManifestFinder(new DirectoryPath(Directory.GetCurrentDirectory()));
        _toolManifestEditor = toolManifestEditor ?? new ToolManifestEditor();
        _localToolsResolverCache = localToolsResolverCache ?? new LocalToolsResolverCache();
 
        restoreActionConfig = new RestoreActionConfig(DisableParallel: parseResult.GetValue(ToolCommandRestorePassThroughOptions.DisableParallelOption),
            NoCache: parseResult.GetValue(ToolCommandRestorePassThroughOptions.NoCacheOption) || parseResult.GetValue(ToolCommandRestorePassThroughOptions.NoHttpCacheOption),
            IgnoreFailedSources: parseResult.GetValue(ToolCommandRestorePassThroughOptions.IgnoreFailedSourcesOption),
            Interactive: parseResult.GetValue(ToolCommandRestorePassThroughOptions.InteractiveRestoreOption));
 
        _toolLocalPackageInstaller = new ToolInstallLocalInstaller(parseResult, toolPackageDownloader, runtimeJsonPathForTests, restoreActionConfig);
        _allowRollForward = parseResult.GetValue(ToolInstallCommandParser.RollForwardOption);
        _allowPackageDowngrade = parseResult.GetValue(ToolInstallCommandParser.AllowPackageDowngradeOption);
    }
 
    public override int Execute()
    {
        if (_updateAll)
        {
            var toolListCommand = new ToolListLocalCommand(_parseResult, (IToolManifestInspector)_toolManifestFinder);
            var toolIds = toolListCommand.GetPackages(null);
            foreach (var toolId in toolIds)
            {
                ExecuteInstallCommand(toolId.Item1.PackageId);
            }
            return 0;
        }
        else
        {
            return ExecuteInstallCommand((PackageId) _packageId);
        }
    }
 
    private int ExecuteInstallCommand(PackageId packageId)
    {
        FilePath manifestFile = GetManifestFilePath();
 
        (FilePath? manifestFileOptional, string warningMessage) =
            _toolManifestFinder.ExplicitManifestOrFindManifestContainPackageId(_explicitManifestFile, packageId);
 
        if (warningMessage != null)
        {
            _reporter.WriteLine(warningMessage.Yellow());
        }
 
        manifestFile = manifestFileOptional ?? GetManifestFilePath();
        var existingPackageWithPackageId = _toolManifestFinder.Find(manifestFile).Where(p => p.PackageId.Equals(packageId));
 
        if (!existingPackageWithPackageId.Any())
        {
            return InstallNewTool(manifestFile, packageId);
        }
 
        var existingPackage = existingPackageWithPackageId.Single();
        var toolDownloadedPackage = _toolLocalPackageInstaller.Install(manifestFile, packageId);
 
        InstallToolUpdate(existingPackage, toolDownloadedPackage, manifestFile, packageId);
 
        _localToolsResolverCache.SaveToolPackage(
            toolDownloadedPackage,
            _toolLocalPackageInstaller.TargetFrameworkToInstall);
 
        return 0;
    }
 
    public int InstallToolUpdate(ToolManifestPackage existingPackage, IToolPackage toolDownloadedPackage, FilePath manifestFile, PackageId packageId)
    {
        if (existingPackage.Version > toolDownloadedPackage.Version && !_allowPackageDowngrade)
        {
            throw new GracefulException(
                [
                    string.Format(
                        CliCommandStrings.UpdateLocalToolToLowerVersion,
                        toolDownloadedPackage.Version.ToNormalizedString(),
                        existingPackage.Version.ToNormalizedString(),
                        manifestFile.Value)
                ],
                isUserError: false);
        }
        else if (existingPackage.Version == toolDownloadedPackage.Version)
        {
            _reporter.WriteLine(
                string.Format(
                    CliCommandStrings.UpdateLocaToolSucceededVersionNoChange,
                    toolDownloadedPackage.Id,
                    existingPackage.Version.ToNormalizedString(),
                    manifestFile.Value));
        }
        else
        {
            _toolManifestEditor.Edit(
                manifestFile,
                packageId,
                toolDownloadedPackage.Version,
                [toolDownloadedPackage.Command.Name]);
            _reporter.WriteLine(
                string.Format(
                    CliCommandStrings.UpdateLocalToolSucceeded,
                    toolDownloadedPackage.Id,
                    existingPackage.Version.ToNormalizedString(),
                    toolDownloadedPackage.Version.ToNormalizedString(),
                    manifestFile.Value).Green());
        }
 
        _localToolsResolverCache.SaveToolPackage(
            toolDownloadedPackage,
            _toolLocalPackageInstaller.TargetFrameworkToInstall);
 
        return 0;
    }
 
    public int InstallNewTool(FilePath manifestFile, PackageId packageId)
    {
        IToolPackage toolDownloadedPackage =
            _toolLocalPackageInstaller.Install(manifestFile, packageId);
 
        _toolManifestEditor.Add(
            manifestFile,
            toolDownloadedPackage.Id,
            toolDownloadedPackage.Version,
            [toolDownloadedPackage.Command.Name],
            _allowRollForward);
 
        _localToolsResolverCache.SaveToolPackage(
            toolDownloadedPackage,
            _toolLocalPackageInstaller.TargetFrameworkToInstall);
 
        _reporter.WriteLine(
            string.Format(
                CliCommandStrings.LocalToolInstallationSucceeded,
                toolDownloadedPackage.Command.Name,
                toolDownloadedPackage.Id,
                toolDownloadedPackage.Version.ToNormalizedString(),
                manifestFile.Value).Green());
 
        return 0;
    }
 
    public FilePath GetManifestFilePath()
    {
        return string.IsNullOrWhiteSpace(_explicitManifestFile)
            ? _toolManifestFinder.FindFirst(_createManifestIfNeeded)
            : new FilePath(_explicitManifestFile);
    }
}