dependabot-nuget 0.292.0 → 0.293.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/helpers/lib/NuGetUpdater/.gitignore +1 -0
  3. data/helpers/lib/NuGetUpdater/Directory.Packages.props +1 -0
  4. data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation/Correlator.cs +197 -0
  5. data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation/DotNetPackageCorrelation.csproj +12 -0
  6. data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation/Model/PackageMapper.cs +68 -0
  7. data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation/Model/PackageSet.cs +11 -0
  8. data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation/Model/Release.cs +25 -0
  9. data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation/Model/ReleasesFile.cs +9 -0
  10. data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation/Model/RuntimePackages.cs +11 -0
  11. data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation/Model/Sdk.cs +13 -0
  12. data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation/Model/SemVerComparer.cs +16 -0
  13. data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation/Model/SemVersionConverter.cs +42 -0
  14. data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation.Cli/DotNetPackageCorrelation.Cli.csproj +16 -0
  15. data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation.Cli/Program.cs +32 -0
  16. data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation.Test/CorrelatorTests.cs +99 -0
  17. data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation.Test/DotNetPackageCorrelation.Test.csproj +18 -0
  18. data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation.Test/EndToEndTests.cs +30 -0
  19. data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation.Test/RuntimePackagesTests.cs +206 -0
  20. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/AnalyzeCommand.cs +6 -4
  21. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/DiscoverCommand.cs +8 -7
  22. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/RunCommand.cs +4 -4
  23. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/UpdateCommand.cs +17 -5
  24. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Analyze.cs +7 -1
  25. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Discover.cs +46 -6
  26. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Update.cs +8 -0
  27. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/AnalyzeWorker.cs +8 -17
  28. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/DependencyFinder.cs +4 -4
  29. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/Extensions.cs +1 -1
  30. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Clone/CloneWorker.cs +2 -1
  31. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/DependencyDiscovery.targets +2 -2
  32. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/DiscoveryWorker.cs +7 -20
  33. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/ProjectDiscoveryResult.cs +3 -3
  34. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/SdkProjectDiscovery.cs +99 -2
  35. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/EnsureDotNetPackageCorrelation.targets +25 -0
  36. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/ExperimentsManager.cs +9 -22
  37. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Files/PackagesConfigBuildFile.cs +5 -1
  38. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/MissingFileException.cs +2 -1
  39. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/NativeResult.cs +3 -3
  40. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/NuGetUpdater.Core.csproj +3 -0
  41. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/DependencyFileNotFound.cs +7 -3
  42. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/DependencyFileNotParseable.cs +15 -0
  43. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/JobErrorBase.cs +24 -0
  44. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/RunWorker.cs +6 -21
  45. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/UnparseableFileException.cs +12 -0
  46. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackageReferenceUpdater.cs +21 -0
  47. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/UpdaterWorker.cs +6 -30
  48. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/DotNetPackageCorrelationManager.cs +46 -0
  49. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +51 -27
  50. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/NuGetHelper.cs +1 -1
  51. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/AnalyzeWorkerTestBase.cs +15 -4
  52. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/AnalyzeWorkerTests.cs +15 -9
  53. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTestBase.cs +20 -12
  54. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.Project.cs +108 -0
  55. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.cs +16 -12
  56. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/ExpectedDiscoveryResults.cs +1 -1
  57. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/MockNuGetPackage.cs +15 -28
  58. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/RunWorkerTests.cs +5 -4
  59. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/SerializationTests.cs +9 -1
  60. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/TestBase.cs +24 -0
  61. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/ExpectedUpdateOperationResult.cs +1 -1
  62. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTestBase.cs +11 -15
  63. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.DirsProj.cs +1 -1
  64. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.Mixed.cs +14 -8
  65. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackageReference.cs +148 -3
  66. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackagesConfig.cs +12 -14
  67. data/helpers/lib/NuGetUpdater/NuGetUpdater.sln +18 -1
  68. data/lib/dependabot/nuget/native_helpers.rb +41 -16
  69. metadata +25 -6
  70. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/ErrorType.cs +0 -12
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fc0cf2e8a7f769337787f983af710cd8a4c30c464b1118f22589c522b3ed107a
4
- data.tar.gz: b1e2fb7957b3cd3daf6331e199ebce5b0f94d84f42033de6e6f0e39bef4295b8
3
+ metadata.gz: 833b170b34a1cef53346970cdfc208b39f49899aaeeb1d2da285463eabf2aea3
4
+ data.tar.gz: aea74663e1ec787d2496c6d63f97c531d6ccd5415cdd810ce7d8518080b027cc
5
5
  SHA512:
