-
Notifications
You must be signed in to change notification settings - Fork 264
dotnet nuget why proposal #11875
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
dotnet nuget why proposal #11875
Changes from all commits
Commits
Show all changes
29 commits
Select commit
Hold shift + click to select a range
02e76f3
basic work
kartheekp-ms 2533d08
more updates
kartheekp-ms 8c2cdc8
updates
kartheekp-ms ed23c5f
more updates
kartheekp-ms 9ee902e
more work
kartheekp-ms bbc507c
update (added appendix)
kartheekp-ms a23a5f4
update
kartheekp-ms d5d7b93
fix typos
kartheekp-ms 3c5010f
typos
kartheekp-ms cb0ce7b
updated command name
kartheekp-ms 771ffbe
Update version option info
kartheekp-ms e5ef82f
Fixed header
pragnya17 a001db8
add version parameter to additional improvements section
pragnya17 809b357
rearrange the intro
pragnya17 c007b1f
changing the wording here
pragnya17 9e389a1
Changes based on comments on the PR
pragnya17 847240b
Remove unresolved questions section, packages.config is out of scope
pragnya17 06c68f3
diamond dependency
pragnya17 cddb63f
Fix typos, add more to additional improvements
pragnya17 20e0b81
update examples and wording in frameworks options sentence
pragnya17 ccb9f0f
move to additional improvements section
pragnya17 297b015
Rewording a few sentences/section headings
pragnya17 b33ca2a
change picture, add clarity to sentence
pragnya17 31ffcb7
update screenshot
pragnya17 ba61efc
Clarify sentence, move section around
pragnya17 f528520
Add sample project.assets.json file
pragnya17 31560cb
add section to example assets file
pragnya17 a1bf5d4
add limitation to PMUI view
pragnya17 768e7d9
Add some things to additional improvements
pragnya17 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,225 @@ | ||
# dotnet nuget why command | ||
|
||
- Author: [Kartheek Penagamuri](https://github.com/kartheekp-ms), [Pragnya Pandrate](https://github.com/pragnya17) | ||
- GitHub Issue [11782](https://github.com/NuGet/Home/issues/11782) | ||
|
||
## Summary | ||
|
||
Currently, there is not a great solution for developers to understand the nature of top-level packages and their transitive dependencies. The solution explorer in Visual Studio does a decent job at allowing a user to dive into each NuGet package & see all transitive dependencies from the top-level dependency. | ||
|
||
 | ||
|
||
However, in comparison to Visual Studio, the dotnet CLI does not provide insight into “why” a transitive dependency is listed, although it does list everything that has been resolved as seen below. | ||
|
||
 | ||
|
||
## Motivation | ||
|
||
A developer should be able to understand where every package in a solution/project originated from. This can help them understand complex dependency chains they may have within their projects. Having this understanding can help a developer promote a transitive dependency quickly & easily to a top-level dependency in case there is a security concern or conflict. | ||
|
||
## Explanation | ||
|
||
### Functional Explanation | ||
|
||
The NuGet restore operation generates a `project.assets.json` file under the `obj` folder for `PackageReference` style project. This file maintains a project's dependency graph. Therefore, this file can be used in order to produce a dependency graph when the `dotnet nuget why` command is called. | ||
|
||
Sample `project.assets.json` file: | ||
|
||
``` | ||
{ | ||
"version": 3, | ||
"targets": { | ||
"net6.0": { | ||
"Microsoft.CSharp/4.3.0": { | ||
"type": "package", | ||
"dependencies": { | ||
"System.Collections": "4.3.0", | ||
"System.Diagnostics.Debug": "4.3.0", | ||
"System.Dynamic.Runtime": "4.3.0", | ||
"System.Globalization": "4.3.0", | ||
"System.Linq": "4.3.0", | ||
"System.Linq.Expressions": "4.3.0", | ||
"System.ObjectModel": "4.3.0", | ||
"System.Reflection": "4.3.0", | ||
"System.Reflection.Extensions": "4.3.0", | ||
"System.Reflection.Primitives": "4.3.0", | ||
"System.Reflection.TypeExtensions": "4.3.0", | ||
"System.Resources.ResourceManager": "4.3.0", | ||
"System.Runtime": "4.3.0", | ||
"System.Runtime.Extensions": "4.3.0", | ||
"System.Runtime.InteropServices": "4.3.0", | ||
"System.Threading": "4.3.0" | ||
}, | ||
"compile": { | ||
... | ||
} | ||
}, | ||
"runtime": { | ||
... | ||
} | ||
}, | ||
"Microsoft.ML/1.7.1": { | ||
"type": "package", | ||
"dependencies": { | ||
"Microsoft.ML.CpuMath": "1.7.1", | ||
"Microsoft.ML.DataView": "1.7.1", | ||
"Newtonsoft.Json": "10.0.3", | ||
"System.CodeDom": "4.4.0", | ||
"System.Collections.Immutable": "1.5.0", | ||
"System.Memory": "4.5.3", | ||
"System.Reflection.Emit.Lightweight": "4.3.0", | ||
"System.Threading.Channels": "4.7.1" | ||
}, | ||
"compile": { | ||
... | ||
}, | ||
"runtime": { | ||
... | ||
}, | ||
"build": { | ||
... | ||
}, | ||
"runtimeTargets": { | ||
... | ||
}, | ||
}, | ||
"libraries": { | ||
... | ||
}, | ||
"projectFileDependencyGroups": { | ||
"net6.0": [ | ||
"Microsoft.ML >= 1.7.1", | ||
"Microsoft.ML.SampleUtils >= 0.19.1" | ||
] | ||
}, | ||
"packageFolders": { | ||
... | ||
}, | ||
"project": { | ||
"version": "1.0.0", | ||
"restore": { | ||
... | ||
}, | ||
"frameworks": { | ||
... | ||
} | ||
} | ||
} | ||
|
||
``` | ||
|
||
### Technical Explanation | ||
|
||
The `dotnet nuget why` command will print out the dependency graph of a given package only if it is part of the final graph. | ||
pragnya17 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
``` | ||
dotnet nuget why [<PROJECT>|<SOLUTION>] <PACKAGE_NAME> | ||
[-f|--framework <FRAMEWORK>] | ||
|
||
dotnet nuget why -h|--help | ||
``` | ||
#### Arguments | ||
|
||
- PROJECT | SOLUTION | ||
|
||
The project or solution file to operate on. If more than one solution is found, an error is thrown. If more than one project is found, the dependency graph for each project is printed. | ||
|
||
- PACKAGE_NAME | ||
|
||
The exact package name/id for which the dependency graph has to be identified. Package name wildcards are not supported. | ||
pragnya17 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
#### Options | ||
|
||
- -f|--framework <FRAMEWORK> | ||
|
||
Print out the dependency graph of a given package for a specific target framework. | ||
|
||
- -?|-h|--help | ||
|
||
Prints out a description of how to use the command. | ||
|
||
#### Examples | ||
pragnya17 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
- List dependency graph of a package given `package id`. | ||
|
||
``` | ||
dotnet nuget why packageA | ||
pragnya17 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
Project 'projectNameA' has the following dependency graph for 'packageA' | ||
pragnya17 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
[net6.0]: | ||
Microsoft.ML (1.0.0) -> Microsoft.ML.Util (1.0.0) -> packageA (1.0.0) | ||
[net472]: | ||
Microsoft.ML (1.0.0) -> Microsoft.ML.Util (1.0.0) -> packageA (1.0.0) | ||
|
||
Project 'projectNameB' has the following dependency graph for 'packageA' | ||
[net6.0]: | ||
Microsoft.ML (1.1.0) -> Microsoft.ML.Util (1.1.0) -> packageA (1.1.0) | ||
[net472]: | ||
Microsoft.ML (1.1.0) -> Microsoft.ML.Util (1.1.0) -> packageA (1.1.0) | ||
``` | ||
|
||
- List dependency graph of a package given `package id` when there is a diamond dependency (a package is brought in by more than one path). | ||
|
||
``` | ||
dotnet nuget why packageA | ||
|
||
Project 'projectNameA' has the following dependency graph for 'packageA' | ||
[net6.0]: | ||
Microsoft.ML (1.0.0) -> Microsoft.ML.Util (1.0.0) -> packageA (1.0.0) | ||
Microsoft.ML (1.0.0) -> Microsoft.ML.SampleUtils (1.0.0) -> packageA (1.0.0) | ||
``` | ||
|
||
- List dependency graph of a package given `package id` and `target framework`. | ||
|
||
``` | ||
dotnet nuget why packageA -f net6.0 | ||
|
||
Project 'projectNameA' has the following dependency graph for 'packageA' | ||
[net6.0]: | ||
Microsoft.ML (1.0.0) -> Microsoft.ML.Util (1.0.0) -> packageA (1.0.0) | ||
|
||
Project 'projectNameB' has the following dependency graph for 'packageA' | ||
[net6.0]: | ||
Microsoft.ML (1.1.0) -> Microsoft.ML.Util (1.1.0) -> packageA (1.1.0) | ||
``` | ||
|
||
## Rationale and Alternatives | ||
|
||
We could have considered a minor tweak to the existing experience of “dotnet list package --include-transitive” to provide the user with a sense of a package's dependencies. This could have been a new column next to “Resolved” called “Transitively Referenced” that had a list of the top-level packages that requested the dependency. The corresponding tracking issue can be found here: https://github.com/NuGet/Home/issues/11625. Unfortunately, this solution doesn't provide an easy way to identify the dependency graph `(starting from project -> top-level package -> transitive-dependency (1) -> ..-> transitive dependency (n))` for a particular package. | ||
pragnya17 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
 | ||
|
||
## Prior Art | ||
|
||
Within Visual Studio, there is a new panel in the Package Manager UI called “Transitive Packages” in which all transitive packages are displayed to the user. There is also an additional header titled “Top-level Packages”. | ||
|
||
When a user highlights a transitive package, they will see a pop-up that displays what top-level package(s) are bringing it in. | ||
|
||
 | ||
|
||
However, the Package Manager UI view still does not provide the user with enough detail about how transitive packages originate; for example, transitive dependencies that are brought in by a project are hidden. Therefore we propose the `dotnet nuget why` command. | ||
|
||
## Additional improvements | ||
pragnya17 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
If not specified, the command searches the current directory for one. | ||
|
||
Add a [--version <VERSION>] option to the command if the user wants to print dependency graphs for a specific version of the package. NuGet currently `flattens`, so it only allows one version of a package per framework to be resolved. See NuGet package versioning for more information. | ||
|
||
Allow the customer to look up transitive dependencies of more than one package. For example: `dotnet nuget why [<PROJECT>|<SOLUTION>] packages package1, package2` or `dotnet nuget why [<PROJECT>|<SOLUTION>] package 'nuget.*'`. | ||
|
||
Allow the customer to transitive dependencies of more than one framework. For example: `--framework net6.0 --framework netstandard2.0`. | ||
pragnya17 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
Create a better visualization of the dependency graph that is printed by the `dotnet nuget why` command by displaying a tree rather than just printing out a list of dependencies. | ||
|
||
Packages acquired through `PackageDownload` are recorded in the `project.assets.json` file under `project -> frameworks -> {frameworkName} -> downloadDependencies`. However, since they are recorded under `downloadDependcies` rather than `dependencies` they are not included in the current output of the `dotnet nuget why` command. In the future packages acquired through `PackageDownload` can be included in the output of the `dotnet nuget why` command. | ||
|
||
The NuGet restore operation downloads other packages during dependency resolution which are not part of the final dependency graph that the `dotnet nuget why` command prints out. The absence of a package id and version in the `project.assets.json` file indicates that a package was downloaded during dependency resolution but is not part of the final dependency graph. In the future these other packages can also be included in the dependency graph that the `dotnet nuget why` command prints out. | ||
|
||
Use non-zero exit codes for errors such as when the `project.assets.json` file is not present or the package is not found in the `project.assets.json` file. | ||
|
||
## Appendix | ||
|
||
- https://github.com/NuGet/Home/blob/dev/proposed/2020/Transitive-Dependencies.md | ||
- [npm-why](https://github.com/amio/npm-why#npm-why-) | ||
- [cargo-tree](https://doc.rust-lang.org/cargo/commands/cargo-tree.html) | ||
- [mvn dependency:tree](https://maven.apache.org/plugins/maven-dependency-plugin/usage.html#dependency:tree) | ||
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.