dependabot-nuget 0.309.0 → 0.311.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/helpers/lib/NuGetUpdater/Directory.Packages.props +1 -1
  3. data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation.Test/DotNetPackageCorrelation.Test.csproj +1 -1
  4. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Run.cs +6 -6
  5. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/NuGetUpdater.Cli.Test.csproj +1 -1
  6. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/ClosePullRequest.cs +13 -0
  7. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/CreatePullRequest.cs +20 -0
  8. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/Job.cs +1 -2
  9. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/JobErrorBase.cs +19 -0
  10. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/MessageBase.cs +1 -0
  11. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/UpdatePullRequest.cs +16 -0
  12. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/HttpApiHandler.cs +12 -3
  13. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/PullRequestTextGenerator.cs +49 -13
  14. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/RunWorker.cs +31 -6
  15. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/UpdateOperationBase.cs +1 -2
  16. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/DependencyConflictResolver.cs +6 -33
  17. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +12 -1
  18. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/AnalyzeWorkerTests.cs +3 -3
  19. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.cs +2 -2
  20. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/MockNuGetPackage.cs +37 -2
  21. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/NuGetUpdater.Core.Test.csproj +1 -1
  22. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/EndToEndTests.cs +355 -0
  23. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/MessageReportTests.cs +232 -0
  24. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/PullRequestTextTests.cs +32 -8
  25. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/RunWorkerTests.cs +1212 -840
  26. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/SerializationTests.cs +4 -2
  27. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/TestHttpServer.cs +16 -6
  28. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateOperationBaseTests.cs +3 -3
  29. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackageReference.cs +1 -13
  30. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/EOLHandlingTests.cs +227 -13
  31. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/LoggerTests.cs +0 -1
  32. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/MSBuildHelperTests.cs +382 -165
  33. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/PathHelperTests.cs +1 -1
  34. data/helpers/lib/NuGetUpdater/global.json +1 -1
  35. metadata +11 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 77852ce632b0aebac74d328dc9b052115ac4dbe98aecb1db4e1cbc70b8fea2f6
4
- data.tar.gz: 1a651c11a375578cc87717817d7c6d168abff9521c130f38e7a8caf098ba73a2
3
+ metadata.gz: a32c81e06aafe3606a77d89ae351b246b6e80c9fd272bfe0001e6486eebf989b
4
+ data.tar.gz: 7796df4d53b7b86d9ea7472ff82fd4801dd7144d93b087fe32423dfb535c50e5
5
5
  SHA512:
6
- metadata.gz: cd1387d4a376c66e6a22cfc498ac0ff17485a8d27e8644e6566fcd60478f52446614aa2563dc7bc0c07dfe2dc92f5604bc37cc5f66e5da2e0a457981443e4226
7
- data.tar.gz: 230228595e7540a4de04d201b6d6cbd171ec73ef2259c20bd186d95652ed29477cebdced964ccecbde1bcdf481ce36819c0712f4ac8650b1595498931efdca00
6
+ metadata.gz: b487988ea2bc084a70c73216d9ab773be814a442c3a9e160374ae5bdefd03e9067ca9b7cc12d55725da0396e3e14af7bb1cb037742ac7f2aba7c9b65130cfb8a
7
+ data.tar.gz: 281290949ba2b0dccaff75e68aaeab2e886721cc19c79cf379ce5302cbe719f2fa7e1ec491720a30f3bda4617136c03e7b8e8a4dc20fef9c6632f79ed3c0a9ae
@@ -38,7 +38,7 @@
38
38
  <PackageVersion Include="System.Text.Json" Version="9.0.3" />
39
39
  <PackageVersion Include="System.Text.RegularExpressions" Version="4.3.1" />
40
40
  <PackageVersion Include="System.Threading.Tasks.Dataflow" Version="9.0.3" />
41
- <PackageVersion Include="xunit" Version="2.9.3" />
41
+ <PackageVersion Include="xunit.v3" Version="2.0.1" />
42
42
  <PackageVersion Include="xunit.runner.visualstudio" Version="3.0.2" />
43
43
  </ItemGroup>
44
44
  </Project>
@@ -7,7 +7,7 @@
7
7
 
8
8
  <ItemGroup>
