Skip to content

Commit 164770f

Browse files
authored
Fix Go CLI cyclic dependency graph (#103)
* Fix Go CLI cyclic dependency graph
1 parent 6c26b9d commit 164770f

File tree

2 files changed

+39
-5
lines changed

2 files changed

+39
-5
lines changed

Diff for: src/Microsoft.ComponentDetection.Detectors/go/GoComponentDetector.cs

+11-5
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,15 @@ public class GoComponentDetector : FileComponentDetector
3333

3434
public override IEnumerable<ComponentType> SupportedComponentTypes { get; } = new[] { ComponentType.Go };
3535

36-
public override int Version => 3;
36+
public override int Version => 4;
3737

3838
private HashSet<string> projectRoots = new HashSet<string>();
3939

4040
protected override async Task OnFileFound(ProcessRequest processRequest, IDictionary<string, string> detectorArgs)
4141
{
4242
var singleFileComponentRecorder = processRequest.SingleFileComponentRecorder;
4343
var file = processRequest.ComponentStream;
44-
44+
4545
var projectRootDirectory = Directory.GetParent(file.Location);
4646
if (projectRoots.Any(path => projectRootDirectory.FullName.StartsWith(path)))
4747
{
@@ -57,9 +57,10 @@ protected override async Task OnFileFound(ProcessRequest processRequest, IDictio
5757
wasGoCliScanSuccessful = await UseGoCliToScan(file.Location, singleFileComponentRecorder);
5858
}
5959
}
60-
catch
60+
catch (Exception ex)
6161
{
62-
Logger.LogInfo("Failed to detect components using go cli.");
62+
Logger.LogError($"Failed to detect components using go cli. Location: {file.Location}");
63+
Logger.LogException(ex, isError: true, printException: true);
6364
}
6465
finally
6566
{
@@ -231,7 +232,12 @@ private void PopulateDependencyGraph(string goGraphOutput, ISingleFileComponentR
231232
}
232233
else if (isParentParsed && isChildParsed)
233234
{
234-
// Go output guarantees that all parents will be output before children
235+
// Go can have a cyclic dependency between modules, which could cause child components to be listed first than parents. Reproducible with Go 1.16
236+
if (singleFileComponentRecorder.GetComponent(parentComponent.Id) == null)
237+
{
238+
singleFileComponentRecorder.RegisterUsage(new DetectedComponent(parentComponent));
239+
}
240+
235241
singleFileComponentRecorder.RegisterUsage(new DetectedComponent(childComponent), parentComponentId: parentComponent.Id);
236242
}
237243
else

Diff for: test/Microsoft.ComponentDetection.Detectors.Tests/GoComponentDetectorTests.cs

+28
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,34 @@ public async Task TestGoDetector_GoGraphHappyPath()
335335
Assert.AreEqual(4, detectedComponents.Count());
336336
}
337337

338+
[TestMethod]
339+
public async Task TestGoDetector_GoGraphCyclicDependencies()
340+
{
341+
var goGraph = @"
342+
github.com/prometheus/[email protected] github.com/prometheus/[email protected]
343+
github.com/prometheus/[email protected] github.com/prometheus/[email protected]";
344+
commandLineMock.Setup(x => x.CanCommandBeLocated("go", null, It.IsAny<DirectoryInfo>(), It.IsAny<string[]>()))
345+
.ReturnsAsync(true);
346+
347+
commandLineMock.Setup(x => x.ExecuteCommand("go", null, It.IsAny<DirectoryInfo>(), new[] { "mod", "graph" }))
348+
.ReturnsAsync(new CommandLineExecutionResult
349+
{
350+
ExitCode = 0,
351+
StdOut = goGraph,
352+
});
353+
354+
envVarService.Setup(x => x.DoesEnvironmentVariableExist("EnableGoCliScan")).Returns(true);
355+
356+
var (scanResult, componentRecorder) = await detectorTestUtility
357+
.WithFile("go.mod", string.Empty)
358+
.ExecuteDetector();
359+
360+
Assert.AreEqual(ProcessingResultCode.Success, scanResult.ResultCode);
361+
362+
var detectedComponents = componentRecorder.GetDetectedComponents();
363+
Assert.AreEqual(3, detectedComponents.Count());
364+
}
365+
338366
[TestMethod]
339367
public async Task TestGoDetector_GoCliRequiresEnvVarToRun()
340368
{

0 commit comments

Comments
 (0)