From e947bae2682a4cd208bc76be236475ad8a8c55d5 Mon Sep 17 00:00:00 2001 From: Nikolche Kolev Date: Tue, 12 Sep 2023 12:18:28 -0700 Subject: [PATCH] Add deprecation info decorator, add dgml visualization tests (#29) --- src/Common/DependencyNodeIdentity.cs | 15 +-- src/Common/DeprecationInfoDecorator.cs | 106 ++++++++++++++++++ src/Common/IPackageDependencyNodeDecorator.cs | 2 +- .../DGMLDependencyVisualizerTool.cs | 43 +++++-- .../DependencyVisualizerTool.Test.csproj | 1 + .../PackageDependencyVisualizerToolTests.cs | 57 +++++++++- .../compiler/resources/diamonddependency.dgml | 5 +- ...cy.withvulnerabilitiesanddeprecations.dgml | 22 ++++ .../diamonddependencywithtoplevel.dgml | 5 +- .../resources/missingpackageversion.dgml | 5 +- .../compiler/resources/multipleversions.dgml | 5 +- .../compiler/resources/multitargeted.dgml | 5 +- .../compiler/resources/nuget.common.dgml | 5 +- .../resources/singlepackagereference.dgml | 5 +- .../resources/singleprojectreference.dgml | 5 +- .../resources/transitivepackagereference.dgml | 5 +- .../resources/transitiveprojectreference.dgml | 5 +- src/DependencyVisualizerTool/Program.cs | 30 +++-- 18 files changed, 280 insertions(+), 46 deletions(-) create mode 100644 src/Common/DeprecationInfoDecorator.cs create mode 100644 src/DependencyVisualizerTool/DependencyVisualizerTool.Test/compiler/resources/diamonddependency.withvulnerabilitiesanddeprecations.dgml diff --git a/src/Common/DependencyNodeIdentity.cs b/src/Common/DependencyNodeIdentity.cs index a124c67..a84176b 100644 --- a/src/Common/DependencyNodeIdentity.cs +++ b/src/Common/DependencyNodeIdentity.cs @@ -9,22 +9,11 @@ public class DependencyNodeIdentity : PackageIdentity public bool Vulnerable { get; set; } + public bool Deprecated { get; set; } + public DependencyNodeIdentity(string id, NuGetVersion version, DependencyType type) : base(id, version) { Type = type; } - - public override int GetHashCode() - { - return HashCode.Combine(base.GetHashCode(), Type, Vulnerable); - } - - public override bool Equals(object? obj) - { - return obj is DependencyNodeIdentity identity && - base.Equals(obj) && - Type == identity.Type && - Vulnerable == identity.Vulnerable; - } } } \ No newline at end of file diff --git a/src/Common/DeprecationInfoDecorator.cs b/src/Common/DeprecationInfoDecorator.cs new file mode 100644 index 0000000..6e1611f --- /dev/null +++ b/src/Common/DeprecationInfoDecorator.cs @@ -0,0 +1,106 @@ +using Logging; +using Microsoft.Extensions.Logging; +using NuGet.Packaging.Core; +using NuGet.Protocol.Core.Types; + +namespace Common +{ + public class DeprecationInfoDecorator : IPackageDependencyNodeDecorator + { + private readonly List _sourceRepositories; + private readonly SourceCacheContext _sourceCacheContext; + private readonly Dictionary PackageDeprecationData = new(); + private readonly List _packageMetadataResources = new(); + private bool _packageMetadataResourcesAcquired; + + public DeprecationInfoDecorator(List sourceRepositories, SourceCacheContext sourceCacheContext) + { + _sourceRepositories = sourceRepositories ?? throw new ArgumentNullException(nameof(sourceRepositories)); + _sourceCacheContext = sourceCacheContext ?? throw new ArgumentNullException(nameof(sourceCacheContext)); + } + + public async Task DecorateAsync(PackageDependencyNode dependencyNode, CancellationToken cancellationToken) + { + if (PackageDeprecationData.TryGetValue(dependencyNode.Identity, out var isPackageDeprecated)) + { + dependencyNode.Identity.Deprecated = isPackageDeprecated; + return; + } + + if (_packageMetadataResourcesAcquired) + { + await InitializeMetadataResource(cancellationToken); + _packageMetadataResourcesAcquired = true; + } + + bool isDeprecated = await IsPackageDeprecatedAsync(dependencyNode.Identity, cancellationToken); + PackageDeprecationData.Add(dependencyNode.Identity, isDeprecated); + dependencyNode.Identity.Deprecated = isDeprecated; + } + + private async Task IsPackageDeprecatedAsync(PackageIdentity packageIdentity, CancellationToken cancellationToken) + { + List>? results = new(_packageMetadataResources.Count); + + bool isDeprecated = false; + foreach (PackageMetadataResource packageMetadataResource in _packageMetadataResources) + { + var packageMetadata = packageMetadataResource.GetMetadataAsync(packageIdentity, _sourceCacheContext, NuGet.Common.NullLogger.Instance, cancellationToken); + if (packageMetadata != null) + { + results.Add(packageMetadata); + } + } + await Task.WhenAll(results); + if (cancellationToken.IsCancellationRequested) + { + cancellationToken.ThrowIfCancellationRequested(); + } + foreach (var result in results) + { + if (result?.Result != null) + { + isDeprecated = isDeprecated || await result.Result.GetDeprecationMetadataAsync() != null; + } + } + + return isDeprecated; + } + + private async Task InitializeMetadataResource(CancellationToken cancellationToken) + { + List>? results = new(_sourceRepositories.Count); + + foreach (SourceRepository source in _sourceRepositories) + { + var metadataResource = GetMetadataResourceAsync(source, _sourceCacheContext, cancellationToken); + if (metadataResource != null) + { + results.Add(metadataResource); + } + } + + await Task.WhenAll(results); + + if (cancellationToken.IsCancellationRequested) + { + cancellationToken.ThrowIfCancellationRequested(); + } + foreach (var result in results) + { + if (result?.Result != null) + { + _packageMetadataResources.Add(result.Result); + } + } + } + + static async Task GetMetadataResourceAsync(SourceRepository source, SourceCacheContext cacheContext, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + PackageMetadataResource metadataResource = + await source.GetResourceAsync(cancellationToken); + return metadataResource; + } + } +} \ No newline at end of file diff --git a/src/Common/IPackageDependencyNodeDecorator.cs b/src/Common/IPackageDependencyNodeDecorator.cs index ec1217c..5dbbd90 100644 --- a/src/Common/IPackageDependencyNodeDecorator.cs +++ b/src/Common/IPackageDependencyNodeDecorator.cs @@ -4,4 +4,4 @@ public interface IPackageDependencyNodeDecorator { Task DecorateAsync(PackageDependencyNode dependencyNode, CancellationToken cancellationToken); } -} \ No newline at end of file +} diff --git a/src/DependencyVisualizerTool/DGMLDependencyVisualizerTool.cs b/src/DependencyVisualizerTool/DGMLDependencyVisualizerTool.cs index 0b6cee8..df8a683 100644 --- a/src/DependencyVisualizerTool/DGMLDependencyVisualizerTool.cs +++ b/src/DependencyVisualizerTool/DGMLDependencyVisualizerTool.cs @@ -28,7 +28,8 @@ public static XDocument TransGraphToDGMLXDocument(PackageDependencyGraph graph, id: firstNode.Identity.ToString(), label: firstNode.Identity.ToString(), type: firstNode.Identity.Type, - isVulnerable: firstNode.Identity.Vulnerable); + isVulnerable: firstNode.Identity.Vulnerable, + isDeprecated: firstNode.Identity.Deprecated); nodes.Add(firstNode.Identity.ToString(), firstNodeDGML); while (queue.Count > 0) @@ -49,7 +50,8 @@ public static XDocument TransGraphToDGMLXDocument(PackageDependencyGraph graph, id: child.Item1.Identity.ToString(), label: child.Item1.Identity.ToString(), type: child.Item1.Identity.Type, - child.Item1.Identity.Vulnerable); + child.Item1.Identity.Vulnerable, + child.Item1.Identity.Deprecated); nodes.Add(child.Item1.Identity.ToString(), currentDGML); } } @@ -88,8 +90,18 @@ from item in links new XAttribute("StrokeThickness", "1")), new XElement(XName.Get("Category", DGMLxmlns), new XAttribute("Id", "VulnerablePackage"), - new XAttribute("Background", "Red"), - new XAttribute("StrokeThickness", "1")))) + new XAttribute("Background", "None"), + new XAttribute("StrokeThickness", "4"), + new XAttribute("Stroke", "Red")), + new XElement(XName.Get("Category", DGMLxmlns), + new XAttribute("Id", "DeprecatedPackage"), + new XAttribute("Background", "Yellow"), + new XAttribute("StrokeThickness", "1")), + new XElement(XName.Get("Category", DGMLxmlns), + new XAttribute("Id", "VulnerableAndDeprecatedPackage"), + new XAttribute("Background", "Yellow"), + new XAttribute("StrokeThickness", "4"), + new XAttribute("Stroke", "Red")))) ); return document; } @@ -102,16 +114,31 @@ private class DGMLNode : IEquatable public string Category { get; set; } - public DGMLNode(string id, string label, DependencyType type, bool isVulnerable) + public DGMLNode(string id, string label, DependencyType type, bool isVulnerable, bool isDeprecated) { this.Id = id; this.Label = label; - this.Category = isVulnerable ? "VulnerablePackage" : type.ToString(); + this.Category = GetCategory(type, isVulnerable, isDeprecated); } - public bool Equals(DGMLNode other) + private static string GetCategory(DependencyType type, bool isVulnerable, bool isDeprecated) { - return Id.Equals(other.Id, StringComparison.OrdinalIgnoreCase); + return isVulnerable && isDeprecated ? + "VulnerableAndDeprecatedPackage" : + isVulnerable ? + "VulnerablePackage" : + isDeprecated ? + "DeprecatedPackage" : + type.ToString(); + } + + public bool Equals(DGMLNode? other) + { + if (other != null) + { + return Id.Equals(other.Id, StringComparison.OrdinalIgnoreCase); + } + return false; } } diff --git a/src/DependencyVisualizerTool/DependencyVisualizerTool.Test/DependencyVisualizerTool.Test.csproj b/src/DependencyVisualizerTool/DependencyVisualizerTool.Test/DependencyVisualizerTool.Test.csproj index 09495b5..26ce788 100644 --- a/src/DependencyVisualizerTool/DependencyVisualizerTool.Test/DependencyVisualizerTool.Test.csproj +++ b/src/DependencyVisualizerTool/DependencyVisualizerTool.Test/DependencyVisualizerTool.Test.csproj @@ -14,6 +14,7 @@ + diff --git a/src/DependencyVisualizerTool/DependencyVisualizerTool.Test/PackageDependencyVisualizerToolTests.cs b/src/DependencyVisualizerTool/DependencyVisualizerTool.Test/PackageDependencyVisualizerToolTests.cs index e60ed1f..84e63af 100644 --- a/src/DependencyVisualizerTool/DependencyVisualizerTool.Test/PackageDependencyVisualizerToolTests.cs +++ b/src/DependencyVisualizerTool/DependencyVisualizerTool.Test/PackageDependencyVisualizerToolTests.cs @@ -1,7 +1,11 @@ +using System.Reflection.Emit; using System.Text.RegularExpressions; using Common; using FluentAssertions; +using FluentAssertions.Equivalency; +using NuGet.Packaging.Core; using NuGet.ProjectModel; +using NuGet.Versioning; using SharedUtility; namespace DependencyVisualizerTool.Test @@ -143,22 +147,67 @@ public async Task TransGraphToDGMLXDocument_transitiveprojectreference_CreateDGM actualDGML.Should().Be(expectedDGML, because: actualDGML); } - private static string RemoveWhitespace(string s) + [Fact] + public async Task TransGraphToDGMLXDocument_WithVulnerableAndDeprecatedPackages_CreateDGMLCorrectly() { - return Regex.Replace(s, @"\s+", string.Empty); + var packageA = new PackageIdentity("A", new NuGetVersion(1, 0, 0)); + var packageB = new PackageIdentity("B", new NuGetVersion(1, 0, 0)); + var packageC = new PackageIdentity("C", new NuGetVersion(1, 1, 0)); + var decorator = new VulnerabilityAndDeprecationDecorator( + vulnerablePackages: new HashSet { packageA, packageC }, + deprecatedPackages: new HashSet { packageB, packageC }); + + var graph = await GetOnlyDependencyGraphAsync("DependencyVisualizerTool.Test.compiler.resources.diamonddependency.assets.json", new() { decorator }); + + string actualDGML = RemoveWhitespace(DGMLDependencyVisualizerTool.TransGraphToDGMLXDocument(graph).ToString()); + + string expectedDGML = RemoveWhitespace(TestHelpers.GetResource("DependencyVisualizerTool.Test.compiler.resources.diamonddependency.withvulnerabilitiesanddeprecations.dgml", GetType())); + + actualDGML.Should().Be(expectedDGML, because: actualDGML); } - private async Task GetOnlyDependencyGraphAsync(string resourceName) + private async Task GetOnlyDependencyGraphAsync(string resourceName, List decorators = null) { var assetsFileText = TestHelpers.GetResource(resourceName, GetType()); var assetsFile = new LockFileFormat().Parse(assetsFileText, Path.GetTempPath()); var dependencyGraphSpec = new DependencyGraphSpec(); dependencyGraphSpec.AddProject(assetsFile.PackageSpec); - Dictionary graphs = await PackageDependencyGraph.GenerateAllDependencyGraphsFromAssetsFileAsync(assetsFile, dependencyGraphSpec, projectsOnly: false, new(), CancellationToken.None); + Dictionary graphs = await PackageDependencyGraph.GenerateAllDependencyGraphsFromAssetsFileAsync(assetsFile, dependencyGraphSpec, projectsOnly: false, decorators ?? new(), CancellationToken.None); graphs.Should().HaveCount(1); var graph = graphs.Single().Value; return graph; } + + private static string RemoveWhitespace(string s) + { + return Regex.Replace(s, @"\s+", string.Empty); + } + + private class VulnerabilityAndDeprecationDecorator : IPackageDependencyNodeDecorator + { + private readonly HashSet _vulnerablePackages; + private readonly HashSet _deprecatedPackages; + + public VulnerabilityAndDeprecationDecorator(HashSet vulnerablePackages, HashSet deprecatedPackages) + { + _vulnerablePackages = vulnerablePackages ?? throw new ArgumentNullException(nameof(vulnerablePackages)); + _deprecatedPackages = deprecatedPackages ?? throw new ArgumentNullException(nameof(deprecatedPackages)); + } + + public Task DecorateAsync(PackageDependencyNode dependencyNode, CancellationToken cancellationToken) + { + PackageIdentity identity = dependencyNode.Identity; + if (_vulnerablePackages.Contains(identity)) + { + dependencyNode.Identity.Vulnerable = true; + } + if (_deprecatedPackages.Contains(identity)) + { + dependencyNode.Identity.Deprecated = true; + } + return Task.CompletedTask; + } + } } } \ No newline at end of file diff --git a/src/DependencyVisualizerTool/DependencyVisualizerTool.Test/compiler/resources/diamonddependency.dgml b/src/DependencyVisualizerTool/DependencyVisualizerTool.Test/compiler/resources/diamonddependency.dgml index 0bda60a..0c43a7c 100644 --- a/src/DependencyVisualizerTool/DependencyVisualizerTool.Test/compiler/resources/diamonddependency.dgml +++ b/src/DependencyVisualizerTool/DependencyVisualizerTool.Test/compiler/resources/diamonddependency.dgml @@ -14,6 +14,9 @@ - + + + + \ No newline at end of file diff --git a/src/DependencyVisualizerTool/DependencyVisualizerTool.Test/compiler/resources/diamonddependency.withvulnerabilitiesanddeprecations.dgml b/src/DependencyVisualizerTool/DependencyVisualizerTool.Test/compiler/resources/diamonddependency.withvulnerabilitiesanddeprecations.dgml new file mode 100644 index 0000000..793cabd --- /dev/null +++ b/src/DependencyVisualizerTool/DependencyVisualizerTool.Test/compiler/resources/diamonddependency.withvulnerabilitiesanddeprecations.dgml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/DependencyVisualizerTool/DependencyVisualizerTool.Test/compiler/resources/diamonddependencywithtoplevel.dgml b/src/DependencyVisualizerTool/DependencyVisualizerTool.Test/compiler/resources/diamonddependencywithtoplevel.dgml index ac003be..5dd2d79 100644 --- a/src/DependencyVisualizerTool/DependencyVisualizerTool.Test/compiler/resources/diamonddependencywithtoplevel.dgml +++ b/src/DependencyVisualizerTool/DependencyVisualizerTool.Test/compiler/resources/diamonddependencywithtoplevel.dgml @@ -12,6 +12,9 @@ - + + + + \ No newline at end of file diff --git a/src/DependencyVisualizerTool/DependencyVisualizerTool.Test/compiler/resources/missingpackageversion.dgml b/src/DependencyVisualizerTool/DependencyVisualizerTool.Test/compiler/resources/missingpackageversion.dgml index 38879ee..b750b26 100644 --- a/src/DependencyVisualizerTool/DependencyVisualizerTool.Test/compiler/resources/missingpackageversion.dgml +++ b/src/DependencyVisualizerTool/DependencyVisualizerTool.Test/compiler/resources/missingpackageversion.dgml @@ -11,6 +11,9 @@ - + + + + \ No newline at end of file diff --git a/src/DependencyVisualizerTool/DependencyVisualizerTool.Test/compiler/resources/multipleversions.dgml b/src/DependencyVisualizerTool/DependencyVisualizerTool.Test/compiler/resources/multipleversions.dgml index ba5a6ee..c1a003e 100644 --- a/src/DependencyVisualizerTool/DependencyVisualizerTool.Test/compiler/resources/multipleversions.dgml +++ b/src/DependencyVisualizerTool/DependencyVisualizerTool.Test/compiler/resources/multipleversions.dgml @@ -15,6 +15,9 @@ - + + + + \ No newline at end of file diff --git a/src/DependencyVisualizerTool/DependencyVisualizerTool.Test/compiler/resources/multitargeted.dgml b/src/DependencyVisualizerTool/DependencyVisualizerTool.Test/compiler/resources/multitargeted.dgml index 3dd65aa..162b3a0 100644 --- a/src/DependencyVisualizerTool/DependencyVisualizerTool.Test/compiler/resources/multitargeted.dgml +++ b/src/DependencyVisualizerTool/DependencyVisualizerTool.Test/compiler/resources/multitargeted.dgml @@ -9,6 +9,9 @@ - + + + + \ No newline at end of file diff --git a/src/DependencyVisualizerTool/DependencyVisualizerTool.Test/compiler/resources/nuget.common.dgml b/src/DependencyVisualizerTool/DependencyVisualizerTool.Test/compiler/resources/nuget.common.dgml index 5543fe8..ead90a1 100644 --- a/src/DependencyVisualizerTool/DependencyVisualizerTool.Test/compiler/resources/nuget.common.dgml +++ b/src/DependencyVisualizerTool/DependencyVisualizerTool.Test/compiler/resources/nuget.common.dgml @@ -29,6 +29,9 @@ - + + + + \ No newline at end of file diff --git a/src/DependencyVisualizerTool/DependencyVisualizerTool.Test/compiler/resources/singlepackagereference.dgml b/src/DependencyVisualizerTool/DependencyVisualizerTool.Test/compiler/resources/singlepackagereference.dgml index 82458bf..32a112c 100644 --- a/src/DependencyVisualizerTool/DependencyVisualizerTool.Test/compiler/resources/singlepackagereference.dgml +++ b/src/DependencyVisualizerTool/DependencyVisualizerTool.Test/compiler/resources/singlepackagereference.dgml @@ -9,6 +9,9 @@ - + + + + \ No newline at end of file diff --git a/src/DependencyVisualizerTool/DependencyVisualizerTool.Test/compiler/resources/singleprojectreference.dgml b/src/DependencyVisualizerTool/DependencyVisualizerTool.Test/compiler/resources/singleprojectreference.dgml index eb572a9..263dbc2 100644 --- a/src/DependencyVisualizerTool/DependencyVisualizerTool.Test/compiler/resources/singleprojectreference.dgml +++ b/src/DependencyVisualizerTool/DependencyVisualizerTool.Test/compiler/resources/singleprojectreference.dgml @@ -9,6 +9,9 @@ - + + + + \ No newline at end of file diff --git a/src/DependencyVisualizerTool/DependencyVisualizerTool.Test/compiler/resources/transitivepackagereference.dgml b/src/DependencyVisualizerTool/DependencyVisualizerTool.Test/compiler/resources/transitivepackagereference.dgml index 5cad070..84d1f82 100644 --- a/src/DependencyVisualizerTool/DependencyVisualizerTool.Test/compiler/resources/transitivepackagereference.dgml +++ b/src/DependencyVisualizerTool/DependencyVisualizerTool.Test/compiler/resources/transitivepackagereference.dgml @@ -11,6 +11,9 @@ - + + + + \ No newline at end of file diff --git a/src/DependencyVisualizerTool/DependencyVisualizerTool.Test/compiler/resources/transitiveprojectreference.dgml b/src/DependencyVisualizerTool/DependencyVisualizerTool.Test/compiler/resources/transitiveprojectreference.dgml index eb75f09..17b329b 100644 --- a/src/DependencyVisualizerTool/DependencyVisualizerTool.Test/compiler/resources/transitiveprojectreference.dgml +++ b/src/DependencyVisualizerTool/DependencyVisualizerTool.Test/compiler/resources/transitiveprojectreference.dgml @@ -11,6 +11,9 @@ - + + + + \ No newline at end of file diff --git a/src/DependencyVisualizerTool/Program.cs b/src/DependencyVisualizerTool/Program.cs index 7c5c356..14e6018 100644 --- a/src/DependencyVisualizerTool/Program.cs +++ b/src/DependencyVisualizerTool/Program.cs @@ -40,7 +40,11 @@ public static int Main(string[] args) var checkVulnerabilityOption = new Option( name: "--visualize-vulnerabilities", - description: "Whether to visualize the vulnerabilities for your package graph"); + description: "Whether to visualize the vulnerable packages in your package graph"); + + var checkDeprecationOption = new Option( + name: "--visualize-deprecations", + description: "Whether to visualize the deprecated packages in your package graph"); var projectsOnlyOption = new Option( name: "--projects-only", @@ -50,21 +54,22 @@ public static int Main(string[] args) rootCommand.AddArgument(fileArgument); rootCommand.AddOption(outputOption); rootCommand.AddOption(checkVulnerabilityOption); + rootCommand.AddOption(checkDeprecationOption); rootCommand.AddOption(projectsOnlyOption); - rootCommand.SetHandler(async (fileArgument, outputOption, checkVulnerabilityOption, projectsOnly) => + rootCommand.SetHandler(async (fileArgument, outputOption, checkVulnerabilityOption, checkDeprecationOption, projectsOnly) => { #if DEBUG System.Diagnostics.Debugger.Launch(); #endif - await GenerateGraph(fileArgument, outputOption, checkVulnerabilityOption, projectsOnly, CancellationTokenSource.Token); + await GenerateGraph(fileArgument, outputOption, checkVulnerabilityOption, checkDeprecationOption, projectsOnly, CancellationTokenSource.Token); }, - fileArgument, outputOption, checkVulnerabilityOption, projectsOnlyOption); + fileArgument, outputOption, checkVulnerabilityOption, checkDeprecationOption, projectsOnlyOption); return rootCommand.InvokeAsync(args).Result; } - private static async Task GenerateGraph(FileInfo projectFile, string? outputFolder, bool? checkVulnerabilities, bool? projectsOnly, CancellationToken cancellationToken) + private static async Task GenerateGraph(FileInfo projectFile, string? outputFolder, bool? checkVulnerabilities, bool? checkDeprecation, bool? projectsOnly, CancellationToken cancellationToken) { MSBuildLocator.RegisterDefaults(); string projectExtensionsPath = GetMSBuildProjectExtensionsPath(projectFile.FullName); @@ -85,7 +90,7 @@ private static async Task GenerateGraph(FileInfo projectFile, string? outpu return 1; } - var decorators = CreateDecorators(assetFile.PackageSpec, checkVulnerabilities == true); + var decorators = CreateDecorators(assetFile.PackageSpec, checkVulnerabilities == true, checkDeprecation == true); Dictionary dictGraph = await PackageDependencyGraph.GenerateAllDependencyGraphsFromAssetsFileAsync( assetFile, @@ -118,14 +123,19 @@ private static async Task GenerateGraph(FileInfo projectFile, string? outpu return 0; } - private static List CreateDecorators(PackageSpec packageSpec, bool visualizeVulnerabilities) + private static List CreateDecorators(PackageSpec packageSpec, bool visualizeVulnerabilities, bool visualizeDeprecation) { List decorators = new(); + List? repositories = visualizeVulnerabilities || visualizeDeprecation ? GetHTTPSourceRepositories(packageSpec) : null; if (visualizeVulnerabilities) { - var repositories = GetHTTPSourceRepositories(packageSpec); - decorators.Add(new VulnerabilityInfoDecorator(repositories, new SourceCacheContext())); + decorators.Add(new VulnerabilityInfoDecorator(repositories!, new SourceCacheContext())); + } + + if (visualizeDeprecation) + { + decorators.Add(new DeprecationInfoDecorator(repositories!, new SourceCacheContext())); } return decorators; @@ -222,7 +232,7 @@ private static void CreateOutputIfNotExists(string outputFolder) } } - protected static void myHandler(object sender, ConsoleCancelEventArgs args) + protected static void myHandler(object? sender, ConsoleCancelEventArgs args) { args.Cancel = true; CancellationTokenSource.Cancel();