Skip to content

Commit 60c6b90

Browse files
authored
Record NuGet PackageDownloads (#1296)
* Record NuGet PackageDownloads Read PackageDownloads from the assets file and record them as top-level development dependencies.
1 parent a55475d commit 60c6b90

File tree

5 files changed

+192
-16
lines changed

5 files changed

+192
-16
lines changed

src/Microsoft.ComponentDetection.Detectors/nuget/NuGetPackageReferenceFrameworkAwareDetector.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@ protected override Task OnFileFoundAsync(ProcessRequest processRequest, IDiction
7878
this.NavigateAndRegister(target, explicitlyReferencedComponentIds, singleFileComponentRecorder, library, null, frameworkPackages);
7979
}
8080
}
81+
82+
// Register PackageDownload
83+
this.RegisterPackageDownloads(singleFileComponentRecorder, lockFile);
8184
}
8285
catch (Exception e)
8386
{
@@ -141,6 +144,30 @@ private void NavigateAndRegister(
141144
bool IsADevelopmentDependency(LockFileTargetLibrary library) => library.RuntimeAssemblies.Concat(library.RuntimeTargets).All(IsAPlaceholderItem);
142145
}
143146

147+
private void RegisterPackageDownloads(ISingleFileComponentRecorder singleFileComponentRecorder, LockFile lockFile)
148+
{
149+
foreach (var framework in lockFile.PackageSpec.TargetFrameworks)
150+
{
151+
foreach (var packageDownload in framework.DownloadDependencies)
152+
{
153+
if (packageDownload?.Name is null || packageDownload?.VersionRange?.MinVersion is null)
154+
{
155+
continue;
156+
}
157+
158+
var libraryComponent = new DetectedComponent(new NuGetComponent(packageDownload.Name, packageDownload.VersionRange.MinVersion.ToNormalizedString()));
159+
160+
// PackageDownload is always a development dependency since it's usage does not make it part of the application
161+
singleFileComponentRecorder.RegisterUsage(
162+
libraryComponent,
163+
isExplicitReferencedDependency: true,
164+
parentComponentId: null,
165+
isDevelopmentDependency: true,
166+
targetFramework: framework.FrameworkName?.GetShortFolderName());
167+
}
168+
}
169+
}
170+
144171
private List<(string Name, Version Version, VersionRange VersionRange)> GetTopLevelLibraries(LockFile lockFile)
145172
{
146173
// First, populate libraries from the TargetFrameworks section -- This is the base level authoritative list of nuget packages a project has dependencies on.

test/Microsoft.ComponentDetection.Detectors.Tests/Mocks/TestResources.Designer.cs

Lines changed: 43 additions & 16 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/Microsoft.ComponentDetection.Detectors.Tests/Mocks/TestResources.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,4 +151,7 @@
151151
<data name="project_assets_3_1" type="System.Resources.ResXFileRef, System.Windows.Forms">
152152
<value>..\Resources\project_assets_3_1.json;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252</value>
153153
</data>
154+
<data name="project_assets_packageDownload" type="System.Resources.ResXFileRef, System.Windows.Forms">
155+
<value>..\Resources\project_assets_packageDownload.json;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252</value>
156+
</data>
154157
</root>
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
{
2+
"version": 3,
3+
"targets": {
4+
".NETFramework,Version=v4.7.2": {},
5+
"net8.0": {}
6+
},
7+
"libraries": {},
8+
"projectFileDependencyGroups": {
9+
".NETFramework,Version=v4.7.2": [],
10+
"net8.0": []
11+
},
12+
"packageFolders": {
13+
"C:\\Users\\username\\.nuget\\packages\\": {},
14+
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages": {}
15+
},
16+
"project": {
17+
"version": "1.0.0",
18+
"restore": {
19+
"projectUniqueName": "C:\\src\\packageDownloadSample\\packageDownloadSample.csproj",
20+
"projectName": "packageDownloadSample",
21+
"projectPath": "C:\\src\\packageDownloadSample\\packageDownloadSample.csproj",
22+
"packagesPath": "C:\\Users\\username\\.nuget\\packages\\",
23+
"outputPath": "C:\\src\\packageDownloadSample\\obj\\",
24+
"projectStyle": "PackageReference",
25+
"crossTargeting": true,
26+
"fallbackFolders": [
27+
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages"
28+
],
29+
"configFilePaths": [
30+
"C:\\Users\\username\\AppData\\Roaming\\NuGet\\NuGet.Config",
31+
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.FallbackLocation.config",
32+
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config"
33+
],
34+
"originalTargetFrameworks": [
35+
"net472",
36+
"net8.0"
37+
],
38+
"sources": {
39+
"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
40+
"C:\\Program Files\\dotnet\\library-packs": {},
41+
"https://api.nuget.org/v3/index.json": {}
42+
},
43+
"frameworks": {
44+
"net8.0": {
45+
"targetAlias": "net8.0",
46+
"projectReferences": {}
47+
},
48+
"net472": {
49+
"targetAlias": "net472",
50+
"projectReferences": {}
51+
}
52+
},
53+
"warningProperties": {
54+
"warnAsError": [
55+
"NU1605"
56+
]
57+
},
58+
"restoreAuditProperties": {
59+
"enableAudit": "true",
60+
"auditLevel": "low",
61+
"auditMode": "all"
62+
},
63+
"SdkAnalysisLevel": "9.0.100"
64+
},
65+
"frameworks": {
66+
"net8.0": {
67+
"targetAlias": "net8.0",
68+
"imports": [
69+
"net461",
70+
"net462",
71+
"net47",
72+
"net471",
73+
"net472",
74+
"net48",
75+
"net481"
76+
],
77+
"assetTargetFallback": true,
78+
"warn": true,
79+
"downloadDependencies": [
80+
{
81+
"name": "Microsoft.Build",
82+
"version": "[17.8.3, 17.8.3]"
83+
},
84+
{
85+
"name": "Microsoft.Build.Framework",
86+
"version": "[17.8.3, 17.8.3]"
87+
},
88+
{
89+
"name": "Microsoft.Build.Utilities.Core",
90+
"version": "[17.8.3, 17.8.3]"
91+
}
92+
],
93+
"frameworkReferences": {
94+
"Microsoft.NETCore.App": {
95+
"privateAssets": "all"
96+
}
97+
},
98+
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.100/PortableRuntimeIdentifierGraph.json"
99+
},
100+
"net472": {
101+
"targetAlias": "net472",
102+
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.100\\RuntimeIdentifierGraph.json"
103+
}
104+
}
105+
}
106+
}

test/Microsoft.ComponentDetection.Detectors.Tests/nuget/NuGetPackageReferenceFrameworkAwareDetectorTests.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,19 @@ public async Task ScanDirectoryAsync_DependencyGraph_3_1_VerificationAsync()
266266
}
267267
}
268268

269+
[TestMethod]
270+
public async Task ScanDirectoryAsync_PackageDownload_VerificationAsync()
271+
{
272+
var (scanResult, componentRecorder) = await this.DetectorTestUtility
273+
.WithFile(this.projectAssetsJsonFileName, TestResources.project_assets_packageDownload)
274+
.ExecuteDetectorAsync();
275+
276+
var developmentDependencies = componentRecorder.GetDetectedComponents().Where(c => componentRecorder.GetEffectiveDevDependencyValue(c.Component.Id).GetValueOrDefault());
277+
developmentDependencies.Should().HaveCount(3, "PackageDownload dev dependencies should exist.");
278+
developmentDependencies.Select(c => c.Component).Should().AllBeOfType<NuGetComponent>();
279+
developmentDependencies.Select(c => c.TargetFrameworks).Should().AllSatisfy(tfms => tfms.Should().BeEquivalentTo(["net8.0"]));
280+
}
281+
269282
[TestMethod]
270283
public async Task ScanDirectory_NoPackageSpecAsync()
271284
{

0 commit comments

Comments
 (0)