File: Commands\PackageReferenceCommands\ListPackage\ListPackageCommand.cs
Web Access
Project: src\src\nuget-client\src\NuGet.Core\NuGet.CommandLine.XPlat\NuGet.CommandLine.XPlat.csproj (NuGet.CommandLine.XPlat)
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

#nullable enable

using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
using Microsoft.Extensions.CommandLineUtils;
using NuGet.CommandLine.XPlat.ListPackage;
using NuGet.Commands;
using NuGet.Common;
using NuGet.Configuration;
using NuGet.Credentials;

namespace NuGet.CommandLine.XPlat
{
    internal static class ListPackageCommand
    {
        public static void Register(
            CommandLineApplication app,
            Func<ILogger> getLogger,
            Action<LogLevel> setLogLevel,
            Func<IListPackageCommandRunner> getCommandRunner)
        {
            app.Command("list", listpkg =>
            {
                listpkg.Description = Strings.ListPkg_Description;
                listpkg.HelpOption(XPlatUtility.HelpOption);

                listpkg.Option(
                    CommandConstants.ForceEnglishOutputOption,
                    Strings.ForceEnglishOutput_Description,
                    CommandOptionType.NoValue);

                var path = listpkg.Argument(
                    "<PROJECT | SOLUTION>",
                    Strings.ListPkg_PathDescription,
                    multipleValues: false);

                var framework = listpkg.Option(
                    "--framework",
                    Strings.ListPkg_FrameworkDescription,
                    CommandOptionType.MultipleValue);

                var deprecatedReport = listpkg.Option(
                    "--deprecated",
                    Strings.ListPkg_DeprecatedDescription,
                    CommandOptionType.NoValue);

                var outdatedReport = listpkg.Option(
                    "--outdated",
                    Strings.ListPkg_OutdatedDescription,
                    CommandOptionType.NoValue);

                var vulnerableReport = listpkg.Option(
                    "--vulnerable",
                    Strings.ListPkg_VulnerableDescription,
                    CommandOptionType.NoValue);

                var includeTransitive = listpkg.Option(
                    "--include-transitive",
                    Strings.ListPkg_TransitiveDescription,
                    CommandOptionType.NoValue);

                var prerelease = listpkg.Option(
                    "--include-prerelease",
                    Strings.ListPkg_PrereleaseDescription,
                    CommandOptionType.NoValue);

                var highestPatch = listpkg.Option(
                    "--highest-patch",
                    Strings.ListPkg_HighestPatchDescription,
                    CommandOptionType.NoValue);

                var highestMinor = listpkg.Option(
                    "--highest-minor",
                    Strings.ListPkg_HighestMinorDescription,
                    CommandOptionType.NoValue);

                var source = listpkg.Option(
                    "--source",
                    Strings.ListPkg_SourceDescription,
                    CommandOptionType.MultipleValue);

                var config = listpkg.Option(
                    "--config",
                    Strings.ListPkg_ConfigDescription,
                    CommandOptionType.SingleValue);

                var outputFormat = listpkg.Option(
                    "--format",
                    Strings.ListPkg_OutputFormatDescription,
                    CommandOptionType.SingleValue);

                var outputVersion = listpkg.Option(
                    "--output-version",
                    Strings.ListPkg_OutputVersionDescription,
                    CommandOptionType.SingleValue);

                var interactive = listpkg.Option(
                    "--interactive",
                    Strings.NuGetXplatCommand_Interactive,
                    CommandOptionType.NoValue);

                var verbosity = listpkg.Option(
                    "-v|--verbosity",
                    Strings.Verbosity_Description,
                    CommandOptionType.SingleValue);

                listpkg.OnExecute(async () =>
                {
                    var logger = getLogger();

                    setLogLevel(XPlatUtility.MSBuildVerbosityToNuGetLogLevel(verbosity.Value()));

                    var settings = ProcessConfigFile(config.Value(), path.Value);
                    var sources = source.Values;

                    var packageSources = GetPackageSources(settings, sources, config);

                    var reportType = GetReportType(
                        isOutdated: outdatedReport.HasValue(),
                        isDeprecated: deprecatedReport.HasValue(),
                        isVulnerable: vulnerableReport.HasValue());

                    IReportRenderer reportRenderer = GetOutputType(app.Out, app.Error, outputFormat.Value(), outputVersionOption: outputVersion.Value());
                    var provider = new PackageSourceProvider(settings);
                    var packageRefArgs = new ListPackageArgs(
                        path.Value,
                        packageSources,
                        framework.Values,
                        reportType,
                        reportRenderer,
                        includeTransitive.HasValue(),
                        prerelease.HasValue(),
                        highestPatch.HasValue(),
                        highestMinor.HasValue(),
                        provider.LoadAuditSources(),
                        logger,
                        CancellationToken.None);

                    WarnAboutIncompatibleOptions(packageRefArgs, reportRenderer);

                    DefaultCredentialServiceUtility.SetupDefaultCredentialService(getLogger(), !interactive.HasValue());

                    var listPackageCommandRunner = getCommandRunner();
                    return await listPackageCommandRunner.ExecuteCommandAsync(packageRefArgs);
                });
            });
        }