9
9
  <PackageReference Include="Microsoft.NET.Test.Sdk" />
10
- <PackageReference Include="xunit" />
10
+ <PackageReference Include="xunit.v3" />
11
11
  <PackageReference Include="xunit.runner.visualstudio" />
12
12
  </ItemGroup>
13
13
 
@@ -50,10 +50,10 @@ public partial class EntryPointTests
50
50
  },
51
51
  expectedUrls:
52
52
  [
53
- "/update_jobs/TEST-ID/update_dependency_list",
54
- "/update_jobs/TEST-ID/increment_metric",
55
- "/update_jobs/TEST-ID/create_pull_request",
56
- "/update_jobs/TEST-ID/mark_as_processed",
53
+ "POST /update_jobs/TEST-ID/update_dependency_list",
54
+ "POST /update_jobs/TEST-ID/increment_metric",
55
+ "POST /update_jobs/TEST-ID/create_pull_request",
56
+ "PATCH /update_jobs/TEST-ID/mark_as_processed",
57
57
  ]
58
58
  );
59
59
  }
@@ -79,9 +79,9 @@ public partial class EntryPointTests
79
79
  await UpdateWorkerTestBase.MockNuGetPackagesInDirectory(packages, tempDirectory.DirectoryPath);
80
80
 
81
81
  var actualUrls = new List<string>();
82
- using var http = TestHttpServer.CreateTestStringServer(url =>
82
+ using var http = TestHttpServer.CreateTestStringServer((method, url) =>
83
83
  {
84
- actualUrls.Add(new Uri(url).PathAndQuery);
84
+ actualUrls.Add($"{method} {new Uri(url).PathAndQuery}");
85
85
  return (200, "ok");
86
86
  });
87
87
  var args = new List<string>()
@@ -13,7 +13,7 @@
13
13
 
14
14
  <ItemGroup>
15
15
  <PackageReference Include="Microsoft.NET.Test.Sdk" />
16
- <PackageReference Include="xunit" />
16
+ <PackageReference Include="xunit.v3" />
17
17
  <PackageReference Include="xunit.runner.visualstudio" />
18
18
  </ItemGroup>
19
19
 
@@ -1,4 +1,5 @@
1
1
  using System.Collections.Immutable;
2
+ using System.Text;
2
3
  using System.Text.Json.Serialization;
3
4
 
4
5
  namespace NuGetUpdater.Core.Run.ApiModel;
@@ -9,4 +10,16 @@ public sealed record ClosePullRequest : MessageBase
9
10
  public required ImmutableArray<string> DependencyNames { get; init; }
10
11
 
11
12
  public string Reason { get; init; } = "up_to_date";
13
+
14
+ public override string GetReport()
15
+ {
16
+ var report = new StringBuilder();
17
+ report.AppendLine($"{nameof(ClosePullRequest)}: {Reason}");
18
+ foreach (var dependencyName in DependencyNames)
19
+ {
20
+ report.AppendLine($"- {dependencyName}");
21
+ }
22
+
23
+ return report.ToString().Trim();
24
+ }
12
25
  }
@@ -1,5 +1,8 @@
1
+ using System.Text;
1
2
  using System.Text.Json.Serialization;
2
3
 
4
+ using NuGet.Versioning;
5
+
3
6
  namespace NuGetUpdater.Core.Run.ApiModel;
4
7
 
5
8
  public sealed record CreatePullRequest : MessageBase
@@ -15,4 +18,21 @@ public sealed record CreatePullRequest : MessageBase
15
18
  public required string PrTitle { get; init; }
16
19
  [JsonPropertyName("pr-body")]
17
20
  public required string PrBody { get; init; }
21
+
22
+ public override string GetReport()
23
+ {
24
+ var dependencyNames = Dependencies
25
+ .OrderBy(d => d.Name, StringComparer.OrdinalIgnoreCase)
26
+ .ThenBy(d => NuGetVersion.Parse(d.Version!))
27
+ .Select(d => $"{d.Name}/{d.Version}")
28
+ .ToArray();
29
+ var report = new StringBuilder();
30
+ report.AppendLine(nameof(CreatePullRequest));
31
+ foreach (var d in dependencyNames)
32
+ {
33
+ report.AppendLine($"- {d}");
34
+ }
35
+
36
+ return report.ToString().Trim();
37
+ }
18
38
  }