6
- metadata.gz: 2c66ad9bb9ce3d932d0965f18fc4a6dde14ae022413343689a7c92f1e9439cca523447c20719f579bbc6c5fe6caeadab1dde0056f0a99e4a1afd09c7a5a79266
7
- data.tar.gz: eb4601daef366ea15b011fb20a3efce1b976da73a4c4ba14c991a1aa9910a01d220b2cc46a086e47e7336432597b01f431d65ad4d40b431b1545996e1af613dd
6
+ metadata.gz: 889ab157302c7dbb0be345ca4e5204fe5518d6aafbd61d7992008404fa84200240e6f88655027ddb4a77611964cb97e2ea7857ad6453d33e498e4d6416b79eb8
7
+ data.tar.gz: 17f3003ef471825cf980d00da41c528bb051dc64285d6ca808ca84bea09a89b91a197a388d8f62c4e089427a52e77ba24d13b452ec9bc7231b46d18c9c84d697
@@ -4,4 +4,5 @@ bin/
4
4
  obj/
5
5
  Properties/launchSettings.json
6
6
  NuGetUpdater.sln.DotSettings.user
7
+ NuGetUpdater.Core/dotnet-package-correlation.json
7
8
  *.binlog
@@ -26,6 +26,7 @@
26
26
  <PackageVersion Include="MSBuild.StructuredLogger" Version="2.2.386" />
27
27
  <PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
28
28
  <PackageVersion Include="NuGet.Core" Version="2.14.0" Aliases="CoreV2" />
29
+ <PackageVersion Include="Semver" Version="3.0.0" />
29
30
  <PackageVersion Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
30
31
  <PackageVersion Include="System.ComponentModel.Composition" Version="9.0.0" />
31
32
  <PackageVersion Include="System.Net.Http" Version="4.3.4" />
