Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgrade to dotnet8, and new plugin to list project dependencies #60

Merged
merged 8 commits into from
Nov 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v2
with:
dotnet-version: 6.0.x
dotnet-version: 8.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build
Expand Down
12 changes: 12 additions & 0 deletions Microsoft.NugetNinja.sln
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NugetNinja.MissingPropertyP
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NugetNinja.PrBot", "src\NugetNinja.PrBot\NugetNinja.PrBot.csproj", "{FF24D161-7796-4CA2-A652-061DBEE89626}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NugetNinja.PackageListerPlugin", "NugetNinja.PackageListerPlugin\NugetNinja.PackageListerPlugin.csproj", "{73804BAD-88F7-4283-9AFF-394AA8CC7C6F}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{6C1940FB-3AD9-4952-AB03-A06F7B99B6A7}"
ProjectSection(SolutionItems) = preProject
.github\workflows\build.yml = .github\workflows\build.yml
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -75,6 +82,10 @@ Global
{FF24D161-7796-4CA2-A652-061DBEE89626}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FF24D161-7796-4CA2-A652-061DBEE89626}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FF24D161-7796-4CA2-A652-061DBEE89626}.Release|Any CPU.Build.0 = Release|Any CPU
{73804BAD-88F7-4283-9AFF-394AA8CC7C6F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{73804BAD-88F7-4283-9AFF-394AA8CC7C6F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{73804BAD-88F7-4283-9AFF-394AA8CC7C6F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{73804BAD-88F7-4283-9AFF-394AA8CC7C6F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -91,6 +102,7 @@ Global
{D2D68A5F-3283-4001-840F-3E14A9B372E8} = {0EEF3C9A-9684-4642-9A66-8CD819EC6235}
{335BE82E-E650-4E62-8107-B6BBD34E3107} = {0EEF3C9A-9684-4642-9A66-8CD819EC6235}
{FF24D161-7796-4CA2-A652-061DBEE89626} = {EE324A7A-86D4-4B7D-ADC5-3E6504738413}
{73804BAD-88F7-4283-9AFF-394AA8CC7C6F} = {0EEF3C9A-9684-4642-9A66-8CD819EC6235}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {2352F460-4B23-4215-951C-A3E923145148}
Expand Down
13 changes: 13 additions & 0 deletions NugetNinja.PackageListerPlugin/App/PackageListingHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using Microsoft.NugetNinja.Core;

namespace Microsoft.NugetNinja.PackageListerPlugin;

public class PackageListingHandler : DetectorBasedCommandHandler<PackageListerDetector, StartUp>
{
public override string Name => "list-packages";

public override string Description => "The command to generate a csv file with all direct packages and nuget.org descriptions. Dry run will only list the packages on the console.";
}
107 changes: 107 additions & 0 deletions NugetNinja.PackageListerPlugin/Models/PackageList.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System.Globalization;
using System.Text;
using CsvHelper;
using CsvHelper.Configuration.Attributes;
using Microsoft.Extensions.Logging;
using Microsoft.NugetNinja.Core;
using NuGet.Common;
using NuGet.Configuration;
using NuGet.Protocol.Core.Types;
using NugetNinja.PackageListerPlugin.Models;

namespace Microsoft.NugetNinja.PackageListerPlugin;

public class PackageList : IAction
{

private List<(string project, List<Package> packages)> _allPackages;
private readonly ILogger<PackageListerDetector> _logger;

public PackageList(List<(string, List<Package>)> allPackages1, Extensions.Logging.ILogger<PackageListerDetector> logger)
{
_allPackages = allPackages1;
_logger = logger;
}

public string BuildMessage()
{
var sb = new StringBuilder();
foreach (var package in _allPackages)
{
sb.AppendLine($"Project: {package.project}\n");
foreach (var p in package.packages)
{
sb.AppendLine($" {p.Name} {p.Version}\n");
}
}
return sb.ToString();
}

public async Task TakeActionAsync()
{
_logger.LogInformation($"Preparing summary file");
var flatList = _allPackages.SelectMany(p => p.packages.Select(package => (p.project, package))).ToList();
var byPackage = flatList.GroupBy(p => p.package.Name).ToList();
var allLines = new List<PackageListLine>();
foreach (var el in byPackage)
{
try
{
var description = SanitizeNugetDescription(await GetNugetDescriptionAsync(el.Key));
allLines.Add(new PackageListLine(el.Key, GetAllVersions(el), description, null, GetAllProjects(el)));
}
catch (Exception e)
{
_logger.LogError(e, $"Error processing package {el.Key}");
}
}
allLines = allLines.OrderBy(p => p.PackageId).ToList();
var outFile = "all_packages.csv";
_logger.LogInformation($"Writing package list to {Path.GetFullPath(outFile)}");
using (var writer = new StreamWriter("all_packages.csv"))
using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture))
{
csv.WriteRecords(allLines);
}
await Task.CompletedTask;
}

public async Task<string> GetNugetDescriptionAsync(string packageId)
{
_logger.LogInformation($"Getting description from nuget.org for {packageId}");
var providers = NuGet.Protocol.Core.Types.Repository.Provider.GetCoreV3();
var source = new PackageSource("https://api.nuget.org/v3/index.json");
var repository = new SourceRepository(source, providers);
var resource = await repository.GetResourceAsync<PackageMetadataResource>();
var metadata = await resource.GetMetadataAsync(packageId, includePrerelease: false, includeUnlisted: false, new SourceCacheContext(), NullLogger.Instance, CancellationToken.None);

var packageMetadata = metadata.FirstOrDefault();
return packageMetadata?.Description ?? "Description not found";
}

private static string SanitizeNugetDescription(string desc)
{
const string marker = "Commonly used types:";
int index = desc.IndexOf(marker, StringComparison.OrdinalIgnoreCase);
if (index >= 0)
{
desc = desc.Substring(0, index);
}
return desc.Replace("\n", " ").Replace("\r", " ").Trim();
}

public string GetAllVersions(IEnumerable<(string project, Package package)> packages)
{
var allVersions = packages.Select(p => p.package.SourceVersionText).Distinct().ToList();
return string.Join(", ", allVersions);
}

public string GetAllProjects(IEnumerable<(string project, Package package)> packages)
{
var allProjects = packages.Select(p => p.project).Distinct().ToList();
return string.Join(", ", allProjects);
}
}
13 changes: 13 additions & 0 deletions NugetNinja.PackageListerPlugin/Models/PackageListLine.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace NugetNinja.PackageListerPlugin.Models
{
public record PackageListLine(string PackageId, string Versions, string NugetDescription, string? TargetFramework, string Projects);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="CsvHelper" Version="33.0.1" />
<PackageReference Include="NuGet.Protocol" Version="6.11.1" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\src\NugetNinja.Core\NugetNinja.Core.csproj" />
</ItemGroup>

</Project>
13 changes: 13 additions & 0 deletions NugetNinja.PackageListerPlugin/PackageListerPlugin.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using Microsoft.NugetNinja.Core;

namespace Microsoft.NugetNinja.PackageListerPlugin;

public class PackageListerPlugin : INinjaPlugin
{
public CommandHandler[] Install() => new CommandHandler[] { new PackageListingHandler() };
}


43 changes: 43 additions & 0 deletions NugetNinja.PackageListerPlugin/Services/PackageListerDetector.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using Microsoft.Extensions.Logging;
using Microsoft.NugetNinja.Core;

namespace Microsoft.NugetNinja.PackageListerPlugin;

public class PackageListerDetector : IActionDetector
{
private readonly ILogger<PackageListerDetector> _logger;
private readonly bool _fillInOutputType = false;

Check warning on line 12 in NugetNinja.PackageListerPlugin/Services/PackageListerDetector.cs

View workflow job for this annotation

GitHub Actions / build (macos-latest)

The field 'PackageListerDetector._fillInOutputType' is assigned but its value is never used

Check warning on line 12 in NugetNinja.PackageListerPlugin/Services/PackageListerDetector.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

The field 'PackageListerDetector._fillInOutputType' is assigned but its value is never used

Check warning on line 12 in NugetNinja.PackageListerPlugin/Services/PackageListerDetector.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest)

The field 'PackageListerDetector._fillInOutputType' is assigned but its value is never used
private readonly bool _enforceNullable = false;

Check warning on line 13 in NugetNinja.PackageListerPlugin/Services/PackageListerDetector.cs

View workflow job for this annotation

GitHub Actions / build (macos-latest)

The field 'PackageListerDetector._enforceNullable' is assigned but its value is never used

Check warning on line 13 in NugetNinja.PackageListerPlugin/Services/PackageListerDetector.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

The field 'PackageListerDetector._enforceNullable' is assigned but its value is never used

Check warning on line 13 in NugetNinja.PackageListerPlugin/Services/PackageListerDetector.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest)

The field 'PackageListerDetector._enforceNullable' is assigned but its value is never used
private readonly bool _enforceImplicitUsings = false;

Check warning on line 14 in NugetNinja.PackageListerPlugin/Services/PackageListerDetector.cs

View workflow job for this annotation

GitHub Actions / build (macos-latest)

The field 'PackageListerDetector._enforceImplicitUsings' is assigned but its value is never used

Check warning on line 14 in NugetNinja.PackageListerPlugin/Services/PackageListerDetector.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

The field 'PackageListerDetector._enforceImplicitUsings' is assigned but its value is never used

Check warning on line 14 in NugetNinja.PackageListerPlugin/Services/PackageListerDetector.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest)

The field 'PackageListerDetector._enforceImplicitUsings' is assigned but its value is never used
private readonly string[] _notSupportedRuntimes = {
"net5.0",
"netcoreapp3.0",
"netcoreapp2.2",
"netcoreapp2.1",
"netcoreapp1.1",
"netcoreapp1.0",
};

private readonly string _suggestedRuntime = "net8.0";

Check warning on line 24 in NugetNinja.PackageListerPlugin/Services/PackageListerDetector.cs

View workflow job for this annotation

GitHub Actions / build (macos-latest)

The field 'PackageListerDetector._suggestedRuntime' is assigned but its value is never used

Check warning on line 24 in NugetNinja.PackageListerPlugin/Services/PackageListerDetector.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

The field 'PackageListerDetector._suggestedRuntime' is assigned but its value is never used

Check warning on line 24 in NugetNinja.PackageListerPlugin/Services/PackageListerDetector.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest)