@@ -2,7 +2,6 @@ using System.Collections.Immutable;
2
2
  using System.Text.Json;
3
3
  using System.Text.Json.Serialization;
4
4
 
5
- using NuGet.Credentials;
6
5
  using NuGet.Versioning;
7
6
 
8
7
  namespace NuGetUpdater.Core.Run.ApiModel;
@@ -32,7 +31,7 @@ public sealed record Job
32
31
  public bool RejectExternalCode { get; init; } = false;
33
32
  public bool RepoPrivate { get; init; } = false;
34
33
  public CommitOptions? CommitMessageOptions { get; init; } = null;
35
- public ImmutableArray<Dictionary<string, string>>? CredentialsMetadata { get; init; } = null;
34
+ public ImmutableArray<Dictionary<string, object>>? CredentialsMetadata { get; init; } = null;
36
35
  public int MaxUpdaterRunTime { get; init; } = 0;
37
36
 
38
37
  public IEnumerable<string> GetAllDirectories()
@@ -1,4 +1,5 @@
1
1
  using System.Net;
2
+ using System.Text;
2
3
  using System.Text.Json.Serialization;
3
4
 
4
5
  using Microsoft.Build.Exceptions;
@@ -20,6 +21,24 @@ public abstract record JobErrorBase : MessageBase
20
21
  [JsonPropertyName("error-details")]
21
22
  public Dictionary<string, object> Details { get; init; } = new();
22
23
 
24
+ public override string GetReport()
25
+ {
26
+ var report = new StringBuilder();
27
+ report.AppendLine($"Error type: {Type}");
28
+ foreach (var (key, value) in Details)
29
+ {
30
+ var valueString = value.ToString();
31
+ if (value is IEnumerable<string> strings)
32
+ {
33
+ valueString = string.Join(", ", strings);
34
+ }
35
+
36
+ report.AppendLine($"- {key}: {valueString}");
37
+ }
38
+
39
+ return report.ToString().Trim();
40
+ }
41
+
23
42
  public static JobErrorBase ErrorFromException(Exception ex, string jobId, string currentDirectory)
24
43
  {
25
44
  return ex switch
@@ -2,4 +2,5 @@ namespace NuGetUpdater.Core.Run.ApiModel;
2
2
 
3
3
  public abstract record MessageBase
4
4
  {
5
+ public abstract string GetReport();
5
6
  }
@@ -1,4 +1,5 @@
1
1
  using System.Collections.Immutable;
2
+ using System.Text;
2
3
  using System.Text.Json.Serialization;
3
4
 
4
5
  namespace NuGetUpdater.Core.Run.ApiModel;
@@ -25,4 +26,19 @@ public sealed record UpdatePullRequest : MessageBase
25
26
 
26
27
  [JsonPropertyName("dependency-group")]
27
28
  public required string? DependencyGroup { get; init; }
29
+
30
+ public override string GetReport()
31
+ {
32
+ var dependencyNames = DependencyNames
33
+ .Order(StringComparer.OrdinalIgnoreCase)
34
+ .ToArray();
35
+ var report = new StringBuilder();
36
+ report.AppendLine(nameof(UpdatePullRequest));
37
+ foreach (var d in dependencyNames)
38
+ {
39
+ report.AppendLine($"- {d}");
40
+ }
41
+
42
+ return report.ToString().Trim();
43
+ }
28
44
  }
@@ -57,7 +57,7 @@ public class HttpApiHandler : IApiHandler
57
57
 
58
58
  public async Task MarkAsProcessed(MarkAsProcessed markAsProcessed)
59
59
  {
60
- await PostAsJson("mark_as_processed", markAsProcessed);
60
+ await PatchAsJson("mark_as_processed", markAsProcessed);
61
61
  }
62
62
 
63
63
  internal static string Serialize(object body)
@@ -70,11 +70,20 @@ public class HttpApiHandler : IApiHandler
70
70
  return payload;
71
71
  }
72
72
 