        private static ReportType GetReportType(bool isDeprecated, bool isOutdated, bool isVulnerable)
        {
            var mutexCount = 0;
            mutexCount += isDeprecated ? 1 : 0;
            mutexCount += isOutdated ? 1 : 0;
            mutexCount += isVulnerable ? 1 : 0;
            if (mutexCount == 0)
            {
                return ReportType.Default;
            }
            else if (mutexCount == 1)
            {
                return isDeprecated ? ReportType.Deprecated : isOutdated ? ReportType.Outdated : ReportType.Vulnerable;
            }

            throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Strings.ListPkg_InvalidOptions));
        }

        private static IReportRenderer GetOutputType(TextWriter consoleOut, TextWriter consoleError, string outputFormatOption, string outputVersionOption)
        {
            ReportOutputFormat outputFormat = ReportOutputFormat.Console;
            if (!string.IsNullOrEmpty(outputFormatOption) &&
                !Enum.TryParse(outputFormatOption, ignoreCase: true, out outputFormat))
            {
                string currentlySupportedFormat = GetEnumValues<ReportOutputFormat>();
                throw new ArgumentException(string.Format(Strings.ListPkg_InvalidOutputFormat, outputFormatOption, currentlySupportedFormat));
            }

            if (outputFormat == ReportOutputFormat.Console)
            {
                if (!string.IsNullOrEmpty(outputVersionOption))
                {
                    throw new ArgumentException(string.Format(Strings.ListPkg_OutputVersionNotApplicable));
                }
                return new ListPackageConsoleRenderer(consoleOut, consoleError);
            }

            IReportRenderer jsonReportRenderer;

            var currentlySupportedReportVersions = new List<string> { "1" };
            // If customer pass unsupported version then error out instead of defaulting to version probably unsupported by customer machine.
            if (!string.IsNullOrEmpty(outputVersionOption) && !currentlySupportedReportVersions.Contains(outputVersionOption))
            {
                throw new ArgumentException(string.Format(Strings.ListPkg_InvalidOutputVersion, outputVersionOption, string.Join(" ,", currentlySupportedReportVersions)));
            }
            else
            {
                jsonReportRenderer = new ListPackageJsonRenderer(consoleOut);
            }

            return jsonReportRenderer;
        }

        private static void WarnAboutIncompatibleOptions(ListPackageArgs packageRefArgs, IReportRenderer reportRenderer)
        {
            if (packageRefArgs.ReportType != ReportType.Outdated &&
                (packageRefArgs.Prerelease || packageRefArgs.HighestMinor || packageRefArgs.HighestPatch))
            {
                reportRenderer.AddProblem(ProblemType.Warning, Strings.ListPkg_VulnerableIgnoredOptions);
            }
        }

        private static ISettings ProcessConfigFile(string configFile, string projectOrSolution)
        {
            if (string.IsNullOrEmpty(configFile))
            {
                return Settings.LoadDefaultSettings(projectOrSolution);
            }

            var configFileFullPath = Path.GetFullPath(configFile);
            var directory = Path.GetDirectoryName(configFileFullPath);
            var configFileName = Path.GetFileName(configFileFullPath);
            return Settings.LoadDefaultSettings(
                directory,
                configFileName,
                machineWideSettings: new XPlatMachineWideSetting());

        }

        private static List<PackageSource> GetPackageSources(ISettings settings, IEnumerable<string> sources, CommandOption config)
        {
            var availableSources = PackageSourceProvider.LoadPackageSources(settings).Where(source => source.IsEnabled);
            var uniqueSources = new HashSet<string>();

            var packageSources = new List<PackageSource>();
            foreach (var source in sources)
            {
                if (!uniqueSources.Contains(source))
                {
                    uniqueSources.Add(source);
                    packageSources.Add(PackageSourceProviderExtensions.ResolveSource(availableSources, source));
                }
            }

            if (packageSources.Count == 0 || config.HasValue())
            {
                packageSources.AddRange(availableSources);
            }

            return packageSources;
        }

        private static string GetEnumValues<T>() where T : struct, Enum
        {
            var enumValues = Enum.GetValues<T>()
               .Select(x => x.ToString());

            return string.Join(", ", enumValues).ToLower(CultureInfo.CurrentCulture);
        }
    }
}