The field 'PackageListerDetector._suggestedRuntime' is assigned but its value is never used

public PackageListerDetector(
ILogger<PackageListerDetector> logger)
{
_logger = logger;
}

public async IAsyncEnumerable<IAction> AnalyzeAsync(Model context)
{
_logger.LogInformation("Analyzing package list");
await Task.CompletedTask;
var allPackages = new List<(string,List<Package>)>();
foreach (var project in context.AllProjects)
{
allPackages.Add((project.PathOnDisk,project.PackageReferences));
}
yield return new PackageList(allPackages, _logger);
}
}
15 changes: 15 additions & 0 deletions NugetNinja.PackageListerPlugin/StartUp.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using Microsoft.Extensions.DependencyInjection;
using Microsoft.NugetNinja.Core;

namespace Microsoft.NugetNinja.PackageListerPlugin;

public class StartUp : IStartUp
{
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<PackageListerDetector>();
}
}
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

Nuget Ninjia is a tool for detecting dependencies of .NET projects. It analyzes the dependency structure of .NET projects in a directory and builds a directed acyclic graph. And will give some modification suggestions for Nuget packages, so that the dependencies of the project are as concise and up-to-date as possible.

The tool can also generate a list of all top level dependencies into a CSV file. This CSV includes the nuget description and avoids issues with `shproj` and `dotnet list packages`.