73
- private async Task PostAsJson(string endpoint, object body)
73
+ private Task PostAsJson(string endpoint, object body) => SendAsJson(endpoint, body, "POST");
74
+ private Task PatchAsJson(string endpoint, object body) => SendAsJson(endpoint, body, "PATCH");
75
+
76
+ private async Task SendAsJson(string endpoint, object body, string method)
74
77
  {
78
+ var uri = $"{_apiUrl}/update_jobs/{_jobId}/{endpoint}";
75
79
  var payload = Serialize(body);
76
80
  var content = new StringContent(payload, Encoding.UTF8, "application/json");
77
- var response = await HttpClient.PostAsync($"{_apiUrl}/update_jobs/{_jobId}/{endpoint}", content);
81
+ var httpMethod = new HttpMethod(method);
82
+ var message = new HttpRequestMessage(httpMethod, uri)
83
+ {
84
+ Content = content
85
+ };
86
+ var response = await HttpClient.SendAsync(message);
78
87
  var _ = response.EnsureSuccessStatusCode();
79
88
  }
80
89
  }
@@ -1,5 +1,7 @@
1
1
  using System.Collections.Immutable;
2
2
 
3
+ using NuGet.Versioning;
4
+
3
5
  using NuGetUpdater.Core.Run.ApiModel;
4
6
  using NuGetUpdater.Core.Updater;
5
7
 
@@ -7,34 +9,68 @@ namespace NuGetUpdater.Core.Run;
7
9
 
8
10
  public class PullRequestTextGenerator
