You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
NuGet.Home/accepted/2023/remove-projects-from-list-p...

6.8 KiB

Remove projects from list package command

Summary

Currently, when listing transitive packages using dotnet list package --include-transitive, both project and packages dependencies are reported. The goal of this proposal is to remove project dependencies from this command related to packages.

Motivation

While packages and projects are very close - packages being packed projects -, packages differ from project because they can be published to a package repository. This package repository enables the addition of metadata that wasn't included in the initial package, which can serve purposes like indicating deprecation or vulnerabilities. In turn, these additional metadata can be consumed to take decisions.

The list package command already supports filtering the results to retrieve only deprecated (--deprecated), vulnerable (--vulnerable), or not up-to-date (--outdated) packages. These commands only return actual packages, which is understandable as the underlying information used to choose what to report is managed by the package manager, aka nuget.

When list package is used without any of these flags,

  • using --include-transitive lists direct project dependencies as transitive.
  • not using --include-transitive does not list project dependencies at all.

Removing projects from the output would allow users to get an accurate view of their package dependencies. This would ensure that the output of the command can be processed in an uniform way by either a user, or a piece of software - through the machine readable output - to support any report, or process associated with packages, even those with no issues related to deprecation, vulnerabilities, or up-to-dateness.

Explanation

Functional explanation

The list package default behavior will be changed to only return packages in the output.

Given a solution setup like the one below,

solution.sln
├── projectA.csproj
│   └── package1.nupkg (2.5.6)
│       └── package2.nupkg (1.0.0)
└── projectB.csproj
    ├── projectA.csproj
    └── package3.nupkg (30.0.1)
        └── package4.nupkg (1.0.5)

Running dotnet list solution.sln package --include-transitive produces the following output:

Project 'projectA' has the following package references
   [net7.0]:
   Top-level Package      Requested   Resolved
   > package1             2.5.6       2.5.6

   Transitive Package       Resolved
   > package2               1.0.0

Project 'projectB' has the following package references
   [net7.0]:
   Top-level Package      Requested   Resolved
   > package3             30.0.1      30.0.1

   Transitive Package       Resolved
   > package4               1.0.5
   > projectA               1.0.0

After the fix, the same command would produce the following output:

Project 'projectA' has the following package references
   [net7.0]:
   Top-level Package      Requested   Resolved
   > package1             2.5.6       2.5.6

   Transitive Package       Resolved
   > package2               1.0.0

Project 'projectB' has the following package references
   [net7.0]:
   Top-level Package      Requested   Resolved
   > package3             30.0.1      30.0.1

   Transitive Package       Resolved
   > package4               1.0.5

Machine-readable output will also reflect the change by not including the project in the output.

Technical explanation

Currently, the implementation determines a ReportType based on the presence of options:

Option Resulting ReportType
--deprecated ReportType.Deprecated
--outdated ReportType.Outdated
--vulnerable ReportType.Vulnerable
none of the above ReportType.Default

MSBuild version resolver is then called with the includeProject parameter set to false for all values except Default, where it is set to true.

As the current implementation incorrectly includes direct project dependencies as transitive, the parameter responsible for this behavior will be removed from the non-public method. The code consuming the parameter will be update to behave as if the parameter would have been set to false

Drawbacks

It might introduce a breaking change for a small number of use cases. However, the presence of a list reference command, working on projects, mitigates this risk

Rationale and alternatives

The proposal considers the current behavior a bug, as determined by the project members review.

Other approaches could have been taken:

  • Adding an --exclude-project option, to remove projects from the output. A proposal has been made, which has been closed as another solution has been chosen: https://github.com/NuGet/NuGet.Client/pull/5302
  • Taking into account that projects are part of the output, and ensuring that direct (or transitive) project references are appropriately reported. In this case, adding a flag to be able to retrieve only package is probably worth it, like mentioned below.
  • Adding a --include-project option and modifying the default behavior of the list command: this would have made the whole command consistent, whether a --deprecated, --vulnerable, or --outdated option is passed or not. However, this would create a breaking change with previous behavior
  • Adding the type of dependency to the output: this would have made the output filterable afterwards, without adding flags. However, this could break console output parsing - which could be acceptable -, and could force an output-version increment as the machine-readable format would change. It would also require an active filtering process after the command is run. Whether this proposal is implemented, this could be an interesting option if consequences are accepted.

If we do not implement this option, then users of the command will have to be able to filter projects out of the output. However, this is not an easy task, and it is subject to errors if for example a project and a package have the same name.

Prior Art

Unresolved Questions

  • Should the nature of the dependency (project or package) be included in the command output (or at least in the machine-readable one)?

Future Possibilities