Skip to content

Commit

Permalink
Merge pull request #26 from tom-englert/feature/improvements
Browse files Browse the repository at this point in the history
Fix #25: Central Package Management support
  • Loading branch information
sboulema authored Nov 5, 2023
2 parents a07fe53 + 634c1e5 commit 96c7d55
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 30 deletions.
8 changes: 6 additions & 2 deletions src/Models/PackageReferenceEntry.cs
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
using NuGet.Versioning;
using Microsoft.Build.Evaluation;
using NuGet.Versioning;
using NuGetMonitor.Services;

namespace NuGetMonitor.Models;

internal sealed record PackageReferenceEntry
{
public PackageReferenceEntry(string id, VersionRange versionRange, ProjectItemInTargetFramework projectItemInTargetFramework, string justification)
public PackageReferenceEntry(string id, VersionRange versionRange, ProjectItem versionSource, ProjectItemInTargetFramework projectItemInTargetFramework, string justification)
{
VersionSource = versionSource;
ProjectItemInTargetFramework = projectItemInTargetFramework;
Justification = justification;
Identity = new PackageReference(id, versionRange);
}

public PackageReference Identity { get; }

public ProjectItem VersionSource { get; }

public ProjectItemInTargetFramework ProjectItemInTargetFramework { get; }

public string Justification { get; }
Expand Down
111 changes: 89 additions & 22 deletions src/Services/ProjectService.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.IO;
using System.Collections.ObjectModel;
using System.IO;
using Community.VisualStudio.Toolkit;
using Microsoft.Build.Construction;
using Microsoft.Build.Evaluation;
Expand All @@ -7,15 +8,58 @@
using NuGetMonitor.Models;
using TomsToolbox.Essentials;
using Project = Microsoft.Build.Evaluation.Project;
using ProjectItem = Microsoft.Build.Evaluation.ProjectItem;

namespace NuGetMonitor.Services;

internal sealed record ProjectItemInTargetFramework(ProjectItem ProjectItem, NuGetFramework TargetFramework)
internal sealed class ProjectItemInTargetFramework
{
public ProjectRootElement ContainingProject => ProjectItem.Xml.ContainingProject;
public ProjectItemInTargetFramework(ProjectItem projectItem, ProjectInTargetFramework project)
{
ProjectItem = projectItem;
Project = project;
}

public ProjectItem ProjectItem { get; init; }

public NuGetFramework TargetFramework => Project.TargetFramework;

public ProjectInTargetFramework Project { get; }
}

internal sealed record ProjectInTargetFramework(Project Project, NuGetFramework TargetFramework);
internal sealed class ProjectInTargetFramework
{
private static readonly ReadOnlyDictionary<string, ProjectItem> _emptyVersionMap = new(new Dictionary<string, ProjectItem>());
private static readonly DelegateEqualityComparer<ProjectItem> _itemIncludeComparer = new(item => item?.EvaluatedInclude.ToUpperInvariant());

public ProjectInTargetFramework(Project project, NuGetFramework targetFramework)
{
Project = project;
TargetFramework = targetFramework;
CentralVersionMap = GetCentralVersionMap(project);
}

public Project Project { get; init; }

public NuGetFramework TargetFramework { get; init; }

public ReadOnlyDictionary<string, ProjectItem> CentralVersionMap { get; }

private static ReadOnlyDictionary<string, ProjectItem> GetCentralVersionMap(Project project)
{
var useCentralPackageManagement = project.GetProperty("ManagePackageVersionsCentrally").IsTrue();

if (!useCentralPackageManagement)
return _emptyVersionMap;

var versionMap = project
.GetItems("PackageVersion")
.Distinct(_itemIncludeComparer)
.ToDictionary(item => item.EvaluatedInclude, item => item);

return new ReadOnlyDictionary<string, ProjectItem>(versionMap);
}
}

internal static class ProjectService
{
Expand Down Expand Up @@ -48,7 +92,7 @@ public static async Task<IReadOnlyCollection<PackageReferenceEntry>> GetPackageR
return references
.SelectMany(items => items)
.OrderBy(item => item.Identity.Id)
.ThenBy(item => Path.GetFileName(item.ProjectItemInTargetFramework.ContainingProject.FullPath))
.ThenBy(item => Path.GetFileName(item.VersionSource.GetContainingProject().FullPath))
.ToArray();
});
}
Expand All @@ -74,12 +118,9 @@ public static ProjectInTargetFramework[] GetProjectsInTargetFramework(this Proje

var projectCollection = _projectCollection;

lock (projectCollection)
{
return frameworks
.Select(framework => LoadProjectInTargetFramework(project, framework, projectCollection))
.ToArray();
}
return frameworks
.Select(framework => LoadProjectInTargetFramework(project, framework, projectCollection))
.ToArray();
}

private static ProjectInTargetFramework LoadProjectInTargetFramework(Project project, NuGetFramework framework, ProjectCollection projectCollection)
Expand Down Expand Up @@ -109,16 +150,11 @@ private static IEnumerable<ProjectItemInTargetFramework> GetPackageReferenceItem
{
try
{
Project project;
var project = projectCollection.LoadProject(projectPath);

lock (projectCollection)
{
project = projectCollection.LoadProject(projectPath);
}
var frameworkSpecificProjects = project.GetProjectsInTargetFramework();

var projects = project.GetProjectsInTargetFramework();

var allItems = projects.SelectMany(p => p.Project.GetItems("PackageReference").Select(item => new ProjectItemInTargetFramework(item, p.TargetFramework)));
var allItems = frameworkSpecificProjects.SelectMany(GetPackageReferenceItems);

return allItems;
}
Expand All @@ -130,23 +166,54 @@ private static IEnumerable<ProjectItemInTargetFramework> GetPackageReferenceItem
}
}