9
11
  {
12
+ private const int MaxTitleLength = 70;
13
+
10
14
  public static string GetPullRequestTitle(Job job, ImmutableArray<UpdateOperationBase> updateOperationsPerformed, string? dependencyGroupName)
11
15
  {
12
16
  // simple version looks like
13
17
  // Update Some.Package to 1.2.3
14
18
  // if multiple packages are updated to multiple versions, result looks like:
15
19
  // Update Package.A to 1.0.0, 2.0.0; Package.B to 3.0.0, 4.0.0
16
- var dependencySets = updateOperationsPerformed
17
- .GroupBy(d => d.DependencyName, StringComparer.OrdinalIgnoreCase)
18
- .OrderBy(g => g.Key, StringComparer.OrdinalIgnoreCase)
19
- .Select(g => new
20
- {
21
- Name = g.Key,
22
- Versions = g
23
- .Select(d => d.NewVersion)
24
- .OrderBy(v => v)
25
- .ToArray()
26
- })
27
- .ToArray();
20
+ var dependencySets = GetDependencySets(updateOperationsPerformed);
28
21
  var updatedPartTitles = dependencySets
29
22
  .Select(d => $"{d.Name} to {string.Join(", ", d.Versions.Select(v => v.ToString()))}")
30
23
  .ToArray();
31
24
  var title = $"{job.CommitMessageOptions?.Prefix}Update {string.Join("; ", updatedPartTitles)}";
25
+
26
+ // don't let the title get too long
27
+ if (title.Length > MaxTitleLength && updatedPartTitles.Length >= 3)
28
+ {
29
+ title = $"{job.CommitMessageOptions?.Prefix}Update {dependencySets[0].Name} and {dependencySets.Length - 1} other dependencies";
30
+ }
31
+
32
32
  return title;
33
33
  }
34
34
 
35
35
  public static string GetPullRequestCommitMessage(Job job, ImmutableArray<UpdateOperationBase> updateOperationsPerformed, string? dependencyGroupName)
36
36
  {
37
- return GetPullRequestTitle(job, updateOperationsPerformed, dependencyGroupName);
37
+ // updating a single dependency looks like
38
+ // Update Some.Package to 1.2.3
39
+ // if multiple packages are updated, result looks like:
40
+ // Update:
41
+ // - Package.A to 1.0.0
42
+ // - Package.B to 2.0.0
43
+ var dependencySets = GetDependencySets(updateOperationsPerformed);
44
+ if (dependencySets.Length == 1)
45
+ {
46
+ var depName = dependencySets[0].Name;
47
+ var depVersions = dependencySets[0].Versions.Select(v => v.ToString());
48
+ return $"Update {dependencySets[0].Name} to {string.Join(", ", depVersions)}";
49
+ }
50
+
51
+ var updatedParts = dependencySets
52
+ .Select(d => $"- {d.Name} to {string.Join(", ", d.Versions.Select(v => v.ToString()))}")
53
+ .ToArray();
54
+ var message = string.Join("\n", ["Update:", .. updatedParts]);
55
+ return message;
56
+ }
57
+
58
+ private static (string Name, NuGetVersion[] Versions)[] GetDependencySets(ImmutableArray<UpdateOperationBase> updateOperationsPerformed)
59
+ {
60
+ var dependencySets = updateOperationsPerformed
61
+ .GroupBy(d => d.DependencyName, StringComparer.OrdinalIgnoreCase)
62
+ .OrderBy(g => g.Key, StringComparer.OrdinalIgnoreCase)
63
+ .Select(g =>
64
+ {
65
+ var name = g.Key;
66
+ var versions = g
67
+ .Select(d => d.NewVersion)
68
+ .OrderBy(v => v)
69
+ .ToArray();
70
+ return (name, versions);
71
+ })
72
+ .ToArray();
73
+ return dependencySets;
38
74
  }
39
75
 
40
76
  public static string GetPullRequestBody(Job job, ImmutableArray<UpdateOperationBase> updateOperationsPerformed, string? dependencyGroupName)
@@ -113,7 +113,16 @@ public class RunWorker
113
113
  _logger.Info("Discovery JSON content:");
114
114
  _logger.Info(JsonSerializer.Serialize(discoveryResult, DiscoveryWorker.SerializerOptions));
115
115
 
116
- // TODO: report errors
116
+ if (discoveryResult.Error is not null)
117
+ {
118
+ // this is unrecoverable
119
+ await _apiHandler.RecordUpdateJobError(discoveryResult.Error);
120
+ return new()
121
+ {
122
+ Base64DependencyFiles = [],
123
+ BaseCommitSha = baseCommitSha,
124
+ };
125
+ }
117
126
 
118
127
  // report dependencies
119
128
  var discoveredUpdatedDependencies = GetUpdatedDependencyListFromDiscovery(discoveryResult, repoContentsPath.FullName);
@@ -201,7 +210,15 @@ public class RunWorker
201
210
 
202
211
  var dependencyInfo = GetDependencyInfo(job, dependency);
203
212
  var analysisResult = await _analyzeWorker.RunAsync(repoContentsPath.FullName, discoveryResult, dependencyInfo);
204
- // TODO: log analysisResult
213
+ _logger.Info("Analysis content:");
214
+ _logger.Info(JsonSerializer.Serialize(analysisResult, AnalyzeWorker.SerializerOptions));
215
+
216
+ if (analysisResult.Error is not null)
217
+ {
218
+ await _apiHandler.RecordUpdateJobError(analysisResult.Error);
219
+ continue;
220
+ }
221
+
205
222
  if (analysisResult.CanUpdate)
206
223
  {
207
224
  if (!job.UpdatingAPullRequest)
@@ -240,8 +257,11 @@ public class RunWorker
240
257
  };
241
258
 
242
259
  var updateResult = await _updaterWorker.RunAsync(repoContentsPath.FullName, updateOperation.ProjectPath, dependency.Name, dependency.Version!, analysisResult.UpdatedVersion, isTransitive: dependency.IsTransitive);
243
- // TODO: need to report if anything was actually updated
244
- if (updateResult.Error is null)
260
+ if (updateResult.Error is not null)
261
+ {
262
+ await _apiHandler.RecordUpdateJobError(updateResult.Error);
263
+ }
264
+ else
245
265
  {
246
266
  actualUpdatedDependencies.Add(updatedDependency);
247
267
  }
@@ -365,10 +385,15 @@ public class RunWorker
365
385
 
366
386
  private async Task SendApiMessage(MessageBase? message)
367
387
  {
388
+ if (message is null)
389
+ {
390
+ return;
391
+ }
392
+
393
+ var report = message.GetReport();
394
+ _logger.Info(report);
368
395
  switch (message)
369
396
  {
370
- case null:
371
- break;
372
397
  case JobErrorBase error:
373
398
  await _apiHandler.RecordUpdateJobError(error);
374
399
  break;
@@ -29,8 +29,7 @@ public abstract record UpdateOperationBase
29
29
  return string.Empty;
30
30
  }
31
31
 
32
- var separator = "\n ";
33
- var report = $"Performed the following updates:{separator}{string.Join(separator, updateMessages.Select(m => $"- {m}"))}";
32
+ var report = $"Performed the following updates:\n{string.Join("\n", updateMessages.Select(m => $"- {m}"))}";
34
33
  return report;
35
34
  }
36
35
 
@@ -465,19 +465,8 @@ public class PackageManager
465
465
  NuGetVersion latestVersion = versions.Where(v => !v.IsPrerelease).Max();
466
466
 
467
467
  // Loop from the current version to the latest version, use next patch as a limit (unless there's a limit) so it doesn't look for versions that don't exist
468
- for (NuGetVersion version = currentVersionParent; version <= latestVersion; version = NextPatch(version, versions))
468
+ for (NuGetVersion? version = currentVersionParent; version is not null && version <= latestVersion; version = NextPatch(version, versions))
469
469
  {
470
- NuGetVersion nextPatch = NextPatch(version, versions);
471
-
472
- // If the next patch is the same as the currentVersioon, then the update is a Success
473
- if (nextPatch == version)
474
- {
475
- return "Success";
476
- }
477
-
478
- string parentVersion = version.ToString();
479
- parent.NewVersion = parentVersion;
480
-
481
470
  // Check if the parent needs to be updated since the child isn't in the existing package list and the parent can update to a newer version to remove the dependency
482
471
  List<PackageToUpdate> dependencyListParentTemp = await GetDependenciesAsync(parent, targetFramework, projectDirectory, logger);
483
472
  PackageToUpdate parentDependencyTemp = dependencyListParentTemp.FirstOrDefault(p => string.Compare(p.PackageName, package.PackageName, StringComparison.OrdinalIgnoreCase) == 0);
@@ -485,7 +474,7 @@ public class PackageManager
485
474
  // If the newer package version of the parent has the same version as the parent's previous dependency, update
486
475
  if (parentDependencyTemp.CurrentVersion == package.CurrentVersion)
487
476
  {
488
- parent.NewVersion = parentVersion;
477
+ parent.NewVersion = version.ToString();
489
478
  parent.CurrentVersion = null;
490
479
  await UpdateVersion(existingPackages, parent, targetFramework, projectDirectory, logger);
491
480
  package.IsSpecific = true;
@@ -542,18 +531,10 @@ public class PackageManager
542
531
  }
543
532
 
544
533
  // Method to update a version to the next available version for a package
545
- public NuGetVersion NextPatch(NuGetVersion version, IEnumerable<NuGetVersion> allVersions)
534
+ private static NuGetVersion? NextPatch(NuGetVersion version, IEnumerable<NuGetVersion> allVersions)
546
535
  {
547
- var versions = allVersions.Where(v => v > version);
548
-
549
- if (!versions.Any())
550
- {
551
- // If there are no greater versions, return current version
552
- return version;
553
- }
554
-
555
- // Find smallest version in the versions
556
- return versions.Min();
536
+ var candidateVersions = allVersions.Where(v => v > version).ToArray();
537
+ return candidateVersions.Min();
557
538
  }
558
539
 
559
540
  // Method to find a compatible version with the child for the parent to update to
@@ -596,18 +577,10 @@ public class PackageManager
596
577
  }
597
578
 
598
579
  // Loop from the current version to the latest version, use next patch as a limit (unless there's a limit) so it doesn't look for versions that don't exist
599
- for (NuGetVersion version = CurrentVersion; version <= latestVersion; version = NextPatch(version, versions))
580
+ for (NuGetVersion? version = CurrentVersion; version is not null && version <= latestVersion; version = NextPatch(version, versions))
600
581
  {
601
582
  possibleParent.NewVersion = version.ToString();
602
583
 
603
- NuGetVersion nextPatch = NextPatch(version, versions);
604
-
605
- // If the next patch is the same as the CurrentVersion, then nothing is needed
606
- if (nextPatch == version)
607
- {
608
- return nextPatch;
609
- }
610
-
611
584
  // Check if there's compatibility with parent and dependency
612
585
  if (await IsCompatibleAsync(possibleParent, possibleDependency, targetFramework, nugetContext.CurrentDirectory, logger))
613
586
  {
@@ -1033,7 +1033,18 @@ internal static partial class MSBuildHelper
1033
1033
  var matches = patterns.Select(p => p.Match(output)).Where(m => m.Success);
1034
1034
  if (matches.Any())
1035
1035
  {
1036
- var packages = matches.Select(m => m.Groups["PackageName"].Value).Distinct(StringComparer.OrdinalIgnoreCase).ToArray();
1036
+ var packages = matches.Select(m =>
1037
+ {
1038
+ var packageName = m.Groups["PackageName"].Value;
1039
+ if (m.Groups.TryGetValue("PackageVersion", out var versionGroup))
1040
+ {
1041
+ packageName = $"{packageName}/{versionGroup.Value}";
1042
+ }
1043
+
1044
+ return packageName;
1045
+ })
1046
+ .Distinct(StringComparer.OrdinalIgnoreCase)
1047
+ .ToArray();
1037
1048
  throw new DependencyNotFoundException(packages);
1038
1049
  }
1039
1050
  }
@@ -702,7 +702,7 @@ public partial class AnalyzeWorkerTests : AnalyzeWorkerTestBase
702
702
 
703
703
  // nothing else is found
704
704
  return (404, Encoding.UTF8.GetBytes("{}"));
705
- };
705
+ }
706
706
  }
