Feature: Add conan detector that parses conan.lock files of conan package manager version 1.x (#692)
Co-authored-by: Justin Perez <justinmp@vt.edu> Co-authored-by: Ashok Gowtham Mathivanan <amathivanan@lenovo.com>
This commit is contained in:
Родитель
00cc3b9080
Коммит
9de5ba459e
|
@ -0,0 +1,11 @@
|
|||
# Conan Detection
|
||||
## Requirements
|
||||
Conan detection relies on a conan.lock file being present.
|
||||
|
||||
## Detection strategy
|
||||
Conan detection is performed by parsing every **conan.lock** found under the scan directory.
|
||||
|
||||
## Known limitations
|
||||
Conan detection will not work if lock files are not being used or not yet generated. So ensure to run the conan build to generate the lock file(s) before running the scan.
|
||||
|
||||
Full dependency graph generation is not supported. However, dependency relationships identified/present in the **conan.lock** file is captured.
|
|
@ -17,6 +17,7 @@ public class TypedComponentConverter : JsonConverter
|
|||
{ ComponentType.Git, typeof(GitComponent) },
|
||||
{ ComponentType.RubyGems, typeof(RubyGemsComponent) },
|
||||
{ ComponentType.Cargo, typeof(CargoComponent) },
|
||||
{ ComponentType.Conan, typeof(ConanComponent) },
|
||||
{ ComponentType.Pip, typeof(PipComponent) },
|
||||
{ ComponentType.Go, typeof(GoComponent) },
|
||||
{ ComponentType.DockerImage, typeof(DockerImageComponent) },
|
||||
|
|
|
@ -53,4 +53,7 @@ public enum ComponentType : byte
|
|||
|
||||
[EnumMember]
|
||||
DockerReference = 16,
|
||||
|
||||
[EnumMember]
|
||||
Conan = 17,
|
||||
}
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
namespace Microsoft.ComponentDetection.Contracts.TypedComponent;
|
||||
|
||||
using PackageUrl;
|
||||
|
||||
public class ConanComponent : TypedComponent
|
||||
{
|
||||
private ConanComponent()
|
||||
{
|
||||
// reserved for deserialization
|
||||
}
|
||||
|
||||
public ConanComponent(string name, string version)
|
||||
{
|
||||
this.Name = this.ValidateRequiredInput(name, nameof(this.Name), nameof(ComponentType.Conan));
|
||||
this.Version = this.ValidateRequiredInput(version, nameof(this.Version), nameof(ComponentType.Conan));
|
||||
}
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public string Version { get; set; }
|
||||
|
||||
public override ComponentType Type => ComponentType.Conan;
|
||||
|
||||
public override string Id => $"{this.Name} {this.Version} - {this.Type}";
|
||||
|
||||
public override PackageURL PackageUrl => new PackageURL("conan", string.Empty, this.Name, this.Version, null, string.Empty);
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
namespace Microsoft.ComponentDetection.Detectors.Conan;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.ComponentDetection.Contracts;
|
||||
using Microsoft.ComponentDetection.Contracts.Internal;
|
||||
using Microsoft.ComponentDetection.Contracts.TypedComponent;
|
||||
using Microsoft.ComponentDetection.Detectors.Conan.Contracts;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
public class ConanLockComponentDetector : FileComponentDetector, IDefaultOffComponentDetector
|
||||
{
|
||||
public ConanLockComponentDetector(
|
||||
IComponentStreamEnumerableFactory componentStreamEnumerableFactory,
|
||||
IObservableDirectoryWalkerFactory walkerFactory,
|
||||
ILogger<ConanLockComponentDetector> logger)
|
||||
{
|
||||
this.ComponentStreamEnumerableFactory = componentStreamEnumerableFactory;
|
||||
this.Scanner = walkerFactory;
|
||||
this.Logger = logger;
|
||||
}
|
||||
|
||||
public override string Id => "ConanLock";
|
||||
|
||||
public override IList<string> SearchPatterns => new List<string> { "conan.lock" };
|
||||
|
||||
public override IEnumerable<ComponentType> SupportedComponentTypes => new[] { ComponentType.Conan };
|
||||
|
||||
public override int Version { get; } = 1;
|
||||
|
||||
public override IEnumerable<string> Categories => new List<string> { "Conan" };
|
||||
|
||||
protected override async Task OnFileFoundAsync(ProcessRequest processRequest, IDictionary<string, string> detectorArgs)
|
||||
{
|
||||
var singleFileComponentRecorder = processRequest.SingleFileComponentRecorder;
|
||||
var conanLockFile = processRequest.ComponentStream;
|
||||
|
||||
try
|
||||
{
|
||||
var conanLock = await JsonSerializer.DeserializeAsync<ConanLock>(conanLockFile.Stream);
|
||||
this.RecordLockfileVersion(conanLock.Version);
|
||||
|
||||
if (!conanLock.HasNodes())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var packagesDictionary = conanLock.GraphLock.Nodes;
|
||||
var explicitReferencedDependencies = new HashSet<string>();
|
||||
var developmentDependencies = new HashSet<string>();
|
||||
if (packagesDictionary.ContainsKey("0"))
|
||||
{
|
||||
packagesDictionary.Remove("0", out var rootNode);
|
||||
if (rootNode?.Requires != null)
|
||||
{
|
||||
explicitReferencedDependencies = new HashSet<string>(rootNode.Requires);
|
||||
}
|
||||
|
||||
if (rootNode?.BuildRequires != null)
|
||||
{
|
||||
developmentDependencies = new HashSet<string>(rootNode.BuildRequires);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var (packageIndex, package) in packagesDictionary)
|
||||
{
|
||||
singleFileComponentRecorder.RegisterUsage(
|
||||
new DetectedComponent(package.ToComponent()),
|
||||
isExplicitReferencedDependency: explicitReferencedDependencies.Contains(packageIndex),
|
||||
isDevelopmentDependency: developmentDependencies.Contains(packageIndex));
|
||||
}
|
||||
|
||||
foreach (var (conanPackageIndex, package) in packagesDictionary)
|
||||
{
|
||||
var parentPackages = packagesDictionary.Values.Where(package => package.Requires?.Contains(conanPackageIndex) == true);
|
||||
foreach (var parentPackage in parentPackages)
|
||||
{
|
||||
singleFileComponentRecorder.RegisterUsage(new DetectedComponent(package.ToComponent()), false, parentPackage.ToComponent().Id, isDevelopmentDependency: false);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// If something went wrong, just ignore the file
|
||||
this.Logger.LogError(e, "Failed to process conan.lock file '{ConanLockLocation}'", conanLockFile.Location);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
namespace Microsoft.ComponentDetection.Detectors.Conan.Contracts;
|
||||
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
public class ConanLock
|
||||
{
|
||||
[JsonPropertyName("version")]
|
||||
public string Version { get; set; }
|
||||
|
||||
[JsonPropertyName("profile_host")]
|
||||
public string ProfileHost { get; set; }
|
||||
|
||||
[JsonPropertyName("profile_build")]
|
||||
public string ProfileBuild { get; set; }
|
||||
|
||||
[JsonPropertyName("graph_lock")]
|
||||
public ConanLockGraph GraphLock { get; set; }
|
||||
|
||||
internal bool HasNodes() => this.GraphLock?.Nodes?.Count > 0;
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
namespace Microsoft.ComponentDetection.Detectors.Conan.Contracts;
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
public class ConanLockGraph
|
||||
{
|
||||
[JsonPropertyName("revisions_enabled")]
|
||||
public bool RevisionsEnabled { get; set; }
|
||||
|
||||
[JsonPropertyName("nodes")]
|
||||
public Dictionary<string, ConanLockNode> Nodes { get; set; }
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
namespace Microsoft.ComponentDetection.Detectors.Conan.Contracts;
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text.Json.Serialization;
|
||||
using Microsoft.ComponentDetection.Contracts.TypedComponent;
|
||||
|
||||
public class ConanLockNode
|
||||
{
|
||||
[JsonPropertyName("context")]
|
||||
public string Context { get; set; }
|
||||
|
||||
[JsonPropertyName("modified")]
|
||||
public bool? Modified { get; set; }
|
||||
|
||||
[JsonPropertyName("options")]
|
||||
public string Options { get; set; }
|
||||
|
||||
[JsonPropertyName("package_id")]
|
||||
public string PackageId { get; set; }
|
||||
|
||||
[JsonPropertyName("path")]
|
||||
public string Path { get; set; }
|
||||
|
||||
[JsonPropertyName("prev")]
|
||||
public string Previous { get; set; }
|
||||
|
||||
[JsonPropertyName("ref")]
|
||||
public string Reference { get; set; }
|
||||
|
||||
[JsonPropertyName("requires")]
|
||||
public string[] Requires { get; set; }
|
||||
|
||||
[JsonPropertyName("build_requires")]
|
||||
public string[] BuildRequires { get; set; }
|
||||
|
||||
internal string Name() => this.Reference == null ? string.Empty : this.Reference.Split('/', 2, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries).FirstOrDefault("Unknown");
|
||||
|
||||
internal TypedComponent ToComponent() => new ConanComponent(this.Name(), this.Version());
|
||||
|
||||
internal string Version() => this.Reference == null ? string.Empty : this.Reference.Split('/', 2, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries).Skip(1).FirstOrDefault("None");
|
||||
}
|
|
@ -4,6 +4,7 @@ using Microsoft.ComponentDetection.Common;
|
|||
using Microsoft.ComponentDetection.Common.Telemetry;
|
||||
using Microsoft.ComponentDetection.Contracts;
|
||||
using Microsoft.ComponentDetection.Detectors.CocoaPods;
|
||||
using Microsoft.ComponentDetection.Detectors.Conan;
|
||||
using Microsoft.ComponentDetection.Detectors.Dockerfile;
|
||||
using Microsoft.ComponentDetection.Detectors.Go;
|
||||
using Microsoft.ComponentDetection.Detectors.Gradle;
|
||||
|
@ -78,6 +79,9 @@ public static class ServiceCollectionExtensions
|
|||
// CocoaPods
|
||||
services.AddSingleton<IComponentDetector, PodComponentDetector>();
|
||||
|
||||
// Conan
|
||||
services.AddSingleton<IComponentDetector, ConanLockComponentDetector>();
|
||||
|
||||
// Conda
|
||||
services.AddSingleton<IComponentDetector, CondaLockComponentDetector>();
|
||||
|
||||
|
|
|
@ -122,6 +122,18 @@ public class TypedComponentSerializationTests
|
|||
cargoComponent.Version.Should().Be("1.2.3");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TypedComponent_Serialization_Conan()
|
||||
{
|
||||
TypedComponent tc = new ConanComponent("SomeConanPackage", "1.2.3");
|
||||
var result = JsonConvert.SerializeObject(tc);
|
||||
var deserializedTC = JsonConvert.DeserializeObject<TypedComponent>(result);
|
||||
deserializedTC.Should().BeOfType(typeof(ConanComponent));
|
||||
var conanComponent = (ConanComponent)deserializedTC;
|
||||
conanComponent.Name.Should().Be("SomeConanPackage");
|
||||
conanComponent.Version.Should().Be("1.2.3");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TypedComponent_Serialization_Pip()
|
||||
{
|
||||
|
|
|
@ -0,0 +1,259 @@
|
|||
namespace Microsoft.ComponentDetection.Detectors.Tests;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Microsoft.ComponentDetection.Contracts;
|
||||
using Microsoft.ComponentDetection.Contracts.TypedComponent;
|
||||
using Microsoft.ComponentDetection.Detectors.Conan;
|
||||
using Microsoft.ComponentDetection.Detectors.Tests.Utilities;
|
||||
using Microsoft.ComponentDetection.TestsUtilities;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
[TestClass]
|
||||
[TestCategory("Governance/All")]
|
||||
[TestCategory("Governance/ComponentDetection")]
|
||||
public class ConanLockComponentDetectorTests : BaseDetectorTest<ConanLockComponentDetector>
|
||||
{
|
||||
private readonly string testConanLockString = @"{
|
||||
""graph_lock"": {
|
||||
""nodes"": {
|
||||
""0"": {
|
||||
""ref"": ""MyConanProject/None"",
|
||||
""options"": ""SomeLongOptionsString"",
|
||||
""requires"": [
|
||||
""1"",
|
||||
""2"",
|
||||
""3"",
|
||||
""4"",
|
||||
""5"",
|
||||
""6""
|
||||
],
|
||||
""path"": ""../conanfile.py"",
|
||||
""context"": ""host""
|
||||
},
|
||||
""1"": {
|
||||
""ref"": ""libabc/1.2.12#someHashOfLibAbc"",
|
||||
""options"": ""someOptionsString"",
|
||||
""package_id"": ""packageIdOfLibAbc"",
|
||||
""prev"": ""someHashOfLibAbc"",
|
||||
""context"": ""host""
|
||||
},
|
||||
""2"": {
|
||||
""ref"": ""libawesomelibrary/3.2.1#someHashOfLibAwesomeLibrary"",
|
||||
""options"": ""someOptionsString"",
|
||||
""package_id"": ""packageIdOfLibAwesomeLibrary"",
|
||||
""prev"": ""someHashOfLibAwesomeLibrary"",
|
||||
""requires"": [
|
||||
""1""
|
||||
],
|
||||
""context"": ""host""
|
||||
},
|
||||
""3"": {
|
||||
""ref"": ""libanotherlibrary1/2.3.4#someHashOfLibAnotherLibrary1"",
|
||||
""options"": ""someOptionsString"",
|
||||
""package_id"": ""packageIdOfLibAnotherLibrary1"",
|
||||
""prev"": ""someHashOfLibAnotherLibrary1"",
|
||||
""requires"": [
|
||||
""4"",
|
||||
""6""
|
||||
],
|
||||
""context"": ""host""
|
||||
},
|
||||
""4"": {
|
||||
""ref"": ""libanotherlibrary2/3.4.5#someHashOfLibAnotherLibrary2"",
|
||||
""options"": ""someOptionsString"",
|
||||
""package_id"": ""packageIdOfLibAnotherLibrary2"",
|
||||
""prev"": ""someHashOfLibAnotherLibrary2"",
|
||||
""requires"": [
|
||||
""5""
|
||||
],
|
||||
""context"": ""host""
|
||||
},
|
||||
""5"": {
|
||||
""ref"": ""libanotherlibrary3/4.5.6#someHashOfLibAnotherLibrary3"",
|
||||
""options"": """",
|
||||
""package_id"": ""packageIdOfLibAnotherLibrary3"",
|
||||
""prev"": ""someHashOfLibAnotherLibrary3"",
|
||||
""context"": ""host""
|
||||
},
|
||||
""6"": {
|
||||
""ref"": ""libanotherlibrary4/5.6.7#someHashOfLibAnotherLibrary4"",
|
||||
""options"": ""someOptionsString"",
|
||||
""package_id"": ""packageIdOfLibAnotherLibrary4"",
|
||||
""prev"": ""someHashOfLibAnotherLibrary4"",
|
||||
""modified"": true,
|
||||
""context"": ""host""
|
||||
}
|
||||
},
|
||||
""revisions_enabled"": true
|
||||
},
|
||||
""version"": ""0.4"",
|
||||
""profile_host"": ""someLongProfileHostSettingsString\n"",
|
||||
""profile_build"": ""someLongProfileBuildSettingsString\n""
|
||||
}
|
||||
";
|
||||
|
||||
private readonly string testConanLockStringWithNullValueForRootNode = @"{
|
||||
""graph_lock"": {
|
||||
""nodes"": {
|
||||
""0"": null,
|
||||
""1"": {
|
||||
""ref"": ""libabc/1.2.12#someHashOfLibAbc"",
|
||||
""options"": ""someOptionsString"",
|
||||
""package_id"": ""packageIdOfLibAbc"",
|
||||
""prev"": ""someHashOfLibAbc"",
|
||||
""context"": ""host""
|
||||
}
|
||||
},
|
||||
""revisions_enabled"": true
|
||||
},
|
||||
""version"": ""0.4"",
|
||||
""profile_host"": ""someLongProfileHostSettingsString\n"",
|
||||
""profile_build"": ""someLongProfileBuildSettingsString\n""
|
||||
}
|
||||
";
|
||||
|
||||
private readonly string testConanLockNoDependenciesString = @"{
|
||||
""graph_lock"": {
|
||||
""nodes"": {
|
||||
""0"": {
|
||||
""ref"": ""MyConanProject/None"",
|
||||
""options"": ""SomeLongOptionsString"",
|
||||
""path"": ""../conanfile.py"",
|
||||
""context"": ""host""
|
||||
}
|
||||
},
|
||||
""revisions_enabled"": true
|
||||
},
|
||||
""version"": ""0.4"",
|
||||
""profile_host"": ""someLongProfileHostSettingsString\n"",
|
||||
""profile_build"": ""someLongProfileBuildSettingsString\n""
|
||||
}
|
||||
";
|
||||
|
||||
[TestMethod]
|
||||
public async Task TestGraphIsCorrectAsync()
|
||||
{
|
||||
var (result, componentRecorder) = await this.DetectorTestUtility
|
||||
.WithFile("Conan.lock", this.testConanLockString)
|
||||
.ExecuteDetectorAsync();
|
||||
|
||||
result.ResultCode.Should().Be(ProcessingResultCode.Success);
|
||||
componentRecorder.GetDetectedComponents().Count().Should().Be(6);
|
||||
|
||||
var graph = componentRecorder.GetDependencyGraphsByLocation().Values.First(); // There should only be 1
|
||||
|
||||
// Verify explicitly referenced roots
|
||||
var rootComponents = new List<string>
|
||||
{
|
||||
"libabc 1.2.12#someHashOfLibAbc - Conan",
|
||||
"libawesomelibrary 3.2.1#someHashOfLibAwesomeLibrary - Conan",
|
||||
"libanotherlibrary1 2.3.4#someHashOfLibAnotherLibrary1 - Conan",
|
||||
"libanotherlibrary2 3.4.5#someHashOfLibAnotherLibrary2 - Conan",
|
||||
"libanotherlibrary3 4.5.6#someHashOfLibAnotherLibrary3 - Conan",
|
||||
"libanotherlibrary4 5.6.7#someHashOfLibAnotherLibrary4 - Conan",
|
||||
};
|
||||
|
||||
var enumerable = graph.GetComponents().ToList();
|
||||
|
||||
rootComponents.ForEach(rootComponentId =>
|
||||
{
|
||||
graph.IsComponentExplicitlyReferenced(rootComponentId).Should().BeTrue($"Expected Component to be explicitly referenced but it is not: {rootComponentId}");
|
||||
});
|
||||
|
||||
// components without any dependencies
|
||||
graph.GetDependenciesForComponent("libabc 1.2.12#someHashOfLibAbc - Conan").Should().BeEmpty();
|
||||
graph.GetDependenciesForComponent("libanotherlibrary3 4.5.6#someHashOfLibAnotherLibrary3 - Conan").Should().BeEmpty();
|
||||
graph.GetDependenciesForComponent("libanotherlibrary4 5.6.7#someHashOfLibAnotherLibrary4 - Conan").Should().BeEmpty();
|
||||
|
||||
// Verify dependencies for other dependencies
|
||||
graph.GetDependenciesForComponent("libawesomelibrary 3.2.1#someHashOfLibAwesomeLibrary - Conan").Should().BeEquivalentTo(new[] { "libabc 1.2.12#someHashOfLibAbc - Conan" });
|
||||
var a = graph.GetDependenciesForComponent("libanotherlibrary1 2.3.4#someHashOfLibAnotherLibrary1 - Conan");
|
||||
graph.GetDependenciesForComponent("libanotherlibrary1 2.3.4#someHashOfLibAnotherLibrary1 - Conan").Should().BeEquivalentTo(new[] { "libanotherlibrary2 3.4.5#someHashOfLibAnotherLibrary2 - Conan", "libanotherlibrary4 5.6.7#someHashOfLibAnotherLibrary4 - Conan" });
|
||||
graph.GetDependenciesForComponent("libanotherlibrary2 3.4.5#someHashOfLibAnotherLibrary2 - Conan").Should().BeEquivalentTo(new[] { "libanotherlibrary3 4.5.6#someHashOfLibAnotherLibrary3 - Conan" });
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task TestDetectionForConanLockFileWithNullValuesForRootNodeAsync()
|
||||
{
|
||||
var (result, componentRecorder) = await this.DetectorTestUtility
|
||||
.WithFile("Conan.lock", this.testConanLockStringWithNullValueForRootNode)
|
||||
.ExecuteDetectorAsync();
|
||||
|
||||
result.ResultCode.Should().Be(ProcessingResultCode.Success);
|
||||
componentRecorder.GetDetectedComponents().Count().Should().Be(1);
|
||||
|
||||
(componentRecorder.GetDetectedComponents().First().Component as ConanComponent).Name.Should().Be("libabc");
|
||||
(componentRecorder.GetDetectedComponents().First().Component as ConanComponent).Version.Should().Be("1.2.12#someHashOfLibAbc");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task TestConanDetectorAsync()
|
||||
{
|
||||
var (result, componentRecorder) = await this.DetectorTestUtility
|
||||
.WithFile("Conan.lock", this.testConanLockString)
|
||||
.ExecuteDetectorAsync();
|
||||
|
||||
result.ResultCode.Should().Be(ProcessingResultCode.Success);
|
||||
componentRecorder.GetDetectedComponents().Count().Should().Be(6);
|
||||
|
||||
IDictionary<string, string> packageVersions = new Dictionary<string, string>()
|
||||
{
|
||||
{ "libabc", "1.2.12#someHashOfLibAbc" },
|
||||
{ "libawesomelibrary", "3.2.1#someHashOfLibAwesomeLibrary" },
|
||||
{ "libanotherlibrary1", "2.3.4#someHashOfLibAnotherLibrary1" },
|
||||
{ "libanotherlibrary2", "3.4.5#someHashOfLibAnotherLibrary2" },
|
||||
{ "libanotherlibrary3", "4.5.6#someHashOfLibAnotherLibrary3" },
|
||||
{ "libanotherlibrary4", "5.6.7#someHashOfLibAnotherLibrary4" },
|
||||
};
|
||||
|
||||
IDictionary<string, ISet<string>> packageDependencyRoots = new Dictionary<string, ISet<string>>()
|
||||
{
|
||||
{ "libabc", new HashSet<string>() { "libabc", "libawesomelibrary" } },
|
||||
{ "libawesomelibrary", new HashSet<string>() { "libawesomelibrary" } },
|
||||
{ "libanotherlibrary1", new HashSet<string>() { "libanotherlibrary1" } },
|
||||
{ "libanotherlibrary2", new HashSet<string>() { "libanotherlibrary2", "libanotherlibrary1" } },
|
||||
{ "libanotherlibrary3", new HashSet<string>() { "libanotherlibrary3", "libanotherlibrary1", "libanotherlibrary2" } },
|
||||
{ "libanotherlibrary4", new HashSet<string>() { "libanotherlibrary4", "libanotherlibrary1" } },
|
||||
};
|
||||
|
||||
ISet<string> componentNames = new HashSet<string>();
|
||||
foreach (var discoveredComponent in componentRecorder.GetDetectedComponents())
|
||||
{
|
||||
// Verify each package has the right information
|
||||
var packageName = (discoveredComponent.Component as ConanComponent).Name;
|
||||
|
||||
// Verify version
|
||||
(discoveredComponent.Component as ConanComponent).Version.Should().Be(packageVersions[packageName]);
|
||||
|
||||
var dependencyRoots = new HashSet<string>();
|
||||
|
||||
componentRecorder.AssertAllExplicitlyReferencedComponents(
|
||||
discoveredComponent.Component.Id,
|
||||
packageDependencyRoots[packageName].Select(expectedRoot =>
|
||||
new Func<ConanComponent, bool>(parentComponent => parentComponent.Name == expectedRoot)).ToArray());
|
||||
|
||||
componentNames.Add(packageName);
|
||||
}
|
||||
|
||||
// Verify all packages were detected
|
||||
foreach (var expectedPackage in packageVersions.Keys)
|
||||
{
|
||||
componentNames.Should().Contain(expectedPackage);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task TestConanDetector_SupportNoDependenciesAsync()
|
||||
{
|
||||
var (result, componentRecorder) = await this.DetectorTestUtility
|
||||
.WithFile("conan.lock", this.testConanLockNoDependenciesString)
|
||||
.ExecuteDetectorAsync();
|
||||
|
||||
result.ResultCode.Should().Be(ProcessingResultCode.Success);
|
||||
componentRecorder.GetDetectedComponents().Should().BeEmpty();
|
||||
}
|
||||
}
|
4
test/Microsoft.ComponentDetection.VerificationTests/resources/VerificationTest.ps1
Normal file → Executable file
4
test/Microsoft.ComponentDetection.VerificationTests/resources/VerificationTest.ps1
Normal file → Executable file
|
@ -38,14 +38,14 @@ function main()
|
|||
Set-Location ((Get-Item $repoPath).FullName + "\src\Microsoft.ComponentDetection")
|
||||
dotnet run scan --SourceDirectory $verificationTestRepo --Output $output `
|
||||
--DockerImagesToScan $dockerImagesToScan `
|
||||
--DetectorArgs DockerReference=EnableIfDefaultOff,SPDX22SBOM=EnableIfDefaultOff,CondaLock=EnableIfDefaultOff
|
||||
--DetectorArgs DockerReference=EnableIfDefaultOff,SPDX22SBOM=EnableIfDefaultOff,CondaLock=EnableIfDefaultOff,ConanLock=EnableIfDefaultOff
|
||||
|
||||
Set-Location $CDRelease
|
||||
dotnet restore
|
||||
Set-Location ($CDRelease + "\src\Microsoft.ComponentDetection")
|
||||
dotnet run scan --SourceDirectory $verificationTestRepo --Output $releaseOutput `
|
||||
--DockerImagesToScan $dockerImagesToScan `
|
||||
--DetectorArgs DockerReference=EnableIfDefaultOff,SPDX22SBOM=EnableIfDefaultOff,CondaLock=EnableIfDefaultOff
|
||||
--DetectorArgs DockerReference=EnableIfDefaultOff,SPDX22SBOM=EnableIfDefaultOff,CondaLock=EnableIfDefaultOff,ConanLock=EnableIfDefaultOff
|
||||
|
||||
$env:GITHUB_OLD_ARTIFACTS_DIR = $releaseOutput
|
||||
$env:GITHUB_NEW_ARTIFACTS_DIR = $output
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
{
|
||||
"graph_lock": {
|
||||
"nodes": {
|
||||
"0": {
|
||||
"ref": "MyAwesomeConanProject/1.2.5",
|
||||
"options": "",
|
||||
"requires": [
|
||||
"1"
|
||||
],
|
||||
"build_requires": [
|
||||
"6"
|
||||
],
|
||||
"path": "conanfile.py",
|
||||
"context": "host"
|
||||
},
|
||||
"1": {
|
||||
"ref": "boost/1.82.0",
|
||||
"options": "",
|
||||
"package_id": "dd7f5f958c7381cfd81e611a16062de0c827160a",
|
||||
"prev": "0",
|
||||
"modified": true,
|
||||
"requires": [
|
||||
"2",
|
||||
"3",
|
||||
"4"
|
||||
],
|
||||
"build_requires": [
|
||||
"5"
|
||||
],
|
||||
"context": "host"
|
||||
},
|
||||
"2": {
|
||||
"ref": "zlib/1.2.13",
|
||||
"options": "",
|
||||
"package_id": "240c2182163325b213ca6886a7614c8ed2bf1738",
|
||||
"prev": "0",
|
||||
"modified": true,
|
||||
"context": "host"
|
||||
},
|
||||
"3": {
|
||||
"ref": "bzip2/1.0.8",
|
||||
"options": "",
|
||||
"package_id": "238a93dc813ca1550968399f1f8925565feeff8e",
|
||||
"prev": "0",
|
||||
"modified": true,
|
||||
"context": "host"
|
||||
},
|
||||
"4": {
|
||||
"ref": "libbacktrace/cci.20210118",
|
||||
"options": "",
|
||||
"package_id": "240c2182163325b213ca6886a7614c8ed2bf1738",
|
||||
"prev": "0",
|
||||
"modified": true,
|
||||
"context": "host"
|
||||
},
|
||||
"5": {
|
||||
"ref": "b2/4.9.6",
|
||||
"options": "",
|
||||
"package_id": "a5ad5696abf650a25eea8f377806b3d5fe234e6e",
|
||||
"prev": "0",
|
||||
"modified": true,
|
||||
"context": "host"
|
||||
},
|
||||
"6": {
|
||||
"ref": "gtest/1.8.1",
|
||||
"options": "",
|
||||
"package_id": "fb16a498e820fb09d04ff9374a782b5b21da0601",
|
||||
"prev": "0",
|
||||
"modified": true,
|
||||
"context": "host"
|
||||
}
|
||||
},
|
||||
"revisions_enabled": false
|
||||
},
|
||||
"version": "0.4",
|
||||
"profile_host": "\n"
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
from conans import ConanFile
|
||||
|
||||
class MyAwesome(ConanFile):
|
||||
name = "MyAwesomeConanProject"
|
||||
version = "1.2.5"
|
||||
|
||||
def requirements(self):
|
||||
self.requires("boost/1.82.0")
|
||||
|
||||
def build_requirements(self):
|
||||
self.tool_requires("gtest/1.8.1")
|
|
@ -0,0 +1,60 @@
|
|||
{
|
||||
"graph_lock": {
|
||||
"nodes": {
|
||||
"0": {
|
||||
"options": "",
|
||||
"requires": [
|
||||
"1"
|
||||
],
|
||||
"build_requires": [
|
||||
"5"
|
||||
],
|
||||
"path": "conanfile.txt",
|
||||
"context": "host"
|
||||
},
|
||||
"1": {
|
||||
"ref": "boost/1.82.0",
|
||||
"options": "",
|
||||
"package_id": "dd7f5f958c7381cfd81e611a16062de0c827160a",
|
||||
"prev": "0",
|
||||
"requires": [
|
||||
"2",
|
||||
"3",
|
||||
"4"
|
||||
],
|
||||
"context": "host"
|
||||
},
|
||||
"2": {
|
||||
"ref": "zlib/1.2.13",
|
||||
"options": "",
|
||||
"package_id": "240c2182163325b213ca6886a7614c8ed2bf1738",
|
||||
"prev": "0",
|
||||
"context": "host"
|
||||
},
|
||||
"3": {
|
||||
"ref": "bzip2/1.0.8",
|
||||
"options": "",
|
||||
"package_id": "238a93dc813ca1550968399f1f8925565feeff8e",
|
||||
"prev": "0",
|
||||
"context": "host"
|
||||
},
|
||||
"4": {
|
||||
"ref": "libbacktrace/cci.20210118",
|
||||
"options": "",
|
||||
"package_id": "240c2182163325b213ca6886a7614c8ed2bf1738",
|
||||
"prev": "0",
|
||||
"context": "host"
|
||||
},
|
||||
"5": {
|
||||
"ref": "gtest/1.8.1",
|
||||
"options": "",
|
||||
"package_id": "fb16a498e820fb09d04ff9374a782b5b21da0601",
|
||||
"prev": "0",
|
||||
"context": "host"
|
||||
}
|
||||
},
|
||||
"revisions_enabled": false
|
||||
},
|
||||
"version": "0.4",
|
||||
"profile_host": "\n"
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
[requires]
|
||||
boost/1.82.0
|
||||
|
||||
[tool_requires]
|
||||
gtest/1.8.1
|
Загрузка…
Ссылка в новой задаче