Skip to content

Commit b837087

Browse files
committed
report apphost packages from publish
1 parent eee567f commit b837087

File tree

4 files changed

+258
-13
lines changed

4 files changed

+258
-13
lines changed

src/Microsoft.ComponentDetection.Detectors/Microsoft.ComponentDetection.Detectors.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,8 @@
3636
</EmbeddedResource>
3737
</ItemGroup>
3838

39+
<ItemGroup>
40+
<InternalsVisibleTo Include="Microsoft.ComponentDetection.Detectors.Tests" />
41+
</ItemGroup>
42+
3943
</Project>

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

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,18 @@ public class NuGetMSBuildBinaryLogComponentDetector : FileComponentDetector
2424
// the items listed below represent collection names that NuGet will resolve a package into, along with the metadata value names to get the package name and version
2525
private static readonly Dictionary<string, (string NameMetadata, string VersionMetadata)> ResolvedPackageItemNames = new Dictionary<string, (string, string)>(StringComparer.OrdinalIgnoreCase)
2626
{
27+
// regular restore operations
2728
["NativeCopyLocalItems"] = ("NuGetPackageId", "NuGetPackageVersion"),
2829
["ResourceCopyLocalItems"] = ("NuGetPackageId", "NuGetPackageVersion"),
2930
["RuntimeCopyLocalItems"] = ("NuGetPackageId", "NuGetPackageVersion"),
3031
["ResolvedAnalyzers"] = ("NuGetPackageId", "NuGetPackageVersion"),
3132
["_PackageDependenciesDesignTime"] = ("Name", "Version"),
33+
34+
// implicitly added by the SDK during a publish operation
35+
["ResolvedAppHostPack"] = ("NuGetPackageId", "NuGetPackageVersion"),
36+
["ResolvedSingleFileHostPack"] = ("NuGetPackageId", "NuGetPackageVersion"),
37+
["ResolvedComHostPack"] = ("NuGetPackageId", "NuGetPackageVersion"),
38+
["ResolvedIjwHostPack"] = ("NuGetPackageId", "NuGetPackageVersion"),
3239
};
3340