707
707
  using var http1 = TestHttpServer.CreateTestServer(TestHttpHandler1);
708
708
  using var http2 = TestHttpServer.CreateTestServer(TestHttpHandler2);
@@ -874,7 +874,7 @@ public partial class AnalyzeWorkerTests : AnalyzeWorkerTestBase
874
874
 
875
875
  // nothing else is found
876
876
  return (404, Encoding.UTF8.GetBytes("{}"));
877
- };
877
+ }
878
878
  }
879
879
  using var http1 = TestHttpServer.CreateTestServer(TestHttpHandler1);
880
880
  using var http2 = TestHttpServer.CreateTestServer(TestHttpHandler2);
@@ -1064,7 +1064,7 @@ public partial class AnalyzeWorkerTests : AnalyzeWorkerTestBase
1064
1064
 
1065
1065
  // nothing else is found
1066
1066
  return (404, Encoding.UTF8.GetBytes("{}"));
1067
- };
1067
+ }
1068
1068
  }
1069
1069
  using var http = TestHttpServer.CreateTestServer(TestHttpHandler);
1070
1070
  await TestAnalyzeAsync(
@@ -27,7 +27,7 @@ public partial class DiscoveryWorkerTests : DiscoveryWorkerTestBase
27
27
  {
28
28
  // this package ships with the SDK and is automatically added for F# projects but should be manually added here to make the test consistent
29
29
  // only direct package discovery finds this, though
30
- expectedDependencies.Add(new Dependency("FSharp.Core", "9.0.100", DependencyType.PackageReference, TargetFrameworks: ["net8.0"], IsDirect: true));
30
+ expectedDependencies.Add(new Dependency("FSharp.Core", MockNuGetPackage.FSharpCorePackageVersion.Value, DependencyType.PackageReference, TargetFrameworks: ["net8.0"], IsDirect: true));
31
31
  }
32
32
 
33
33
  var experimentsManager = new ExperimentsManager() { UseDirectDiscovery = useDirectDiscovery };
@@ -1559,7 +1559,7 @@ public partial class DiscoveryWorkerTests : DiscoveryWorkerTestBase
1559
1559
  {
1560
1560
  Path = "",
1561
1561
  Projects = [],
1562
- Error = new DependencyNotFound("Transitive.Dependency"),
1562
+ Error = new DependencyNotFound("Transitive.Dependency/>= 4.5.6"),
1563
1563
  }
1564
1564
  );
