diff --git a/README.md b/README.md index 9fbe7e6..b0cf47a 100644 --- a/README.md +++ b/README.md @@ -51,12 +51,18 @@ Shared package references, e.g. in the `Directory.Build.props` file, are handled CentralPackageManagement (`PackageVersion` entries) are supported as well. +A version can be pinned by adding the `IsPinned` property to `PackageReference` or `PackageVersion` entries, to stop NuGetMonitor +from offering to update this version, if e.g. updating the package might break some functionality. +This is an alternate approach to using the package range notation `[13.0.1]`, that avoids side effects on dependent projects, e.g. when creating a NuGet package. +```xml + +``` A justification property can be added to `PackageReference` or `PackageVersion` entries, to e.g. document why a reference is pinned and can't be updated ```xml ``` - -A mitigation element can be added to suppress warnings for transitive dependencies that can't be updated due to project limitations but have been evaluated to not affect the product security. +A mitigation element can be added to suppress warnings for transitive dependencies that can't be updated due to project limitations +but have been evaluated to not affect the product security. ```xml ``` diff --git a/src/NuGetMonitor.Model/ExtensionMethods.cs b/src/NuGetMonitor.Model/ExtensionMethods.cs index 3b4a2d7..951d159 100644 --- a/src/NuGetMonitor.Model/ExtensionMethods.cs +++ b/src/NuGetMonitor.Model/ExtensionMethods.cs @@ -1,4 +1,6 @@ -namespace NuGetMonitor.Model; +using Microsoft.Build.Evaluation; + +namespace NuGetMonitor.Model; public static class ExtensionMethods { @@ -22,4 +24,16 @@ public static class ExtensionMethods } } } + + public static bool GetIsPinned(this ProjectItem projectItem) + { + var metadataValue = projectItem.GetMetadataValue("IsPinned"); + + return bool.TryParse(metadataValue, out var value) && value; + } + + public static string GetJustification(this ProjectItem projectItem) + { + return projectItem.GetMetadataValue("Justification"); + } } \ No newline at end of file diff --git a/src/NuGetMonitor.Model/Models/PackageReference.cs b/src/NuGetMonitor.Model/Models/PackageReference.cs index ebbb62c..5add85c 100644 --- a/src/NuGetMonitor.Model/Models/PackageReference.cs +++ b/src/NuGetMonitor.Model/Models/PackageReference.cs @@ -5,7 +5,7 @@ namespace NuGetMonitor.Model.Models; [DebuggerDisplay("{Id}, {VersionRange}")] -public sealed record PackageReference(string Id, VersionRange VersionRange) +public sealed record PackageReference(string Id, VersionRange VersionRange, bool IsPinned) { public PackageIdentity? FindBestMatch(IEnumerable? versions) { diff --git a/src/NuGetMonitor.Model/Models/PackageReferenceEntry.cs b/src/NuGetMonitor.Model/Models/PackageReferenceEntry.cs index 8381d97..b5b5cee 100644 --- a/src/NuGetMonitor.Model/Models/PackageReferenceEntry.cs +++ b/src/NuGetMonitor.Model/Models/PackageReferenceEntry.cs @@ -15,14 +15,15 @@ public enum VersionKind [DebuggerDisplay("{Identity}, {ProjectItemInTargetFramework}")] public sealed record PackageReferenceEntry { - public PackageReferenceEntry(string id, VersionRange versionRange, VersionKind versionKind, ProjectItem versionSource, ProjectItemInTargetFramework projectItemInTargetFramework, string justification, bool isPrivateAsset) + public PackageReferenceEntry(string id, VersionRange versionRange, VersionKind versionKind, ProjectItem versionSource, ProjectItemInTargetFramework projectItemInTargetFramework, bool isPrivateAsset) { VersionKind = versionKind; VersionSource = versionSource; ProjectItemInTargetFramework = projectItemInTargetFramework; - Justification = justification; + Justification = versionSource.GetJustification(); IsPrivateAsset = isPrivateAsset; - Identity = new(id, versionRange); + IsPinned = versionSource.GetIsPinned(); + Identity = new(id, versionRange, IsPinned); } public PackageReference Identity { get; } @@ -36,4 +37,6 @@ public PackageReferenceEntry(string id, VersionRange versionRange, VersionKind v public string Justification { get; } public bool IsPrivateAsset { get; } + + public bool IsPinned { get; } } \ No newline at end of file diff --git a/src/NuGetMonitor.Model/Models/ProjectInTargetFramework.cs b/src/NuGetMonitor.Model/Models/ProjectInTargetFramework.cs index e15bd10..75863fe 100644 --- a/src/NuGetMonitor.Model/Models/ProjectInTargetFramework.cs +++ b/src/NuGetMonitor.Model/Models/ProjectInTargetFramework.cs @@ -73,7 +73,7 @@ private static ReadOnlyDictionary GetPackageMitigations .Select(item => new { Identity = new PackageIdentity(item.EvaluatedInclude, NuGetVersion.Parse(item.GetMetadataValue("Version"))), - Justification = item.GetMetadataValue("Justification") + Justification = item.GetJustification() } ) .ToDictionary(item => item.Identity, item => item.Justification); diff --git a/src/NuGetMonitor.Model/Services/ProjectService.cs b/src/NuGetMonitor.Model/Services/ProjectService.cs index c8739e1..ac684ab 100644 --- a/src/NuGetMonitor.Model/Services/ProjectService.cs +++ b/src/NuGetMonitor.Model/Services/ProjectService.cs @@ -217,9 +217,10 @@ private static IEnumerable GetPackageReferenceItem } } - return version is null - ? null - : new PackageReferenceEntry(id, version, versionKind, versionSource, projectItemInTargetFramework, versionSource.GetMetadataValue("Justification"), projectItem.GetIsPrivateAsset()); + if (version is null) + return null; + + return new(id, version, versionKind, versionSource, projectItemInTargetFramework, projectItem.GetIsPrivateAsset()); } internal static bool IsTrue(this ProjectProperty? property) diff --git a/src/NuGetMonitor/Services/InfoBarService.cs b/src/NuGetMonitor/Services/InfoBarService.cs index a069d79..6106f39 100644 --- a/src/NuGetMonitor/Services/InfoBarService.cs +++ b/src/NuGetMonitor/Services/InfoBarService.cs @@ -1,7 +1,4 @@ -using System.IO; -using System.Text; -using System.Windows; -using Community.VisualStudio.Toolkit; +using Community.VisualStudio.Toolkit; using Microsoft.VisualStudio.Imaging; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; diff --git a/src/NuGetMonitor/View/Monitor/NuGetMonitorControl.xaml b/src/NuGetMonitor/View/Monitor/NuGetMonitorControl.xaml index bfd4b3b..13dd60f 100644 --- a/src/NuGetMonitor/View/Monitor/NuGetMonitorControl.xaml +++ b/src/NuGetMonitor/View/Monitor/NuGetMonitorControl.xaml @@ -184,6 +184,11 @@ + item.Items) .Select(item => item.ProjectItemInTargetFramework) .Where(item => item.Project.IsTransitivePinningEnabled) - .SelectMany(project => project.Project.CentralVersionMap.Values.Select(item => new PackageReferenceEntry(item.EvaluatedInclude, item.GetVersion() ?? VersionRange.None, VersionKind.CentralDefinition, item, project, item.GetMetadataValue("Justification"), false))) + .SelectMany(project => project.Project.CentralVersionMap.Values.Select(item => new PackageReferenceEntry(item.EvaluatedInclude, item.GetVersion() ?? VersionRange.None, VersionKind.CentralDefinition, item, project, false))) .Where(item => !packageIds.Contains(item.Identity.Id)) .GroupBy(item => item.Identity) - .Select(item => new PackageViewModel(this, item, PackageItemType.PackageVersion, _solutionService)) + .Select(group => new PackageViewModel(this, group, PackageItemType.PackageVersion, _solutionService)) .ToArray(); Packages = packages.Concat(transitivePins).ToArray(); diff --git a/src/NuGetMonitor/ViewModels/PackageViewModel.cs b/src/NuGetMonitor/ViewModels/PackageViewModel.cs index d0b2207..641c193 100644 --- a/src/NuGetMonitor/ViewModels/PackageViewModel.cs +++ b/src/NuGetMonitor/ViewModels/PackageViewModel.cs @@ -24,6 +24,7 @@ public PackageViewModel(NuGetMonitorViewModel parent, IGrouping (itemType == PackageItemType.PackageVersion ? item.VersionSource : item.ProjectItemInTargetFramework.ProjectItem).GetContainingProject()).Select(item => new ProjectViewModel(item.Key, solutionService)).ToArray(); ActiveVersion = NuGetVersion.TryParse(PackageReference.VersionRange.OriginalString, out var simpleVersion) ? simpleVersion : PackageReference.VersionRange; Justifications = string.Join(", ", Items.Select(reference => reference.Justification).Distinct()); + IsPinned = items.Key.IsPinned; } public IGrouping Items { get; } @@ -53,6 +54,8 @@ public PackageViewModel(NuGetMonitorViewModel parent, IGrouping version != SelectedVersion, VersionRange versionRange => versionRange.FindBestMatch(Package?.Versions) != SelectedVersion,