3441
private static readonly object MSBuildRegistrationGate = new();
@@ -46,7 +53,7 @@ public NuGetMSBuildBinaryLogComponentDetector(
4653

4754
public override IEnumerable<string> Categories => new[] { Enum.GetName(typeof(DetectorClass), DetectorClass.NuGet) };
4855

49-
public override IList<string> SearchPatterns { get; } = new List<string> { "*.binlog" };
56+
public override IList<string> SearchPatterns { get; } = new List<string> { "*.binlog", "*.buildlog" };
5057

5158
public override IEnumerable<ComponentType> SupportedComponentTypes { get; } = new[] { ComponentType.NuGet };
5259

@@ -133,19 +140,16 @@ protected override Task OnFileFoundAsync(ProcessRequest processRequest, IDiction
133140
{
134141
try
135142
{
136-
lock (MSBuildRegistrationGate)
137-
{
138-
if (!isMSBuildRegistered)
139-
{
140-
// this must happen once per process, and never again
141-
var defaultInstance = MSBuildLocator.QueryVisualStudioInstances().First();
142-
MSBuildLocator.RegisterInstance(defaultInstance);
143-
isMSBuildRegistered = true;
144-
}
145-
}
143+
EnsureMSBuildIsRegistered();
146144

147145
var singleFileComponentRecorder = this.ComponentRecorder.CreateSingleFileComponentRecorder(processRequest.ComponentStream.Location);
148-
var buildRoot = BinaryLog.ReadBuild(processRequest.ComponentStream.Stream);
146+
var extension = Path.GetExtension(processRequest.ComponentStream.Location);
147+
var buildRoot = extension.ToLower() switch
148+
{
149+
".binlog" => BinaryLog.ReadBuild(processRequest.ComponentStream.Stream),
150+
".buildlog" => BuildLogReader.Read(processRequest.ComponentStream.Stream),
151+
_ => throw new NotSupportedException($"Unexpected log file extension: {extension}"),
152+
};
149153
this.RecordLockfileVersion(buildRoot.FileFormatVersion);
150154
this.ProcessBinLog(buildRoot, singleFileComponentRecorder);
151155
}
@@ -158,6 +162,20 @@ protected override Task OnFileFoundAsync(ProcessRequest processRequest, IDiction
158162
return Task.CompletedTask;
159163
}
160164

165+
internal static void EnsureMSBuildIsRegistered()
166+
{
167+
lock (MSBuildRegistrationGate)
168+
{
169+
if (!isMSBuildRegistered)
170+
{
171+
// this must happen once per process, and never again
172+
var defaultInstance = MSBuildLocator.QueryVisualStudioInstances().First();
173+
MSBuildLocator.RegisterInstance(defaultInstance);
174+
isMSBuildRegistered = true;
175+
}
176+
}
177+
}
178+
161179
protected override Task OnDetectionFinishedAsync()
162180
{
163181
return Task.CompletedTask;

test/Microsoft.ComponentDetection.Detectors.Tests/Microsoft.ComponentDetection.Detectors.Tests.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@
77

88
<ItemGroup Label="Package References">
99
<PackageReference Include="FluentAssertions.Analyzers" PrivateAssets="all" />
10+
<PackageReference Include="Microsoft.Build.Framework" ExcludeAssets="Runtime" PrivateAssets="All" />
11+
<PackageReference Include="Microsoft.Build.Locator" />
1012
<PackageReference Include="Microsoft.Extensions.Logging" />
13+
<PackageReference Include="MSBuild.StructuredLogger" />
1114
<PackageReference Include="NuGet.Versioning" />
1215
<PackageReference Include="SemanticVersioning" />
1316
<PackageReference Include="System.Reactive" />

test/Microsoft.ComponentDetection.Detectors.Tests/NuGetMSBuildBinaryLogComponentDetectorTests.cs

Lines changed: 221 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,27 @@
11
namespace Microsoft.ComponentDetection.Detectors.Tests;
22

3+
using System;
4+
using System.IO;
35
using System.Linq;
46
using System.Threading.Tasks;
57
using FluentAssertions;
8+
using Microsoft.Build.Logging.StructuredLogger;
69
using Microsoft.ComponentDetection.Contracts;
710
using Microsoft.ComponentDetection.Contracts.TypedComponent;
811
using Microsoft.ComponentDetection.Detectors.NuGet;
912
using Microsoft.ComponentDetection.TestsUtilities;
1013
using Microsoft.VisualStudio.TestTools.UnitTesting;
1114

15+
using MSBuildTask = Microsoft.Build.Logging.StructuredLogger.Task;
16+
using Task = System.Threading.Tasks.Task;
17+
1218
[TestClass]
1319
[TestCategory("Governance/All")]
1420
[TestCategory("Governance/ComponentDetection")]
1521
public class NuGetMSBuildBinaryLogComponentDetectorTests : BaseDetectorTest<NuGetMSBuildBinaryLogComponentDetector>
1622
{
23+
static NuGetMSBuildBinaryLogComponentDetectorTests() => NuGetMSBuildBinaryLogComponentDetector.EnsureMSBuildIsRegistered();
24+
1725
[TestMethod]
1826
public async Task DependenciesAreReportedForEachProjectFile()
1927
{
@@ -206,12 +214,224 @@ public async Task PackagesReportedFromSeparateProjectsDoNotOverlap()
206214
solutionComponents.Should().BeEmpty();
207215
}
208216

217+
[TestMethod]
218+
public async Task PackagesImplicitlyAddedBySdkDuringPublishAreAdded()
219+
{
220+
// When a project is published, the SDK will add references to some AppHost specific packages. Doing an actual
221+
// publish operation here would be too slow, so a mock in-memory binary log is used that has the same shape
222+
// (although trimmed down) of a real publish log.
223+
var binlog = new Build()
224+
{
225+
Succeeded = true,
226+
Children =
227+
{
228+
new Project()
229+
{
230+
ProjectFile = "project.csproj",
231+
Children =
232+
{
233+
new Target()
234+
{
235+
Name = "ResolveFrameworkReferences",
236+
Children =
237+
{
238+
// ResolvedAppHostPack
239+
new MSBuildTask()
240+
{
241+
Name = "GetPackageDirectory",
242+
Children =
243+
{
244+
new Folder()
245+
{
246+
Name = "OutputItems",
247+
Children =
248+
{
249+
new AddItem()
250+
{
251+
Name = "ResolvedAppHostPack",
252+
Children =
253+
{
254+
new Item()
255+
{
256+
Name = "AppHost",
257+
Children =
258+
{
259+
new Metadata()
260+
{
261+
Name = "NuGetPackageId",
262+
Value = "Microsoft.NETCore.App.Host.win-x64",
263+
},
264+
new Metadata()
265+
{
266+
Name = "NuGetPackageVersion",
267+
Value = "6.0.33",
268+
},
269+
},
270+
},
271+
},
272+
},
273+
},
274+
},
275+
},
276+
},
277+
278+
// ResolvedSingleFileHostPack
279+
new MSBuildTask()
280+
{
281+
Name = "GetPackageDirectory",
282+
Children =
283+
{
284+
new Folder()
285+
{
286+
Name = "OutputItems",
287+
Children =
288+
{
289+
new AddItem()
290+
{
291+
Name = "ResolvedSingleFileHostPack",
292+
Children =
293+
{
294+
new Item()
295+
{
296+
Name = "SingleFileHost",
297+
Children =
298+
{
299+
new Metadata()
300+
{
301+
Name = "NuGetPackageId",
302+
Value = "Microsoft.NETCore.App.Host.win-x64",
303+
},
304+
new Metadata()
305+
{
306+
Name = "NuGetPackageVersion",
307+
Value = "6.0.33",
308+
},
309+
},
310+
},
311+
},
312+
},
313+
},
314+
},
315+
},
316+
},
317+
318+
// ResolvedComHostPack
319+
new MSBuildTask()
320+
{
321+
Name = "GetPackageDirectory",
322+
Children =
323+
{
324+
new Folder()
325+
{
326+
Name = "OutputItems",
327+
Children =
328+
{
329+
new AddItem()
330+
{
331+
Name = "ResolvedComHostPack",
332+
Children =
333+
{
334+
new Item()
335+
{
336+
Name = "ComHost",
337+
Children =
338+
{
339+
new Metadata()
340+
{
341+
Name = "NuGetPackageId",
342+
Value = "Microsoft.NETCore.App.Host.win-x64",
343+
},
344+
new Metadata()
345+
{
346+
Name = "NuGetPackageVersion",
347+
Value = "6.0.33",
348+
},
349+
},
350+
},
351+
},
352+
},
353+
},
354+
},
355+
},
356+
},
357+
358+
// ResolvedIjwHostPack
359+
new MSBuildTask()
360+
{
361+
Name = "GetPackageDirectory",
362+
Children =
363+
{
364+
new Folder()
365+
{
366+
Name = "OutputItems",
367+
Children =
368+
{
369+
new AddItem()
370+
{
371+
Name = "ResolvedIjwHostPack",
372+
Children =
373+
{
374+
new Item()
375+
{
376+
Name = "IjwHost",
377+
Children =
378+
{
379+
new Metadata()
380+
{
381+
Name = "NuGetPackageId",
382+
Value = "Microsoft.NETCore.App.Host.win-x64",
383+
},
384+
new Metadata()
385+
{
386+
Name = "NuGetPackageVersion",
387+
Value = "6.0.33",
388+
},
389+
},
390+
},
391+
},
392+
},
393+
},
394+
},
395+
},
396+
},
397+
},
398+
},
399+
},
400+
},
401+
},
402+
};
403+
404+
// in-memory logs need to be `.buildlog`
405+
var tempFile = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid():d}.buildlog");
406+
try
407+
{
408+
Serialization.Write(binlog, tempFile);
409+
using var binLogStream = File.OpenRead(tempFile);
410+
411+
var (scanResult, componentRecorder) = await this.DetectorTestUtility
412+
.WithFile(tempFile, binLogStream)
413+
.ExecuteDetectorAsync();
414+
var detectedComponents = componentRecorder.GetDetectedComponents();
415+
416+
var components = detectedComponents
417+
.Select(d => d.Component)
418+
.Cast<NuGetComponent>()
419+
.OrderBy(c => c.Name)
420+
.Select(c => $"{c.Name}/{c.Version}");
421+
components.Should().Equal("Microsoft.NETCore.App.Host.win-x64/6.0.33");
422+
}
423+
finally
424+
{
425+
File.Delete(tempFile);
426+
}
427+
}
428+
209429
private async Task<(IndividualDetectorScanResult ScanResult, IComponentRecorder ComponentRecorder)> ExecuteDetectorAndGetBinLogAsync(
210430
string projectContents,
211431
(string FileName, string Content)[] additionalFiles = null,
212432
(string Name, string Version, string TargetFramework, string DependenciesXml)[] mockedPackages = null)
213433
{
214-
using var binLogStream = await MSBuildTestUtilities.GetBinLogStreamFromFileContentsAsync("project.csproj", projectContents, additionalFiles, mockedPackages);
434+
using var binLogStream = await MSBuildTestUtilities.GetBinLogStreamFromFileContentsAsync("project.csproj", projectContents, additionalFiles: additionalFiles, mockedPackages: mockedPackages);
215435
var (scanResult, componentRecorder) = await this.DetectorTestUtility
216436
.WithFile("msbuild.binlog", binLogStream)
217437
.ExecuteDetectorAsync();

0 commit comments

Comments
 (0)