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

improve nuget package detection with SDK-managed packages #11127

Draft
wants to merge 18 commits into
base: main
Choose a base branch
from
Draft
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
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,6 @@
path = nuget/helpers/lib/NuGet.Client
url = https://github.com/NuGet/NuGet.Client
branch = release-6.12.x
[submodule "nuget/helpers/lib/dotnet-core"]
path = nuget/helpers/lib/dotnet-core
url = https://github.com/dotnet/core
1 change: 1 addition & 0 deletions nuget/helpers/lib/NuGetUpdater/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ bin/
obj/
Properties/launchSettings.json
NuGetUpdater.sln.DotSettings.user
NuGetUpdater.Core/dotnet-package-correlation.json
*.binlog
1 change: 1 addition & 0 deletions nuget/helpers/lib/NuGetUpdater/Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
<PackageVersion Include="MSBuild.StructuredLogger" Version="2.2.386" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
<PackageVersion Include="NuGet.Core" Version="2.14.0" Aliases="CoreV2" />
<PackageVersion Include="Semver" Version="3.0.0" />
<PackageVersion Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
<PackageVersion Include="System.ComponentModel.Composition" Version="9.0.0" />
<PackageVersion Include="System.Net.Http" Version="4.3.4" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>$(CommonTargetFramework)</TargetFramework>
<OutputType>Exe</OutputType>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="System.CommandLine" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\DotNetPackageCorrelation\DotNetPackageCorrelation.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using System.CommandLine;
using System.Text.Json;

using DotNetPackageCorrelation;

namespace DotNetPackageCorrelation.Cli;