1565
1565
  }
@@ -346,6 +346,15 @@ namespace NuGetUpdater.Core.Test
346
346
  }
347
347
  });
348
348
 
349
+ public static readonly Lazy<string> FSharpCorePackageVersion = new(() =>
350
+ {
351
+ var fsharpPropsPath = Path.Combine(Path.GetDirectoryName(BundledVersionsPropsPath.Value)!, "FSharp", "Microsoft.FSharp.Core.NetSdk.props");
352
+ var fsharpPropsDocument = XDocument.Load(fsharpPropsPath);
353
+ var fsharpCoreVersionElement = fsharpPropsDocument.XPathSelectElement("//*[name()='FSCorePackageVersion']")!;
354
+ var fsharpCoreVersion = fsharpCoreVersionElement.Value;
355
+ return fsharpCoreVersion;
356
+ });
357
+
349
358
  private static readonly Dictionary<string, MockNuGetPackage> WellKnownPackages = new();
350
359
  public static MockNuGetPackage WellKnownReferencePackage(string packageName, string targetFramework, (string Path, byte[] Content)[]? files = null)
351
360
  {
@@ -370,7 +379,7 @@ namespace NuGetUpdater.Core.Test
370
379
  }
371
380
 
372
381
  string expectedVersion = matchingFrameworkElement.Attribute("TargetingPackVersion")!.Value;