@@ -0,0 +1,197 @@
1
+ using System.Collections.Immutable;
2
+ using System.Text.Json;
3
+ using System.Text.RegularExpressions;
4
+
5
+ using Semver;
6
+
7
+ namespace DotNetPackageCorrelation;
8
+
9
+ public partial class Correlator
10
+ {
11
+ public static readonly JsonSerializerOptions SerializerOptions = new()
12
+ {
13
+ WriteIndented = true,
14
+ Converters = { new SemVersionConverter() },
15
+ };
16
+
17
+ private readonly DirectoryInfo _releaseNotesDirectory;
18
+
19
+ public Correlator(DirectoryInfo releaseNotesDirectory)
20
+ {
21
+ _releaseNotesDirectory = releaseNotesDirectory;
22
+ }
23
+
24
+ public async Task<(RuntimePackages RuntimePackages, IEnumerable<string> Warnings)> RunAsync()
25
+ {
26
+ var runtimeVersions = new List<Version>();
27
+ foreach (var directory in Directory.EnumerateDirectories(_releaseNotesDirectory.FullName))
28
+ {
29
+ var directoryName = Path.GetFileName(directory);
30
+ if (Version.TryParse(directoryName, out var version))
31
+ {
32
+ runtimeVersions.Add(version);
33
+ }
34
+ }
35
+
36
+ var runtimePackages = new RuntimePackages();
37
+ var warnings = new List<string>();
38
+ foreach (var majorVersion in runtimeVersions)
39
+ {
40
+ var releasesJsonPath = Path.Combine(_releaseNotesDirectory.FullName, majorVersion.ToString(), "releases.json");
41
+ if (!File.Exists(releasesJsonPath))
42
+ {
43
+ warnings.Add($"Unable to find releases.json file for version {majorVersion}");
44
+ continue;
45
+ }
46
+
47
+ var releasesJson = await File.ReadAllTextAsync(releasesJsonPath);
48
+ var releasesFile = JsonSerializer.Deserialize<ReleasesFile>(releasesJson, SerializerOptions)!;
49
+
50
+ foreach (var release in releasesFile.Releases)
51
+ {
52
+ foreach (var sdk in release.GetSdks())
53
+ {
54
+ if (sdk.Version is null)
55
+ {
56
+ warnings.Add($"Skipping release with missing version information from {releasesJson}");
57
+ continue;
58
+ }
59
+
60
+ if (sdk.RuntimeVersion is null)
61
+ {
62
+ warnings.Add($"Skipping release with missing runtime version information from {releasesJson}");
63
+ continue;
64
+ }
65
+
66
+ if (runtimePackages.Runtimes.ContainsKey(sdk.RuntimeVersion))
67
+ {
68
+ // already processed this runtime
69
+ continue;
70
+ }
71
+
72
+ var packagesAndVersions = new PackageSet();
73
+ runtimePackages.Runtimes.Add(sdk.RuntimeVersion, packagesAndVersions);
74
+ var runtimeDirectory = new DirectoryInfo(Path.Combine(_releaseNotesDirectory.FullName, majorVersion.ToString(), sdk.RuntimeVersion.ToString()));
75
+ var runtimeMarkdownPath = Path.Combine(runtimeDirectory.FullName, $"{sdk.RuntimeVersion}.md");
76
+ if (!File.Exists(runtimeMarkdownPath))
77
+ {
78
+ warnings.Add($"Unable to find expected markdown file {runtimeMarkdownPath}");
79
+ continue;
80
+ }
81
+
82
+ var markdownContent = await File.ReadAllTextAsync(runtimeMarkdownPath);
83
+ var packages = GetPackagesFromMarkdown(runtimeMarkdownPath, markdownContent, warnings);
84
+ foreach (var (packageName, packageVersion) in packages)
85
+ {
86
+ packagesAndVersions.Packages[packageName] = packageVersion;
87
+ }
88
+ }
89
+ }
90
+ }
91
+
92
+ return (runtimePackages, warnings);
93
+ }
94
+
95
+ public static ImmutableArray<(string Name, SemVersion Version)> GetPackagesFromMarkdown(string markdownPath, string markdownContent, List<string> warnings)
96
+ {
97
+ var lines = markdownContent.Split("\n").Select(l => l.Trim()).ToArray();
98
+
99
+ // the markdown file contains a table that looks like this:
100
+ // Package name | Version
101
+ // :----------- | :------------------
102
+ // Some.Package | 1.2.3
103
+ // ...
104
+ // however there are some formatting issues with some elements that prevent markdown parsers from
105
+ // discovering it, so we fall back to manual parsing
106
+
107
+ var tableStartLine = -1;
108
+ for (int i = 0; i < lines.Length; i++)
109
+ {
110
+ if (Regex.IsMatch(lines[i], "Package name.*Version"))
111
+ {
112
+ tableStartLine = i;
113
+ break;
114
+ }
115
+ }
116
+
117
+ if (tableStartLine == -1)
118
+ {
119
+ warnings.Add($"Unable to find table start in file {markdownPath}");
120
+ return [];
121
+ }
122
+
123
+ // skip the column names and separator line
124
+ tableStartLine += 2;
125
+
126
+ var tableEndLine = lines.Length; // assume the end of the file unless we find a blank line
127
+ for (int i = tableStartLine; i < lines.Length; i++)
128
+ {
129
+ if (string.IsNullOrEmpty(lines[i]))
130
+ {
131
+ tableEndLine = i;
132
+ break;
133
+ }
134
+ }
135
+
136
+ var packages = new List<(string Name, SemVersion Version)>();
137
+ for (int i = tableStartLine; i < tableEndLine; i++)
138
+ {
139
+ var line = lines[i].Trim();
140
+ var foundMatch = false;
141
+ foreach (var pattern in SpecialCasePatterns)
142
+ {
143
+ var match = pattern.Match(line);
144
+ if (match.Success)
145
+ {
146
+ var packageName = match.Groups["PackageName"].Value;
147
+ var packageVersionString = match.Groups["PackageVersion"].Value;
148
+ if (SemVersion.TryParse(packageVersionString, out var packageVersion))
149
+ {
150
+ packages.Add((packageName, packageVersion));
151
+ foundMatch = true;
152
+ break; ;
153
+ }
154
+ }
155
+ }
156
+
157
+ if (!foundMatch)
158
+ {
159
+ warnings.Add($"Unable to parse package and version from string [{line}] in file [{markdownPath}]:{i}");
160
+ }
161
+ }
162
+
163
+ return packages.ToImmutableArray();
164
+ }
165
+
166
+ // The different patterns the lines in the markdown might take. Due to issues with regular expressions, this list
167
+ // is in a very specific order.
168
+ private static ImmutableArray<Regex> SpecialCasePatterns { get; } = [
169
+ StandardLineWithFileExtensions(),
170
+ StandardLine(),
171
+ PackageNameDotVersion(),
172
+ PackageFileNameWithOptionalTrailingPipe(),
173
+ MultiColumnWithOptionalFileSuffix(),
174
+ ];
175
+
176
+ [GeneratedRegex(@"^(?<PackageName>[^|\s]+)\s*\|\s*(?<PackageVersion>[^|\s]+?)(\.symbols)?\.nupkg$", RegexOptions.Compiled)]
177
+ // Some.Package | 1.2.3.nupkg
178
+ // Some.Package | 1.2.3.symbols.nupkg
179
+ private static partial Regex StandardLineWithFileExtensions();
180
+
181
+ [GeneratedRegex(@"^(?<PackageName>[^|\s]+)\s*\|\s*(?<PackageVersion>[^|\s]+)$", RegexOptions.Compiled)]
182
+ // Some.Package | 1.2.3
183
+ private static partial Regex StandardLine();
184
+
185
+ [GeneratedRegex(@"^(?<PackageName>[^\d]+)\.(?<PackageVersion>[\d].+)$", RegexOptions.Compiled)]
186
+ // Some.Package.1.2.3
187
+ private static partial Regex PackageNameDotVersion();
188
+
189
+ [GeneratedRegex(@"^(?<PackageName>[^\d]+)\.(?<PackageVersion>\d.+?)\.nupkg(\s+\|)?$", RegexOptions.Compiled)]
190
+ // some.package.1.2.3.nupkg
191
+ // some.package.1.2.3.nupkg |
192
+ private static partial Regex PackageFileNameWithOptionalTrailingPipe();
193
+
194
+ [GeneratedRegex(@"^(?<PackageName>[^|\s]+)\s*\|[^|]*\|\s*(?<PackageVersion>.*?)(\.nupkg)?$", RegexOptions.Compiled)]
195
+ // Some.Package | 1.2 | 1.2.3.nupkg
196
+ private static partial Regex MultiColumnWithOptionalFileSuffix();
197
+ }
@@ -0,0 +1,12 @@
1
+ <Project Sdk="Microsoft.NET.Sdk">
2
+
3
+ <PropertyGroup>
4
+ <TargetFramework>$(CommonTargetFramework)</TargetFramework>
5
+ </PropertyGroup>
6
+
7
+ <ItemGroup>
8
+ <PackageReference Include="Semver" />
9
+ <PackageReference Include="System.Text.Json" />
10
+ </ItemGroup>
11
+
12
+ </Project>
@@ -0,0 +1,68 @@
1
+ using Semver;
2
+
3
+ namespace DotNetPackageCorrelation;
4
+
5
+ public class PackageMapper
6
+ {
7
+ public RuntimePackages RuntimePackages { get; }
8
+
9
+ private PackageMapper(RuntimePackages runtimePackages)
10
+ {
11
+ RuntimePackages = runtimePackages;
12
+ }
13
+
14
+ /// <summary>
15
+ /// Find the version of <paramref name="candidatePackageName"/> that shipped at the same time as
16
+ /// &quot;<paramref name="packageName"/>/<paramref name="packageVersion"/>&quot;.
17
+ /// </summary>
18
+ public SemVersion? GetPackageVersionThatShippedWithOtherPackage(string packageName, SemVersion packageVersion, string candidatePackageName)
19
+ {
20
+ var runtimeVersion = GetRuntimeVersionFromPackage(packageName, packageVersion);
21
+ if (runtimeVersion is null)
22
+ {
23
+ // no runtime found that contains the package
24
+ return null;
25
+ }
26
+
27
+ var candidateRuntimeVersions = RuntimePackages.Runtimes.Keys
28
+ .Where(v => v.Major == runtimeVersion.Major)
29
+ .Where(v => v.ComparePrecedenceTo(runtimeVersion) <= 0)
30
+ .OrderBy(v => v, SemVerComparer.Instance)
31
+ .Reverse()
32
+ .ToArray();
33
+ foreach (var candidateRuntimeVersion in candidateRuntimeVersions)
34
+ {
35
+ if (!RuntimePackages.Runtimes.TryGetValue(candidateRuntimeVersion, out var packageSet))
36
+ {
37
+ continue;
38
+ }
39
+
40
+ if (packageSet.Packages.TryGetValue(candidatePackageName, out var foundPackageVersion))
41
+ {
42
+ return foundPackageVersion;
43
+ }
44
+ }
45
+
46
+ return null;
47
+ }
48
+
49
+ private SemVersion? GetRuntimeVersionFromPackage(string packageName, SemVersion packageVersion)
50
+ {
51
+ // TODO: linear search is slow
52
+ foreach (var runtime in RuntimePackages.Runtimes)
53
+ {
54
+ if (runtime.Value.Packages.TryGetValue(packageName, out var foundPackageVersion) &&
55
+ foundPackageVersion == packageVersion)
56
+ {
57
+ return runtime.Key;
58
+ }
59
+ }
60
+
61
+ return null;
62
+ }
63
+
64
+ public static PackageMapper Load(RuntimePackages runtimePackages)
65
+ {
66
+ return new PackageMapper(runtimePackages);
67
+ }
68
+ }
@@ -0,0 +1,11 @@
1
+ using System.Text.Json.Serialization;
2
+
3
+ using Semver;
4
+
5
+ namespace DotNetPackageCorrelation;
6
+
7
+ public record PackageSet
8
+ {
9
+ [JsonObjectCreationHandling(JsonObjectCreationHandling.Populate)]
10
+ public SortedDictionary<string, SemVersion> Packages { get; init; } = new(StringComparer.OrdinalIgnoreCase);
11
+ }
@@ -0,0 +1,25 @@
1
+ using System.Collections.Immutable;
2
+ using System.Text.Json.Serialization;
3
+
4
+ namespace DotNetPackageCorrelation;
5
+
6
+ public record Release
7
+ {
8
+ [JsonPropertyName("sdk")]
9
+ public required Sdk Sdk { get; init; }
10
+
11
+ [JsonPropertyName("sdks")]
12
+ public ImmutableArray<Sdk>? Sdks { get; init; } = [];
13
+ }
14
+
15
+ public static class ReleaseExtensions
16
+ {
17
+ public static IEnumerable<Sdk> GetSdks(this Release release)
18
+ {
19
+ yield return release.Sdk;
20
+ foreach (var sdk in release.Sdks ?? [])
21
+ {
22
+ yield return sdk;
23
+ }
24
+ }
25
+ }
@@ -0,0 +1,9 @@
1
+ using System.Text.Json.Serialization;
2
+
3
+ namespace DotNetPackageCorrelation;
4
+
5
+ public record ReleasesFile
6
+ {
7
+ [JsonPropertyName("releases")]
8
+ public required Release[] Releases { get; init; }
9
+ }
@@ -0,0 +1,11 @@
1
+ using System.Text.Json.Serialization;
2
+
3
+ using Semver;
4
+
5
+ namespace DotNetPackageCorrelation;
6
+
7
+ public record RuntimePackages
8
+ {
9
+ [JsonObjectCreationHandling(JsonObjectCreationHandling.Populate)]
10
+ public SortedDictionary<SemVersion, PackageSet> Runtimes { get; init; } = new(SemVerComparer.Instance);
11
+ }
@@ -0,0 +1,13 @@
1
+ using System.Text.Json.Serialization;
2
+
3
+ using Semver;
4
+
5
+ namespace DotNetPackageCorrelation;
6
+
7
+ public record Sdk
8
+ {
9
+ [JsonPropertyName("version")]
10
+ public required SemVersion? Version { get; init; }
11
+ [JsonPropertyName("runtime-version")]
12
+ public SemVersion? RuntimeVersion { get; init; }
13
+ }
@@ -0,0 +1,16 @@
1
+ using Semver;
2
+
3
+ namespace DotNetPackageCorrelation;
4
+
5
+ public class SemVerComparer : IComparer<SemVersion>
6
+ {
7
+ public static SemVerComparer Instance = new();
8
+
9
+ public int Compare(SemVersion? x, SemVersion? y)
10
+ {
11
+ ArgumentNullException.ThrowIfNull(x);
12
+ ArgumentNullException.ThrowIfNull(y);
13
+
14
+ return x.ComparePrecedenceTo(y);
15
+ }
16
+ }
@@ -0,0 +1,42 @@
1
+ using System.Diagnostics.CodeAnalysis;
2
+ using System.Text.Json;
3
+ using System.Text.Json.Serialization;
4
+
5
+ using Semver;
6
+
7
+ namespace DotNetPackageCorrelation;
8
+
9
+ public class SemVersionConverter : JsonConverter<SemVersion?>
10
+ {
11
+ public override SemVersion? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
12
+ {
13
+ var value = reader.GetString();
14
+ if (SemVersion.TryParse(value, out var result))
15
+ {
16
+ return result;
17
+ }
18
+
19
+ return null;
20
+ }
21
+
22
+ public override SemVersion ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
23
+ {
24
+ var value = Read(ref reader, typeToConvert, options);
25
+ if (value is null)
26
+ {
27
+ throw new JsonException($"Unable to read {nameof(SemVersion)} as property name.");
28
+ }
29
+
30
+ return value;
31
+ }
32
+
33
+ public override void Write(Utf8JsonWriter writer, SemVersion? value, JsonSerializerOptions options)
34
+ {
35
+ writer.WriteStringValue(value?.ToString());
36
+ }
37
+
38
+ public override void WriteAsPropertyName(Utf8JsonWriter writer, [DisallowNull] SemVersion value, JsonSerializerOptions options)
39
+ {
40
+ writer.WritePropertyName(value.ToString());
41
+ }
42
+ }
@@ -0,0 +1,16 @@
1
+ <Project Sdk="Microsoft.NET.Sdk">
2
+
3
+ <PropertyGroup>
4
+ <TargetFramework>$(CommonTargetFramework)</TargetFramework>
5
+ <OutputType>Exe</OutputType>
6
+ </PropertyGroup>
7
+
8
+ <ItemGroup>
9
+ <PackageReference Include="System.CommandLine" />
10
+ </ItemGroup>
11
+
12
+ <ItemGroup>
13
+ <ProjectReference Include="..\DotNetPackageCorrelation\DotNetPackageCorrelation.csproj" />
14
+ </ItemGroup>
15
+
16
+ </Project>
@@ -0,0 +1,32 @@
1
+ using System.CommandLine;
2
+ using System.Text.Json;
3
+
4
+ using DotNetPackageCorrelation;
5
+
6
+ namespace DotNetPackageCorrelation.Cli;
7
+
8
+ public class Program
9
+ {
10
+ public static async Task<int> Main(string[] args)
11
+ {
12
+ var coreLocationOption = new Option<DirectoryInfo>("--core-location", "The location of the .NET Core source code.") { IsRequired = true };
13
+ var outputOption = new Option<FileInfo>("--output", "The location to write the result.") { IsRequired = true };
14
+ var command = new Command("build")
15
+ {
16
+ coreLocationOption,
17
+ outputOption,
18
+ };
19
+ command.TreatUnmatchedTokensAsErrors = true;
20
+ command.SetHandler(async (coreLocationDirectory, output) =>
21
+ {
22
+ // the tool is expected to be given the path to the .NET Core repository, but the correlator only needs a specific subdirectory
23
+ var releaseNotesDirectory = new DirectoryInfo(Path.Combine(coreLocationDirectory.FullName, "release-notes"));
24
+ var correlator = new Correlator(releaseNotesDirectory);
25
+ var (sdkPackages, _warnings) = await correlator.RunAsync();
26
+ var json = JsonSerializer.Serialize(sdkPackages, Correlator.SerializerOptions);
27
+ await File.WriteAllTextAsync(output.FullName, json);
28
+ }, coreLocationOption, outputOption);
29
+ var exitCode = await command.InvokeAsync(args);
30
+ return exitCode;
31
+ }
32
+ }
@@ -0,0 +1,99 @@
1
+ using Semver;
2
+
3
+ using Xunit;
4
+
5
+ namespace DotNetPackageCorrelation.Tests;
6
+
7
+ public class CorrelatorTests
8
+ {
9
+ [Fact]
10
+ public async Task FileHandling_AllFilesShapedAppropriately()
11
+ {
12
+ // the JSON and markdown are shaped as expected
13
+ // we're able to determine from `Runtime.Package/8.0.0` that the corresponding version of `Some.Package` is `1.2.3`
14
+ var (packageMapper, warnings) = await PackageMapperFromFilesAsync(
15
+ ("8.0/releases.json", """
16
+ {
17
+ "releases": [
18
+ {
19
+ "sdk": {
20
+ "version": "8.0.100",
21
+ "runtime-version": "8.0.0"
22
+ }
23
+ }
24
+ ]
25
+ }
26
+ """),
27
+ ("8.0/8.0.0/8.0.0.md", """
28
+ Package name | Version
29
+ :-- | :--
30
+ Runtime.Package | 8.0.0
31
+ Some.Package | 1.2.3
32
+ """)
33
+ );
34
+ Assert.Empty(warnings);
35
+ AssertPackageVersion(packageMapper, "Runtime.Package", "8.0.0", "Some.Package", "1.2.3");
36
+ }
37
+
38
+ [Theory]
39
+ [InlineData("Some.Package | 1.2.3", "Some.Package", "1.2.3")] // happy path
40
+ [InlineData("Some.Package.1.2.3", "Some.Package", "1.2.3")] // looks like a restore directory
41
+ [InlineData("Some.Package | 1.2 | 1.2.3.nupkg", "Some.Package", "1.2.3")] // extra columns from a bad filename split
42
+ [InlineData("Some.Package | 1.2.3.nupkg", "Some.Package", "1.2.3")] // version contains package extension
43
+ [InlineData("Some.Package | 1.2.3.symbols.nupkg", "Some.Package", "1.2.3")] // version contains symbols package extension
44
+ [InlineData("some.package.1.2.3.nupkg", "some.package", "1.2.3")] // first column is a filename, second column is missing
45
+ [InlineData("some.package.1.2.3.nupkg |", "some.package", "1.2.3")] // first column is a filename, second column is empty
46
+ public void PackagesParsedFromMarkdown(string markdownLine, string expectedPackageName, string expectedPackageVersion)
47
+ {
48
+ var markdownContent = $"""
49
+ Package name | Version
50
+ :-- | :--
51
+ {markdownLine}
52
+ """;
53
+ var warnings = new List<string>();
54
+ var packages = Correlator.GetPackagesFromMarkdown("test.md", markdownContent, warnings);
55
+ Assert.Empty(warnings);
56
+ var actualpackage = Assert.Single(packages);
57
+ Assert.Equal(expectedPackageName, actualpackage.Name);
58
+ Assert.Equal(expectedPackageVersion, actualpackage.Version.ToString());
59
+ }
60
+
61
+ private static void AssertPackageVersion(PackageMapper packageMapper, string runtimePackageName, string runtimePackageVersion, string candidatePackageName, string? expectedPackageVersion)
62
+ {
63
+ var actualPackageVersion = packageMapper.GetPackageVersionThatShippedWithOtherPackage(runtimePackageName, SemVersion.Parse(runtimePackageVersion), candidatePackageName);
64
+ if (expectedPackageVersion is null)
65
+ {
66
+ Assert.Null(actualPackageVersion);
67
+ }
68
+ else
69
+ {
70
+ Assert.NotNull(actualPackageVersion);
71
+ Assert.Equal(expectedPackageVersion, actualPackageVersion.ToString());
72
+ }
73
+ }
74
+
75
+ private static async Task<(PackageMapper PackageMapper, IEnumerable<string> Warnings)> PackageMapperFromFilesAsync(params (string Path, string Content)[] files)
76
+ {
77
+ var testDirectory = Path.Combine(Path.GetDirectoryName(typeof(CorrelatorTests).Assembly.Location)!, "test-data", Guid.NewGuid().ToString("D"));
78
+ Directory.CreateDirectory(testDirectory);
79
+
80
+ try
81
+ {
82
+ foreach (var (path, content) in files)
83
+ {
84
+ var fullPath = Path.Combine(testDirectory, path);
85
+ Directory.CreateDirectory(Path.GetDirectoryName(fullPath)!);
86
+ await File.WriteAllTextAsync(fullPath, content);
87
+ }
88
+
89
+ var correlator = new Correlator(new DirectoryInfo(testDirectory));
90
+ var (runtimePackages, warnings) = await correlator.RunAsync();
91
+ var packageMapper = PackageMapper.Load(runtimePackages);
92
+ return (packageMapper, warnings);
93
+ }
94
+ finally
95
+ {
96
+ Directory.Delete(testDirectory, recursive: true);
97
+ }
98
+ }
99
+ }
@@ -0,0 +1,18 @@
1
+ <Project Sdk="Microsoft.NET.Sdk">
2
+
3
+ <PropertyGroup>
4
+ <TargetFramework>$(CommonTargetFramework)</TargetFramework>
5
+ <OutputType>Exe</OutputType>
6
+ </PropertyGroup>
7
+
8
+ <ItemGroup>
9
+ <PackageReference Include="Microsoft.NET.Test.Sdk" />
10
+ <PackageReference Include="xunit" />
11
+ <PackageReference Include="xunit.runner.visualstudio" />
12
+ </ItemGroup>
13
+
14
+ <ItemGroup>
15
+ <ProjectReference Include="..\DotNetPackageCorrelation\DotNetPackageCorrelation.csproj" />
16
+ </ItemGroup>
17
+
18
+ </Project>
@@ -0,0 +1,30 @@
1
+ using System.Runtime.CompilerServices;
2
+
3
+ using Semver;
4
+
5
+ using Xunit;
6
+
7
+ namespace DotNetPackageCorrelation.Tests;
8
+
9
+ public class EndToEndTests
10
+ {
11
+ [Fact]
12
+ public async Task IntegrationTest()
13
+ {
14
+ // arrange
15
+ var thisFileDirectory = Path.GetDirectoryName(GetThisFilePath())!;
16
+ var dotnetCoreDirectory = Path.Combine(thisFileDirectory, "..", "..", "dotnet-core");
17
+ var correlator = new Correlator(new DirectoryInfo(Path.Combine(dotnetCoreDirectory, "release-notes")));
18
+
19
+ // act
20
+ var (runtimePackages, _warnings) = await correlator.RunAsync();
21
+ var packageMapper = PackageMapper.Load(runtimePackages);
22
+
23
+ // assert
24
+ // 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
25
+ var systemTextJsonVersion = packageMapper.GetPackageVersionThatShippedWithOtherPackage("Microsoft.NETCore.App.Ref", SemVersion.Parse("8.0.8"), "System.Text.Json");
26
+ Assert.Equal("8.0.4", systemTextJsonVersion?.ToString());
27
+ }
28
+
29
+ private static string GetThisFilePath([CallerFilePath] string? path = null) => path ?? throw new ArgumentNullException(nameof(path));
30
+ }