Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
100a5b4
Add output format and output version
erdembayar Jul 25, 2022
d011047
Add JsonRenderer
erdembayar Jul 26, 2022
855288c
Add v1 of JsonRenderer
erdembayar Jul 28, 2022
83593de
Add v1 of JsonRenderer
erdembayar Jul 28, 2022
9fd3da0
Replace console.write with generic IRenderer.Write
erdembayar Jul 28, 2022
1a24aed
Start implementing JsonRenderer
erdembayar Jul 29, 2022
8dee65f
Introduce non-zero return value from dotnet list package
erdembayar Jul 29, 2022
ccfeacd
Replace all Console instance with Renderer
erdembayar Jul 29, 2022
eb2894d
Start implementing JsonRenderer
erdembayar Jul 30, 2022
6d4e553
Introduce column naming enum
erdembayar Jul 30, 2022
3da53b5
Separate Vulnurability info
erdembayar Jul 30, 2022
5fae241
Fix vulnerability data column after refactoring
erdembayar Jul 30, 2022
4b3612e
Pass data for JsonRendering
erdembayar Jul 30, 2022
a80006b
Fix test
erdembayar Jul 31, 2022
ad603e3
Basic json generation
erdembayar Jul 31, 2022
d8270e4
Fix basic json output
erdembayar Aug 1, 2022
9408c57
Clean up
erdembayar Aug 2, 2022
8b4d969
Remove check if JsonRenderer
erdembayar Aug 2, 2022
fb49a3b
Abstract write project data
erdembayar Aug 2, 2022
b46ba56
Clean up
erdembayar Aug 2, 2022
8dcd633
Make JsonRenderer more abstract for possible feature extension for ot…
erdembayar Aug 2, 2022
c840abf
Error on bad version.
erdembayar Aug 3, 2022
ebbb756
Separate data model from business logic for dotnet list package command
erdembayar Sep 28, 2022
c77cb23
Json output deprecation data
erdembayar Oct 11, 2022
ee7defd
Add problem printing for json output
erdembayar Oct 11, 2022
2ee4340
Reimplement print functionality for dotnet list package console output
erdembayar Oct 12, 2022
766481a
Print AutoReferenceFound flag
erdembayar Oct 12, 2022
8790b8c
Fix arguments for json output
erdembayar Oct 13, 2022
ab1ec6a
Simplify namespace
erdembayar Oct 13, 2022
41950ba
Clean up
erdembayar Oct 13, 2022
6b8c764
Return failure code 1 if there is problem when running dotnet list pa…
erdembayar Oct 13, 2022
c60eb03
Add XPlat list package unit tests
erdembayar Oct 15, 2022
1e111fe
Clean up
erdembayar Oct 18, 2022
eb4ec17
Handle http warning edge case
erdembayar Oct 19, 2022
03db2dc
Fix formatting
erdembayar Oct 21, 2022
0b557a7
Address PR feedback
erdembayar Oct 25, 2022
6bca799
Rename methods
erdembayar Oct 25, 2022
c88d256
Address PR comments
erdembayar Oct 26, 2022
7691366
Address latest PR feedbacks
erdembayar Oct 26, 2022
aec4dbe
Fix handling of invalid output version.
erdembayar Oct 27, 2022
fa02e20
Address PR comment
erdembayar Oct 31, 2022
e081b66
Merge ListPackageJsonRenderer.cs and ListPackageJsonRendererV1.cs files
erdembayar Oct 31, 2022
2d98be1
Removed used vars
erdembayar Oct 31, 2022
805cf06
Address PR comment about not useful inline comment.
erdembayar Nov 1, 2022
2a89fde
Address latest comment about problem level.
erdembayar Nov 1, 2022
fb4c2bd
Remove Information enum from ProblemType enums.
erdembayar Nov 2, 2022
abb5159
MSBuildAPIUtility.GetResolvedVersions shouldn't print anything to con…
erdembayar Nov 2, 2022
9374c1c
Remove LoggerWarning type
erdembayar Nov 2, 2022
345c354
In case of error need to stop processing for Console.Output
erdembayar Nov 2, 2022
86650d0
Make ListReportPackage immutable
erdembayar Nov 2, 2022
ff6b93b
Fix failing test
erdembayar Nov 3, 2022
ce33770
Make logger 1
erdembayar Nov 3, 2022
96524d5
Remove duplicate jsonserialization
erdembayar Nov 3, 2022
d0fbf0c
Remove unnecessary file
erdembayar Nov 3, 2022
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
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ namespace NuGet.CommandLine.XPlat
{
internal interface IListPackageCommandRunner
{
Task ExecuteCommandAsync(ListPackageArgs packageRefArgs);
Task<int> ExecuteCommandAsync(ListPackageArgs packageRefArgs);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using NuGet.CommandLine.XPlat.ListPackage;
using NuGet.Common;
using NuGet.Configuration;

Expand All @@ -16,6 +19,8 @@ internal class ListPackageArgs
public IEnumerable<PackageSource> PackageSources { get; }
public IEnumerable<string> Frameworks { get; }
public ReportType ReportType { get; }
public IReportRenderer Renderer { get; }
public string ArgumentText { get; }
public bool IncludeTransitive { get; }
public bool Prerelease { get; }
public bool HighestPatch { get; }
Expand All @@ -31,6 +36,7 @@ internal class ListPackageArgs
/// <param name="packageSources"> The sources for the packages to check in the case of --outdated </param>
/// <param name="frameworks"> The user inputed frameworks to look up for their packages </param>
/// <param name="reportType"> Which report we're producing (e.g. --outdated) </param>
/// <param name="renderer">The report output renderer (e.g. console, json)</param>
/// <param name="includeTransitive"> Bool for --include-transitive present </param>
/// <param name="prerelease"> Bool for --include-prerelease present </param>
/// <param name="highestPatch"> Bool for --highest-patch present </param>
Expand All @@ -42,6 +48,7 @@ public ListPackageArgs(
IEnumerable<PackageSource> packageSources,
IEnumerable<string> frameworks,
ReportType reportType,
IReportRenderer renderer,
bool includeTransitive,
bool prerelease,
bool highestPatch,
Expand All @@ -53,12 +60,63 @@ public ListPackageArgs(
PackageSources = packageSources ?? throw new ArgumentNullException(nameof(packageSources));
Frameworks = frameworks ?? throw new ArgumentNullException(nameof(frameworks));
ReportType = reportType;
Renderer = renderer;
IncludeTransitive = includeTransitive;
Prerelease = prerelease;
HighestPatch = highestPatch;
HighestMinor = highestMinor;
Logger = logger ?? throw new ArgumentNullException(nameof(logger));
CancellationToken = cancellationToken;
ArgumentText = GetReportParameters();
}

private string GetReportParameters()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there no way to get all the arguments out of the parser instead of us buildiing the arguments ourselves?

My concern is that if/when we add a new option, it'd be super easy to miss that the argument text needs updating.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In command argument/option parse we manually determine what to care about, so currently I don't know any other easy way to doing this.
We can get unprocessed arguments from here.

It looks something like below, it still needs processing to make it look like list --include-transitive:

list --format json C:\Users\...\MyProjectD\MyProjectD.csproj --include-transitive

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I see. You actually want to trim down the argument.
I guess it makes sense in that case.

I'm wondering if there's a way to ensure that we don't forget to add this in a future through tests.
Maybe use some reflection?

Copy link
Contributor Author

@erdembayar erdembayar Oct 25, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to check it simple way using reflection.
Could please you check new JsonRenderer_ListPackageArgse_Verify_AllFields_Covered test in test\NuGet.Core.FuncTests\NuGet.XPlat.FuncTest\ListPackageTests.cs ? Do you think is it enough?
https://github.com/NuGet/NuGet.Client/pull/4855/files#diff-9aff685d365ad24a46596f586a60c5ff0d23a53c1daf4fe275b21ad64d1fd898R191

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Works for me.

If you're motivated, you can even add something where you create an args list and make sure all of the relevant ones are included in the string.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not motivated to add more test for this moment but added reminder in follow up issue #12167.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reviving this thread because I think I have relevant new information to add:

In command argument/option parse we manually determine what to care about, so currently I don't know any other easy way to doing this.

System.Environment.CommandLine

A complication is due to the way that the dotnet cli works. From the operation system's point of view, the command run is dotnet c:\path\to\NuGet.CommandLine.XPlat.dll package list --other --args, so the value will need to be cleaned up a bit to remove everything before the first package list, and also take into account when the command line passes a project/solution path.

Also, looking at the spec PR, I don't see where any customers have asked for the CLI arguments to be written to the json output. Since customers control how dotnet list package is called, from my point of view, they already know the arguments, or at least can look it up in their pipeline scripts. Is this an actually useful feature, or is it just adding technical debt for little benefit?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

System.Environment.CommandLine

A complication is due to the way that the dotnet cli works. From the operation system's point of view, the command run is dotnet c:\path\to\NuGet.CommandLine.XPlat.dll package list --other --args, so the value will need to be cleaned up a bit to remove everything before the first package list, and also take into account when the command line passes a project/solution path.

Yes, I saw command line args, but that one needs more manual processing than this one.

Also, looking at the spec PR, I don't see where any customers have asked for the CLI arguments to be written to the json output. Since customers control how dotnet list package is called, from my point of view, they already know the arguments, or at least can look it up in their pipeline scripts. Is this an actually useful feature, or is it just adding technical debt for little benefit?

Intention for having parameters section is we keep this json output somewhere as archive for safe keeping and having input information is useful later because many CI pipelines runs get deleted after some time.
That time it's very hard to tell if json output is from dotnet list package --vulnerable or dotnet list package --deprecated command. They assumed it was for dotnet list package --vulnerable (check below example) and there was nothing in json output, but actually if it was dotnet list package --deprecated then it misleads the someone looking at report.

{
  "version": 1,
   "sources": [
    "https://api.nuget.org/v3/index.json",
    "https://apidev.nugettest.org/v3-index/index.json"
  ],
  "projects": [
    {
      "path": "src/lib/MyProjectC.csproj",
      "frameworks": [
        {
          "framework": "netcoreapp3.1",
          "topLevelPackages": [
            ]
        },
        {
          "framework": "net5.0",
          "topLevelPackages": [
            ]
        }
      ]
    }
  ]
}

{
StringBuilder sb = new StringBuilder();

switch (ReportType)
{
case ReportType.Default:
break;
case ReportType.Deprecated:
sb.Append(" --deprecated");
break;
case ReportType.Outdated:
sb.Append(" --outdated");
break;
case ReportType.Vulnerable:
sb.Append(" --vulnerable");
break;
default:
break;
}

if (IncludeTransitive)
{
sb.Append(" --include-transitive");
}

if (Frameworks != null && Frameworks.Any())
{
sb.Append(" --framework " + string.Join(" ", Frameworks));
}

if (Prerelease)
{
sb.Append(" --include-prerelease");
}

if (HighestMinor)
{
sb.Append(" --highest-minor");
}

if (HighestPatch)
{
sb.Append("--highest-patch");
}

return sb.ToString().Trim();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System.Linq;
using System.Threading;
using Microsoft.Extensions.CommandLineUtils;
using NuGet.CommandLine.XPlat.ListPackage;
using NuGet.Commands;
using NuGet.Common;
using NuGet.Configuration;
Expand Down Expand Up @@ -89,6 +90,16 @@ public static void Register(
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,
Expand Down Expand Up @@ -117,25 +128,27 @@ public static void Register(
isDeprecated: deprecatedReport.HasValue(),
isVulnerable: vulnerableReport.HasValue());

IReportRenderer reportRenderer = GetOutputType(outputFormat.Value(), outputVersionOption: outputVersion.Value());

var packageRefArgs = new ListPackageArgs(
path.Value,
packageSources,
framework.Values,
reportType,
reportRenderer,
includeTransitive.HasValue(),
prerelease.HasValue(),
highestPatch.HasValue(),
highestMinor.HasValue(),
logger,
CancellationToken.None);

DisplayMessages(packageRefArgs);
WarnAboutIncompatibleOptions(packageRefArgs, reportRenderer);

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

var listPackageCommandRunner = getCommandRunner();
await listPackageCommandRunner.ExecuteCommandAsync(packageRefArgs);
return 0;
return await listPackageCommandRunner.ExecuteCommandAsync(packageRefArgs);
});
});
}
Expand All @@ -158,12 +171,43 @@ private static ReportType GetReportType(bool isDeprecated, bool isOutdated, bool
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Strings.ListPkg_InvalidOptions));
}

private static void DisplayMessages(ListPackageArgs packageRefArgs)
private static IReportRenderer GetOutputType(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)
{
return new ListPackageConsoleRenderer();
}

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();
}

return jsonReportRenderer;
}

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

Expand Down Expand Up @@ -216,5 +260,13 @@ private static IEnumerable<PackageSource> GetPackageSources(ISettings settings,

return packageSources;
}

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

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