373
- return new(
382
+ WellKnownPackages[key] = new MockNuGetPackage(
374
383
  $"{packageName}.Ref",
375
384
  expectedVersion,
376
385
  AdditionalMetadata:
@@ -440,6 +449,33 @@ namespace NuGetUpdater.Core.Test
440
449
  return WellKnownPackages[key];
441
450
  }
442
451
 
452
+ public static MockNuGetPackage WellKnownWindowsSdkRefPackage(string windowsSdkVersion)
453
+ {
454
+ var packageName = "Microsoft.Windows.SDK.NET.Ref";
455
+ var key = $"{packageName}/{windowsSdkVersion}";
456
+ if (!WellKnownPackages.ContainsKey(key))
457
+ {
458
+ var propsDocument = XDocument.Load(BundledVersionsPropsPath.Value);
459
+ var sdkTpmElement = propsDocument.XPathSelectElement($"/Project/ItemGroup/WindowsSdkSupportedTargetPlatformVersion[@Include='{windowsSdkVersion}']")!;
460
+ var packageVersion = sdkTpmElement.Attribute("WindowsSdkPackageVersion")!.Value!;
461
+ var package = new MockNuGetPackage(packageName, packageVersion, Files: [
462
+ ("data/FrameworkList.xml", Encoding.UTF8.GetBytes("""
463
+ <FileList Name="Windows SDK .NET 6.0">
464
+ <!-- contents omitted -->
465
+ </FileList>
466
+ """)),
467
+ ("data/RuntimeList.xml", Encoding.UTF8.GetBytes("""
468
+ <FileList Name="Windows SDK .NET 6.0" TargetFrameworkIdentifier=".NETCoreApp" TargetFrameworkVersion="6.0" FrameworkName="Microsoft.Windows.SDK.NET.Ref">
469
+ <!-- contents omitted -->
470
+ </FileList>
471
+ """))
472
+ ]);
473
+ WellKnownPackages[key] = package;
474
+ }
475
+
476
+ return WellKnownPackages[key];
477
+ }
478
+
443
479
  public static MockNuGetPackage[] CommonPackages { get; } =
444
480
  [
445
481
  CreateSimplePackage("NETStandard.Library", "2.0.3", "netstandard2.0"),
@@ -456,7 +492,6 @@ namespace NuGetUpdater.Core.Test
456
492
  WellKnownReferencePackage("Microsoft.WindowsDesktop.App", "net7.0"),
457
493
  WellKnownReferencePackage("Microsoft.WindowsDesktop.App", "net8.0"),
458
494
  WellKnownReferencePackage("Microsoft.WindowsDesktop.App", "net9.0"),
459
- WellKnownHostPackage("Microsoft.NETCore.App", "net8.0"),
460
495
  ];
461
496
  }
462
497
  }
@@ -13,7 +13,7 @@
13
13
  <ItemGroup>
14
14
  <PackageReference Include="Microsoft.CodeAnalysis.CSharp" />
15
15
  <PackageReference Include="Microsoft.NET.Test.Sdk" />
16
- <PackageReference Include="xunit" />
16
+ <PackageReference Include="xunit.v3" />
17
17
  <PackageReference Include="xunit.runner.visualstudio" />
18
18
  </ItemGroup>
19
19