public class Program
{
public static async Task<int> Main(string[] args)
{
var coreLocationOption = new Option<DirectoryInfo>("--core-location", "The location of the .NET Core source code.") { IsRequired = true };
var outputOption = new Option<FileInfo>("--output", "The location to write the result.") { IsRequired = true };
var command = new Command("build")
{
coreLocationOption,
outputOption,
};
command.TreatUnmatchedTokensAsErrors = true;
command.SetHandler(async (coreLocationDirectory, output) =>
{
// the tool is expected to be given the path to the .NET Core repository, but the correlator only needs a specific subdirectory
var releaseNotesDirectory = new DirectoryInfo(Path.Combine(coreLocationDirectory.FullName, "release-notes"));
var correlator = new Correlator(releaseNotesDirectory);
var (sdkPackages, _warnings) = await correlator.RunAsync();
var json = JsonSerializer.Serialize(sdkPackages, Correlator.SerializerOptions);
await File.WriteAllTextAsync(output.FullName, json);
}, coreLocationOption, outputOption);
var exitCode = await command.InvokeAsync(args);
return exitCode;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
using Semver;

using Xunit;

namespace DotNetPackageCorrelation.Tests;

public class CorrelatorTests
{
[Fact]
public async Task FileHandling_AllFilesShapedAppropriately()
{
// the JSON and markdown are shaped as expected
// we're able to determine from `Runtime.Package/8.0.0` that the corresponding version of `Some.Package` is `1.2.3`
var (packageMapper, warnings) = await PackageMapperFromFilesAsync(
("8.0/releases.json", """
{
"releases": [
{
"sdk": {
"version": "8.0.100",
"runtime-version": "8.0.0"
}
}
]
}
"""),
("8.0/8.0.0/8.0.0.md", """
Package name | Version
:-- | :--
Runtime.Package | 8.0.0
Some.Package | 1.2.3
""")
);
Assert.Empty(warnings);
AssertPackageVersion(packageMapper, "Runtime.Package", "8.0.0", "Some.Package", "1.2.3");
}

[Theory]
[InlineData("Some.Package | 1.2.3", "Some.Package", "1.2.3")] // happy path
[InlineData("Some.Package.1.2.3", "Some.Package", "1.2.3")] // looks like a restore directory
[InlineData("Some.Package | 1.2 | 1.2.3.nupkg", "Some.Package", "1.2.3")] // extra columns from a bad filename split
[InlineData("Some.Package | 1.2.3.nupkg", "Some.Package", "1.2.3")] // version contains package extension
[InlineData("Some.Package | 1.2.3.symbols.nupkg", "Some.Package", "1.2.3")] // version contains symbols package extension
[InlineData("some.package.1.2.3.nupkg", "some.package", "1.2.3")] // first column is a filename, second column is missing
[InlineData("some.package.1.2.3.nupkg |", "some.package", "1.2.3")] // first column is a filename, second column is empty
public void PackagesParsedFromMarkdown(string markdownLine, string expectedPackageName, string expectedPackageVersion)
{
var markdownContent = $"""
Package name | Version
:-- | :--
{markdownLine}
""";
var warnings = new List<string>();
var packages = Correlator.GetPackagesFromMarkdown("test.md", markdownContent, warnings);
Assert.Empty(warnings);
var actualpackage = Assert.Single(packages);
Assert.Equal(expectedPackageName, actualpackage.Name);
Assert.Equal(expectedPackageVersion, actualpackage.Version.ToString());
}

private static void AssertPackageVersion(PackageMapper packageMapper, string runtimePackageName, string runtimePackageVersion, string candidatePackageName, string? expectedPackageVersion)
{
var actualPackageVersion = packageMapper.GetPackageVersionThatShippedWithOtherPackage(runtimePackageName, SemVersion.Parse(runtimePackageVersion), candidatePackageName);
if (expectedPackageVersion is null)
{
Assert.Null(actualPackageVersion);
}
else
{
Assert.NotNull(actualPackageVersion);
Assert.Equal(expectedPackageVersion, actualPackageVersion.ToString());
}
}

private static async Task<(PackageMapper PackageMapper, IEnumerable<string> Warnings)> PackageMapperFromFilesAsync(params (string Path, string Content)[] files)
{
var testDirectory = Path.Combine(Path.GetDirectoryName(typeof(CorrelatorTests).Assembly.Location)!, "test-data", Guid.NewGuid().ToString("D"));
Directory.CreateDirectory(testDirectory);

try
{
foreach (var (path, content) in files)
{
var fullPath = Path.Combine(testDirectory, path);
Directory.CreateDirectory(Path.GetDirectoryName(fullPath)!);
await File.WriteAllTextAsync(fullPath, content);
}

var correlator = new Correlator(new DirectoryInfo(testDirectory));
var (runtimePackages, warnings) = await correlator.RunAsync();
var packageMapper = PackageMapper.Load(runtimePackages);
return (packageMapper, warnings);
}
finally
{
Directory.Delete(testDirectory, recursive: true);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>$(CommonTargetFramework)</TargetFramework>
<OutputType>Exe</OutputType>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="xunit" />
<PackageReference Include="xunit.runner.visualstudio" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\DotNetPackageCorrelation\DotNetPackageCorrelation.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System.Runtime.CompilerServices;

using Semver;

using Xunit;

namespace DotNetPackageCorrelation.Tests;

public class EndToEndTests
{
[Fact]
public async Task IntegrationTest()
{
// arrange
var thisFileDirectory = Path.GetDirectoryName(GetThisFilePath())!;
var dotnetCoreDirectory = Path.Combine(thisFileDirectory, "..", "..", "dotnet-core");
var correlator = new Correlator(new DirectoryInfo(Path.Combine(dotnetCoreDirectory, "release-notes")));

// act
var (runtimePackages, _warnings) = await correlator.RunAsync();
var packageMapper = PackageMapper.Load(runtimePackages);

// assert
// Microsoft.NETCore.App.Ref/8.0.8 didn't ship with System.Text.Json, but the previous version 8.0.7 shipped at the same time as System.Text.Json/8.0.4
var systemTextJsonVersion = packageMapper.GetPackageVersionThatShippedWithOtherPackage("Microsoft.NETCore.App.Ref", SemVersion.Parse("8.0.8"), "System.Text.Json");
Assert.Equal("8.0.4", systemTextJsonVersion?.ToString());
}

private static string GetThisFilePath([CallerFilePath] string? path = null) => path ?? throw new ArgumentNullException(nameof(path));
ryanbrandenburg marked this conversation as resolved.
Show resolved Hide resolved
}
Loading
Loading