private static IEnumerable<ProjectItemInTargetFramework> GetPackageReferenceItems(ProjectInTargetFramework frameworkSpecificProject)
{
var project = frameworkSpecificProject.Project;

return project.GetItems("PackageReference")
.Select(item => new ProjectItemInTargetFramework(item, frameworkSpecificProject));
}

private static PackageReferenceEntry? CreateEntry(ProjectItemInTargetFramework projectItemInTargetFramework)
{
var projectItem = projectItemInTargetFramework.ProjectItem;
var versionSource = projectItem;

var id = projectItem.EvaluatedInclude;

// Ignore the implicit NetStandard library reference in projects targeting NetStandard.
if (id.Equals(NetStandardPackageId, StringComparison.OrdinalIgnoreCase))
return null;

var version = projectItem.GetVersion();
var project = projectItemInTargetFramework.Project;

if (version is null && project.CentralVersionMap.TryGetValue(id, out versionSource))
{
version = versionSource.GetVersion();
}

return version is null
? null
: new PackageReferenceEntry(id, version, versionSource, projectItemInTargetFramework, projectItem.GetMetadataValue("Justification"));
}

internal static bool IsTrue(this ProjectProperty property)
{
return "true".Equals(property.EvaluatedValue, StringComparison.OrdinalIgnoreCase);
}

internal static VersionRange? GetVersion(this ProjectItem projectItem)
{
var versionValue = projectItem.GetMetadata("Version")?.EvaluatedValue;
if (versionValue.IsNullOrEmpty())
return null;

return VersionRange.TryParse(versionValue, out var versionRange)
? new PackageReferenceEntry(id, versionRange, projectItemInTargetFramework, projectItem.GetMetadataValue("Justification"))
: null;
return VersionRange.TryParse(versionValue, out var version) ? version : null;
}

internal static ProjectRootElement GetContainingProject(this ProjectItem projectItem)
{
return projectItem.Xml.ContainingProject;
}
}
9 changes: 5 additions & 4 deletions src/View/Monitor/NugetMonitorViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ private static void Update(ICollection<PackageViewModel> packageViewModels)

var packageReferencesByProject = packageViewModels
.Where(viewModel => viewModel.IsUpdateAvailable)
.SelectMany(viewModel => viewModel.Items.Select(item => new { item.Identity, item.ProjectItemInTargetFramework.ContainingProject.FullPath, viewModel.SelectedVersion }))
.SelectMany(viewModel => viewModel.Items.Select(item => new { item.Identity, item.VersionSource, item.VersionSource.GetContainingProject().FullPath, viewModel.SelectedVersion }))
.GroupBy(item => item.FullPath);

foreach (var packageReferenceEntries in packageReferencesByProject)
Expand All @@ -129,17 +129,19 @@ private static void Update(ICollection<PackageViewModel> packageViewModels)

var project = ProjectRootElement.Open(fullPath, projectCollection, true);

var packageReferences = project.Items;
var projectItems = project.Items;

foreach (var packageReferenceEntry in packageReferenceEntries)
{
var identity = packageReferenceEntry.Identity;
var selectedVersion = packageReferenceEntry.SelectedVersion;
var versionSource = packageReferenceEntry.VersionSource;

if (selectedVersion == null)
continue;

var metadataItems = packageReferences
var metadataItems = projectItems
.Where(item => item.ItemType == versionSource.ItemType)
.Where(item => string.Equals(item.Include, identity.Id, StringComparison.OrdinalIgnoreCase))
.Select(item => item.Metadata.FirstOrDefault(metadata => string.Equals(metadata.Name, "Version", StringComparison.OrdinalIgnoreCase)
&& string.Equals(metadata.Value, identity.VersionRange.OriginalString, StringComparison.OrdinalIgnoreCase)))
Expand All @@ -148,7 +150,6 @@ private static void Update(ICollection<PackageViewModel> packageViewModels)
foreach (var metadata in metadataItems)
{
metadata.Value = selectedVersion.ToString();
metadata.ExpressedAsAttribute = true;
}
}

Expand Down
3 changes: 1 addition & 2 deletions src/View/PackageViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,9 @@ public PackageViewModel(IGrouping<PackageReference, PackageReferenceEntry> items
{
Items = items;
PackageReference = items.Key;
Projects = items.GroupBy(item => item.ProjectItemInTargetFramework.ContainingProject).Select(item => new ProjectViewModel(item.Key)).ToArray();
Projects = items.GroupBy(item => item.ProjectItemInTargetFramework.ProjectItem.GetContainingProject()).Select(item => new ProjectViewModel(item.Key)).ToArray();
ActiveVersion = NuGetVersion.TryParse(PackageReference.VersionRange.OriginalString, out var simpleVersion) ? simpleVersion : PackageReference.VersionRange;
Justifications = string.Join(", ", Items.Select(reference => reference.Justification).Distinct());

}

public IGrouping<PackageReference, PackageReferenceEntry> Items { get; }
Expand Down

0 comments on commit 96c7d55

Please sign in to comment.