## Usage

After getting the binary, run it directly in the terminal.
Expand Down Expand Up @@ -36,13 +38,14 @@ Commands:
upgrade-pkg The command to upgrade all package references to possible latest and avoid conflicts.
clean-pkg The command to clean up possible useless package references.
clean-prj The command to clean up possible useless project references.
list-packages The command to generate a csv file with all direct packages and nuget.org descriptions. Dry run will only list the packages on the console.
```

## How to build and run locally

Requirements about how to develop.

* [.NET SDK 6.0](https://github.com/dotnet/core/tree/master/release-notes)
* [.NET SDK 8.0](https://github.com/dotnet/core/tree/master/release-notes)

1. Execute `dotnet restore` to restore all .NET dependencies.
2. Execute the following command to build the app:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@

<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace>Microsoft.NugetNinja.AllOfficialsPlugin</RootNamespace>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\NugetNinja.PackageListerPlugin\NugetNinja.PackageListerPlugin.csproj" />
<ProjectReference Include="..\NugetNinja.DeprecatedPackagePlugin\NugetNinja.DeprecatedPackagePlugin.csproj" />
<ProjectReference Include="..\NugetNinja.MissingPropertyPlugin\NugetNinja.MissingPropertyPlugin.csproj" />
<ProjectReference Include="..\NugetNinja.PossiblePackageUpgradePlugin\NugetNinja.PossiblePackageUpgradePlugin.csproj" />
Expand Down
2 changes: 1 addition & 1 deletion src/NugetNinja.Core/Model/Workspace/Model.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ private Package[] GetPackageReferences(HtmlDocument doc)
var packageReferences = doc.DocumentNode
.Descendants("PackageReference")
.Select(p => new Package(
name: p.Attributes["Include"].Value,
name: p.Attributes["Include"]?.Value?? p.Attributes["Update"].Value,
versionText: p.Attributes["Version"]?.Value ?? "0.0.1"))
.ToArray();

Expand Down
4 changes: 4 additions & 0 deletions src/NugetNinja.Core/Model/Workspace/NugetVersion.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,13 @@ public NugetVersion(string versionString)
public static bool operator !=(NugetVersion? lvs, NugetVersion? rvs) => !(lvs == rvs);

public static bool operator <(NugetVersion? lvs, NugetVersion? rvs) => lvs?.CompareTo(rvs) < 0;
public static bool operator <=(NugetVersion? lvs, NugetVersion? rvs) => lvs?.CompareTo(rvs) <= 0;

public static bool operator >(NugetVersion? lvs, NugetVersion? rvs) => lvs?.CompareTo(rvs) > 0;

public static bool operator >=(NugetVersion? lvs, NugetVersion? rvs) => lvs?.CompareTo(rvs) >= 0;


public object Clone() => new NugetVersion(SourceString);

public bool IsPreviewVersion() => !string.IsNullOrWhiteSpace(AdditionalText);
Expand Down
Loading
Loading