dependabot-nuget 0.311.0 → 0.312.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.
- checksums.yaml +4 -4
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Run.cs +2 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/VersionFinder.cs +7 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/BadResponseException.cs +12 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/SdkProjectDiscovery.cs +121 -9
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/JobErrorBase.cs +7 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/UnknownError.cs +12 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/HttpApiHandler.cs +19 -50
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/IApiHandler.cs +33 -7
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/RunWorker.cs +10 -5
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackageReferenceUpdater.cs +13 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +1 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/VersionFinderTests.cs +74 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Clone/CloneWorkerTests.cs +1 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.Project.cs +3 -9
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.cs +1 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/SdkProjectDiscoveryTests.cs +58 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/HttpApiHandlerTests.cs +116 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/MessageReportTests.cs +1 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/RunWorkerTests.cs +227 -6
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/SerializationTests.cs +40 -11
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/TestApiHandler.cs +2 -39
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/TestHttpServer.cs +9 -5
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/PackageReferenceUpdaterTests.cs +99 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/MSBuildHelperTests.cs +8 -0
- metadata +25 -23
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: acb9d775c29330f4cae905aa23b7ad9c36f0ed161d8cc1c2fbfc7391fb52646b
|
4
|
+
data.tar.gz: 97b60783a35914969fe10efc5235a91d29a470faf40e4520cf331bf6bafe21f1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d1d117e611b42497834c3276f08e35e3a70a3b2d0f960ac3d666dde3c12eb32697f911e0af3409e76500977f4c1db835c4979a85879e86bbd00e0ec0df4cc34c
|
7
|
+
data.tar.gz: b3f8f76bc8be51fc23493c1660cb41e5f6b9c10721941bd98c72c49652c807bb320def722cdc3c08d822f2cd2ed0dbe993b75cd6d43d0f1593de6890a753af32
|
@@ -58,7 +58,7 @@ public partial class EntryPointTests
|
|
58
58
|
);
|
59
59
|
}
|
60
60
|
|
61
|
-
private static async Task RunAsync(TestFile[] files, Job job, string[] expectedUrls, MockNuGetPackage[]? packages = null)
|
61
|
+
private static async Task RunAsync(TestFile[] files, Job job, string[] expectedUrls, MockNuGetPackage[]? packages = null, string? repoContentsPath = null)
|
62
62
|
{
|
63
63
|
using var tempDirectory = new TemporaryDirectory();
|
64
64
|
|
@@ -90,7 +90,7 @@ public partial class EntryPointTests
|
|
90
90
|
"--job-path",
|
91
91
|
jobPath,
|
92
92
|
"--repo-contents-path",
|
93
|
-
tempDirectory.DirectoryPath,
|
93
|
+
repoContentsPath ?? tempDirectory.DirectoryPath,
|
94
94
|
"--api-url",
|
95
95
|
http.BaseUrl,
|
96
96
|
"--job-id",
|
@@ -1,5 +1,7 @@
|
|
1
1
|
using System.Collections.Immutable;
|
2
2
|
|
3
|
+
using Newtonsoft.Json;
|
4
|
+
|
3
5
|
using NuGet.Common;
|
4
6
|
using NuGet.Configuration;
|
5
7
|
using NuGet.Frameworks;
|
@@ -98,6 +100,11 @@ internal static class VersionFinder
|
|
98
100
|
// if anything goes wrong here, the package source obviously doesn't contain the requested package
|
99
101
|
continue;
|
100
102
|
}
|
103
|
+
catch (JsonReaderException ex)
|
104
|
+
{
|
105
|
+
// unable to parse server response
|
106
|
+
throw new BadResponseException(ex.Message, source.Source);
|
107
|
+
}
|
101
108
|
|
102
109
|
var feedVersions = (await feed.GetVersions(
|
103
110
|
packageId,
|
@@ -1,9 +1,11 @@
|
|
1
1
|
using System.Collections.Immutable;
|
2
|
+
using System.Text.Json;
|
2
3
|
using System.Xml.Linq;
|
3
4
|
using System.Xml.XPath;
|
4
5
|
|
5
6
|
using Microsoft.Build.Logging.StructuredLogger;
|
6
7
|
|
8
|
+
using NuGet.Frameworks;
|
7
9
|
using NuGet.Versioning;
|
8
10
|
|
9
11
|
using NuGetUpdater.Core.Utilities;
|
@@ -39,6 +41,8 @@ internal static class SdkProjectDiscovery
|
|
39
41
|
// these packages are resolved during restore, but aren't really updatable and shouldn't be reported as dependencies
|
40
42
|
private static readonly HashSet<string> NonReportedPackgeNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
41
43
|
{
|
44
|
+
"Microsoft.NETCore.Platforms",
|
45
|
+
"Microsoft.NETCore.Targets",
|
42
46
|
"NETStandard.Library"
|
43
47
|
};
|
44
48
|
|
@@ -84,6 +88,9 @@ internal static class SdkProjectDiscovery
|
|
84
88
|
Dictionary<string, Dictionary<string, Dictionary<string, string>>> packagesReplacedBySdkPerProject = new(PathComparer.Instance);
|
85
89
|
// projectPath tfm packageName packageVersion
|
86
90
|
|
91
|
+
Dictionary<string, Dictionary<string, HashSet<string>>> packageDependencies = new(PathComparer.Instance);
|
92
|
+
// projectPath tfm packageNames
|
93
|
+
|
87
94
|
Dictionary<string, Dictionary<string, string>> resolvedProperties = new(PathComparer.Instance);
|
88
95
|
// projectPath propertyName propertyValue
|
89
96
|
|
@@ -228,6 +235,28 @@ internal static class SdkProjectDiscovery
|
|
228
235
|
}
|
229
236
|
}
|
230
237
|
}
|
238
|
+
|
239
|
+
// track all referenced projects in case they have no assemblies and can't be otherwise reported
|
240
|
+
if (addItem.Name.Equals("PackageDependencies", StringComparison.OrdinalIgnoreCase))
|
241
|
+
{
|
242
|
+
var projectEvaluation = GetNearestProjectEvaluation(node);
|
243
|
+
if (projectEvaluation is not null)
|
244
|
+
{
|
245
|
+
var specificPackageDeps = packageDependencies.GetOrAdd(projectEvaluation.ProjectFile, () => new(StringComparer.OrdinalIgnoreCase));
|
246
|
+
var tfm = GetPropertyValueFromProjectEvaluation(projectEvaluation, "TargetFramework");
|
247
|
+
if (tfm is not null)
|
248
|
+
{
|
249
|
+
var packagesByTfm = specificPackageDeps.GetOrAdd(tfm, () => new(StringComparer.OrdinalIgnoreCase));
|
250
|
+
foreach (var package in addItem.Children.OfType<Item>())
|
251
|
+
{
|
252
|
+
if (!NonReportedPackgeNames.Contains(package.Name))
|
253
|
+
{
|
254
|
+
packagesByTfm.Add(package.Name);
|
255
|
+
}
|
256
|
+
}
|
257
|
+
}
|
258
|
+
}
|
259
|
+
}
|
231
260
|
}
|
232
261
|
break;
|
233
262
|
case Target target when target.Name == "_HandlePackageFileConflicts":
|
@@ -330,7 +359,7 @@ internal static class SdkProjectDiscovery
|
|
330
359
|
}
|
331
360
|
|
332
361
|
// and done
|
333
|
-
var projectDiscoveryResults = BuildResults(
|
362
|
+
var projectDiscoveryResults = await BuildResults(
|
334
363
|
repoRootPath,
|
335
364
|
workspacePath,
|
336
365
|
packagesPerProject,
|
@@ -338,6 +367,7 @@ internal static class SdkProjectDiscovery
|
|
338
367
|
packagesReplacedBySdkPerProject,
|
339
368
|
topLevelPackagesPerProject,
|
340
369
|
resolvedProperties,
|
370
|
+
packageDependencies,
|
341
371
|
referencedProjects,
|
342
372
|
importedFiles,
|
343
373
|
additionalFiles
|
@@ -345,7 +375,7 @@ internal static class SdkProjectDiscovery
|
|
345
375
|
return projectDiscoveryResults;
|
346
376
|
}
|
347
377
|
|
348
|
-
private static ImmutableArray<ProjectDiscoveryResult
|
378
|
+
private static async Task<ImmutableArray<ProjectDiscoveryResult>> BuildResults(
|
349
379
|
string repoRootPath,
|
350
380
|
string workspacePath,
|
351
381
|
Dictionary<string, Dictionary<string, Dictionary<string, string>>> packagesPerProject,
|
@@ -353,13 +383,14 @@ internal static class SdkProjectDiscovery
|
|
353
383
|
Dictionary<string, Dictionary<string, Dictionary<string, string>>> packagesReplacedBySdkPerProject,
|
354
384
|
Dictionary<string, Dictionary<string, HashSet<string>>> topLevelPackagesPerProject,
|
355
385
|
Dictionary<string, Dictionary<string, string>> resolvedProperties,
|
386
|
+
Dictionary<string, Dictionary<string, HashSet<string>>> packageDependencies,
|
356
387
|
Dictionary<string, HashSet<string>> referencedProjects,
|
357
388
|
Dictionary<string, HashSet<string>> importedFiles,
|
358
389
|
Dictionary<string, HashSet<string>> additionalFiles
|
359
390
|
)
|
360
391
|
{
|
361
392
|
var projectDiscoveryResults = new List<ProjectDiscoveryResult>();
|
362
|
-
foreach (var projectPath in packagesPerProject.Keys.OrderBy(p => p))
|
393
|
+
foreach (var projectPath in packagesPerProject.Keys.OrderBy(p => p))
|
363
394
|
{
|
364
395
|
// gather some project-level information
|
365
396
|
var packagesByTfm = packagesPerProject[projectPath];
|
@@ -400,18 +431,99 @@ internal static class SdkProjectDiscovery
|
|
400
431
|
.SelectMany(kvp => kvp.Value)
|
401
432
|
.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
402
433
|
|
434
|
+
var propertiesForProject = resolvedProperties.GetOrAdd(projectPath, () => new(StringComparer.OrdinalIgnoreCase));
|
435
|
+
var assetsJson = new Lazy<JsonElement?>(() =>
|
436
|
+
{
|
437
|
+
if (propertiesForProject.TryGetValue("ProjectAssetsFile", out var assetsFilePath))
|
438
|
+
{
|
439
|
+
var assetsContent = File.ReadAllText(assetsFilePath);
|
440
|
+
var assets = JsonDocument.Parse(assetsContent).RootElement;
|
441
|
+
return assets;
|
442
|
+
}
|
443
|
+
|
444
|
+
return null;
|
445
|
+
});
|
446
|
+
|
403
447
|
// create dependencies
|
404
448
|
var tfms = packagesByTfm.Keys.OrderBy(tfm => tfm).ToImmutableArray();
|
405
|
-
var
|
449
|
+
var groupedDependencies = new Dictionary<string, Dependency>(StringComparer.OrdinalIgnoreCase);
|
450
|
+
foreach (var tfm in tfms)
|
406
451
|
{
|
407
|
-
|
452
|
+
var parsedTfm = NuGetFramework.Parse(tfm);
|
453
|
+
var packages = packagesByTfm[tfm];
|
454
|
+
|
455
|
+
// augment with any packages that might not have reported assemblies
|
456
|
+
var assetsPackageVersions = new Lazy<Dictionary<string, string>>(() =>
|
457
|
+
{
|
458
|
+
var result = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
459
|
+
if (assetsJson.Value is { } assets &&
|
460
|
+
assets.TryGetProperty("targets", out var tfmObjects))
|
461
|
+
{
|
462
|
+
foreach (var tfmObject in tfmObjects.EnumerateObject())
|
463
|
+
{
|
464
|
+
// TFM might have a RID suffix after a slash that we can't parse
|
465
|
+
var tfmParts = tfmObject.Name.Split('/');
|
466
|
+
var reportedTargetFramework = NuGetFramework.Parse(tfmParts[0]);
|
467
|
+
if (reportedTargetFramework == parsedTfm)
|
468
|
+
{
|
469
|
+
foreach (var packageObject in tfmObject.Value.EnumerateObject())
|
470
|
+
{
|
471
|
+
var parts = packageObject.Name.Split('/');
|
472
|
+
if (parts.Length == 2)
|
473
|
+
{
|
474
|
+
var packageName = parts[0];
|
475
|
+
var packageVersion = parts[1];
|
476
|
+
result[packageName] = packageVersion;
|
477
|
+
}
|
478
|
+
}
|
479
|
+
}
|
480
|
+
}
|
481
|
+
}
|
482
|
+
|
483
|
+
return result;
|
484
|
+
});
|
485
|
+
var packageDepsForProject = packageDependencies.GetOrAdd(projectPath, () => new(StringComparer.OrdinalIgnoreCase));
|
486
|
+
var packageDepsForTfm = packageDepsForProject.GetOrAdd(tfm, () => new(StringComparer.OrdinalIgnoreCase));
|
487
|
+
foreach (var packageDepName in packageDepsForTfm)
|
488
|
+
{
|
489
|
+
if (packages.ContainsKey(packageDepName))
|
490
|
+
{
|
491
|
+
// we already know about this
|
492
|
+
continue;
|
493
|
+
}
|
494
|
+
|
495
|
+
// otherwise find the corresponding version through project.assets.json
|
496
|
+
if (assetsPackageVersions.Value.TryGetValue(packageDepName, out var packageDepVersion))
|
497
|
+
{
|
498
|
+
packages[packageDepName] = packageDepVersion;
|
499
|
+
}
|
500
|
+
}
|
501
|
+
|
502
|
+
foreach (var package in packages)
|
408
503
|
{
|
409
|
-
var
|
504
|
+
var packageName = package.Key;
|
505
|
+
var packageVersion = package.Value;
|
410
506
|
var isTopLevel = topLevelPackageNames.Contains(packageName);
|
411
507
|
var dependencyType = isTopLevel ? DependencyType.PackageReference : DependencyType.Unknown;
|
412
|
-
|
413
|
-
|
414
|
-
|
508
|
+
var combinedTfms = new HashSet<string>([tfm], StringComparer.OrdinalIgnoreCase);
|
509
|
+
if (groupedDependencies.TryGetValue(packageName, out var existingDependency) &&
|
510
|
+
existingDependency.Version == packageVersion &&
|
511
|
+
existingDependency.Type == dependencyType &&
|
512
|
+
existingDependency.TargetFrameworks is not null)
|
513
|
+
{
|
514
|
+
// same dependency, combine tfms
|
515
|
+
combinedTfms.AddRange(existingDependency.TargetFrameworks);
|
516
|
+
}
|
517
|
+
|
518
|
+
var normalizedTfms = combinedTfms.OrderBy(t => t).ToImmutableArray();
|
519
|
+
groupedDependencies[package.Key] = new Dependency(packageName, packageVersion, dependencyType, TargetFrameworks: normalizedTfms, IsDirect: isTopLevel, IsTransitive: !isTopLevel);
|
520
|
+
}
|
521
|
+
}
|
522
|
+
|
523
|
+
var dependencies = groupedDependencies.Values
|
524
|
+
.OrderBy(d => d.Name)
|
525
|
+
.ThenBy(d => d.Version)
|
526
|
+
.ToImmutableArray();
|
415
527
|
|
416
528
|
// others
|
417
529
|
var properties = resolvedProperties[projectPath]
|
@@ -27,6 +27,12 @@ public abstract record JobErrorBase : MessageBase
|
|
27
27
|
report.AppendLine($"Error type: {Type}");
|
28
28
|
foreach (var (key, value) in Details)
|
29
29
|
{
|
30
|
+
if (this is UnknownError && key == "error-backtrace")
|
31
|
+
{
|
32
|
+
// there's nothing meaningful in this field
|
33
|
+
continue;
|
34
|
+
}
|
35
|
+
|
30
36
|
var valueString = value.ToString();
|
31
37
|
if (value is IEnumerable<string> strings)
|
32
38
|
{
|
@@ -44,6 +50,7 @@ public abstract record JobErrorBase : MessageBase
|
|
44
50
|
return ex switch
|
45
51
|
{
|
46
52
|
BadRequirementException badRequirement => new BadRequirement(badRequirement.Message),
|
53
|
+
BadResponseException badResponse => new PrivateSourceBadResponse([badResponse.Uri]),
|
47
54
|
DependencyNotFoundException dependencyNotFound => new DependencyNotFound(string.Join(", ", dependencyNotFound.Dependencies)),
|
48
55
|
HttpRequestException httpRequest => httpRequest.StatusCode switch
|
49
56
|
{
|
@@ -1,13 +1,23 @@
|
|
1
|
+
using System.Text.Json.Serialization;
|
2
|
+
|
1
3
|
namespace NuGetUpdater.Core.Run.ApiModel;
|
2
4
|
|
3
5
|
public record UnknownError : JobErrorBase
|
4
6
|
{
|
7
|
+
[JsonIgnore]
|
8
|
+
public Exception Exception { get; init; }
|
9
|
+
|
5
10
|
public UnknownError(Exception ex, string jobId)
|
6
11
|
: base("unknown_error")
|
7
12
|
{
|
13
|
+
Exception = ex;
|
14
|
+
|
15
|
+
// The following object is parsed by the server and the `error-backtrace` property is expected to be a Ruby
|
16
|
+
// stacktrace. Since we're not in Ruby we can set an empty string there and append the .NET stacktrace to
|
17
|
+
// the message.
|
8
18
|
Details["error-class"] = ex.GetType().Name;
|
9
|
-
Details["error-message"] = ex.
|
10
|
-
Details["error-backtrace"] =
|
19
|
+
Details["error-message"] = ex.ToString();
|
20
|
+
Details["error-backtrace"] = "";
|
11
21
|
Details["package-manager"] = "nuget";
|
12
22
|
Details["job-id"] = jobId;
|
13
23
|
}
|
@@ -2,8 +2,6 @@ using System.Text;
|
|
2
2
|
using System.Text.Json;
|
3
3
|
using System.Text.Json.Serialization;
|
4
4
|
|
5
|
-
using NuGetUpdater.Core.Run.ApiModel;
|
6
|
-
|
7
5
|
namespace NuGetUpdater.Core.Run;
|
8
6
|
|
9
7
|
public class HttpApiHandler : IApiHandler
|
@@ -25,39 +23,27 @@ public class HttpApiHandler : IApiHandler
|
|
25
23
|
_jobId = jobId;
|
26
24
|
}
|
27
25
|
|
28
|
-
public async Task
|
29
|
-
{
|
30
|
-
await PostAsJson("record_update_job_error", error);
|
31
|
-
}
|
32
|
-
|
33
|
-
public async Task UpdateDependencyList(UpdatedDependencyList updatedDependencyList)
|
26
|
+
public async Task SendAsync(string endpoint, object body, string method)
|
34
27
|
{
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
}
|
52
|
-
|
53
|
-
public async Task UpdatePullRequest(UpdatePullRequest updatePullRequest)
|
54
|
-
{
|
55
|
-
await PostAsJson("update_pull_request", updatePullRequest);
|
56
|
-
}
|
28
|
+
var uri = $"{_apiUrl}/update_jobs/{_jobId}/{endpoint}";
|
29
|
+
var payload = Serialize(body);
|
30
|
+
var content = new StringContent(payload, Encoding.UTF8, "application/json");
|
31
|
+
var httpMethod = new HttpMethod(method);
|
32
|
+
var message = new HttpRequestMessage(httpMethod, uri)
|
33
|
+
{
|
34
|
+
Content = content
|
35
|
+
};
|
36
|
+
var response = await HttpClient.SendAsync(message);
|
37
|
+
if (!response.IsSuccessStatusCode)
|
38
|
+
{
|
39
|
+
var responseContent = await response.Content.ReadAsStringAsync();
|
40
|
+
if (!string.IsNullOrEmpty(responseContent))
|
41
|
+
{
|
42
|
+
responseContent = string.Concat(": ", responseContent);
|
43
|
+
}
|
57
44
|
|
58
|
-
|
59
|
-
|
60
|
-
await PatchAsJson("mark_as_processed", markAsProcessed);
|
45
|
+
throw new HttpRequestException(message: $"{(int)response.StatusCode} ({response.StatusCode}){responseContent}", inner: null, statusCode: response.StatusCode);
|
46
|
+
}
|
61
47
|
}
|
62
48
|
|
63
49
|
internal static string Serialize(object body)
|
@@ -69,21 +55,4 @@ public class HttpApiHandler : IApiHandler
|
|
69
55
|
var payload = JsonSerializer.Serialize(wrappedBody, SerializerOptions);
|
70
56
|
return payload;
|
71
57
|
}
|
72
|
-
|
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)
|
77
|
-
{
|
78
|
-
var uri = $"{_apiUrl}/update_jobs/{_jobId}/{endpoint}";
|
79
|
-
var payload = Serialize(body);
|
80
|
-
var content = new StringContent(payload, Encoding.UTF8, "application/json");
|
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);
|
87
|
-
var _ = response.EnsureSuccessStatusCode();
|
88
|
-
}
|
89
58
|
}
|
@@ -4,11 +4,37 @@ namespace NuGetUpdater.Core.Run;
|
|
4
4
|
|
5
5
|
public interface IApiHandler
|
6
6
|
{
|
7
|
-
Task
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
Task
|
13
|
-
|
7
|
+
Task SendAsync(string endpoint, object body, string method);
|
8
|
+
}
|
9
|
+
|
10
|
+
public static class IApiHandlerExtensions
|
11
|
+
{
|
12
|
+
public static async Task RecordUpdateJobError(this IApiHandler handler, JobErrorBase error)
|
13
|
+
{
|
14
|
+
await handler.PostAsJson("record_update_job_error", error);
|
15
|
+
if (error is UnknownError unknown)
|
16
|
+
{
|
17
|
+
await handler.PostAsJson("record_update_job_unknown_error", error);
|
18
|
+
var increment = new IncrementMetric()
|
19
|
+
{
|
20
|
+
Metric = "updater.update_job_unknown_error",
|
21
|
+
Tags =
|
22
|
+
{
|
23
|
+
["package_manager"] = "nuget",
|
24
|
+
["class_name"] = unknown.Exception.GetType().Name
|
25
|
+
},
|
26
|
+
};
|
27
|
+
await handler.IncrementMetric(increment);
|
28
|
+
}
|
29
|
+
}
|
30
|
+
|
31
|
+
public static Task UpdateDependencyList(this IApiHandler handler, UpdatedDependencyList updatedDependencyList) => handler.PostAsJson("update_dependency_list", updatedDependencyList);
|
32
|
+
public static Task IncrementMetric(this IApiHandler handler, IncrementMetric incrementMetric) => handler.PostAsJson("increment_metric", incrementMetric);
|
33
|
+
public static Task CreatePullRequest(this IApiHandler handler, CreatePullRequest createPullRequest) => handler.PostAsJson("create_pull_request", createPullRequest);
|
34
|
+
public static Task ClosePullRequest(this IApiHandler handler, ClosePullRequest closePullRequest) => handler.PostAsJson("close_pull_request", closePullRequest);
|
35
|
+
public static Task UpdatePullRequest(this IApiHandler handler, UpdatePullRequest updatePullRequest) => handler.PostAsJson("update_pull_request", updatePullRequest);
|
36
|
+
public static Task MarkAsProcessed(this IApiHandler handler, MarkAsProcessed markAsProcessed) => handler.PatchAsJson("mark_as_processed", markAsProcessed);
|
37
|
+
|
38
|
+
private static Task PostAsJson(this IApiHandler handler, string endpoint, object body) => handler.SendAsync(endpoint, body, "POST");
|
39
|
+
private static Task PatchAsJson(this IApiHandler handler, string endpoint, object body) => handler.SendAsync(endpoint, body, "PATCH");
|
14
40
|
}
|
@@ -223,12 +223,17 @@ public class RunWorker
|
|
223
223
|
{
|
224
224
|
if (!job.UpdatingAPullRequest)
|
225
225
|
{
|
226
|
-
var
|
227
|
-
|
226
|
+
var updatedDependencyFromAnalysis = analysisResult.UpdatedDependencies
|
227
|
+
.FirstOrDefault(d => d.Name.Equals(dependency.Name, StringComparison.OrdinalIgnoreCase));
|
228
|
+
if (updatedDependencyFromAnalysis is not null)
|
228
229
|
{
|
229
|
-
|
230
|
-
|
231
|
-
|
230
|
+
var existingPullRequest = job.GetExistingPullRequestForDependency(updatedDependencyFromAnalysis);
|
231
|
+
if (existingPullRequest is not null)
|
232
|
+
{
|
233
|
+
await SendApiMessage(new PullRequestExistsForLatestVersion(dependency.Name, analysisResult.UpdatedVersion));
|
234
|
+
unhandledPullRequestDependenciesSet.RemoveWhere(handled => handled.Count == 1 && handled.Contains(dependency.Name));
|
235
|
+
continue;
|
236
|
+
}
|
232
237
|
}
|
233
238
|
}
|
234
239
|
|
@@ -1,5 +1,6 @@
|
|
1
1
|
using System.Collections.Immutable;
|
2
2
|
using System.Text.Json;
|
3
|
+
using System.Text.RegularExpressions;
|
3
4
|
|
4
5
|
using Microsoft.Language.Xml;
|
5
6
|
|
@@ -526,7 +527,7 @@ internal static class PackageReferenceUpdater
|
|
526
527
|
/// <returns>The updated files.</returns>
|
527
528
|
private static async Task<IEnumerable<string>> AddTransitiveDependencyAsync(string repoRootPath, string projectPath, string dependencyName, string newDependencyVersion, ExperimentsManager experimentsManager, ILogger logger)
|
528
529
|
{
|
529
|
-
var updatedFiles = new
|
530
|
+
var updatedFiles = new List<string>() { projectPath }; // assume this worked unless...
|
530
531
|
var projectDirectory = Path.GetDirectoryName(projectPath)!;
|
531
532
|
await MSBuildHelper.HandleGlobalJsonAsync(projectDirectory, repoRootPath, experimentsManager, async () =>
|
532
533
|
{
|
@@ -545,6 +546,17 @@ internal static class PackageReferenceUpdater
|
|
545
546
|
updatedFiles = [];
|
546
547
|
}
|
547
548
|
|
549
|
+
// output might contain a line like this:
|
550
|
+
// info : PackageReference for package 'Some.Package' added to 'C:\project.csproj' and PackageVersion added to central package management file 'C:\Directory.Packages.props'.
|
551
|
+
// we explicitly want to pull out this: ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
552
|
+
// so we can report all files updated on disk
|
553
|
+
var match = Regex.Match(stdout, @"added to central package management file '(?<CentralPackageManagementFilePath>[^']+)'");
|
554
|
+
if (match.Success)
|
555
|
+
{
|
556
|
+
var cpmFilePath = match.Groups["CentralPackageManagementFilePath"].Value;
|
557
|
+
updatedFiles.Add(cpmFilePath);
|
558
|
+
}
|
559
|
+
|
548
560
|
return exitCode;
|
549
561
|
}, logger, retainMSBuildSdks: true);
|
550
562
|
|
@@ -1029,6 +1029,7 @@ internal static partial class MSBuildHelper
|
|
1029
1029
|
new Regex(@"Unable to find package (?<PackageName>[^ ]+)\. No packages exist with this id in source\(s\): (?<PackageSource>.*)$", RegexOptions.Multiline),
|
1030
1030
|
new Regex(@"Unable to find package (?<PackageName>[^ ]+) with version \((?<PackageVersion>[^)]+)\)"),
|
1031
1031
|
new Regex(@"Could not resolve SDK ""(?<PackageName>[^ ]+)""\."),
|
1032
|
+
new Regex(@"Failed to fetch results from V2 feed at '.*FindPackagesById\(\)\?id='(?<PackageName>[^']+)'&semVerLevel=2\.0\.0' with following message : Response status code does not indicate success: 404\."),
|
1032
1033
|
};
|
1033
1034
|
var matches = patterns.Select(p => p.Match(output)).Where(m => m.Success);
|
1034
1035
|
if (matches.Any())
|
@@ -1,9 +1,12 @@
|
|
1
1
|
using System.Collections.Immutable;
|
2
|
+
using System.Text.Json;
|
2
3
|
|
3
4
|
using NuGet.Frameworks;
|
4
5
|
using NuGet.Versioning;
|
5
6
|
|
6
7
|
using NuGetUpdater.Core.Analyze;
|
8
|
+
using NuGetUpdater.Core.Run;
|
9
|
+
using NuGetUpdater.Core.Run.ApiModel;
|
7
10
|
using NuGetUpdater.Core.Test.Update;
|
8
11
|
using NuGetUpdater.Core.Test.Utilities;
|
9
12
|
|
@@ -11,7 +14,7 @@ using Xunit;
|
|
11
14
|
|
12
15
|
namespace NuGetUpdater.Core.Test.Analyze;
|
13
16
|
|
14
|
-
public class VersionFinderTests
|
17
|
+
public class VersionFinderTests : TestBase
|
15
18
|
{
|
16
19
|
[Fact]
|
17
20
|
public void VersionFilter_VersionInIgnoredVersions_ReturnsFalse()
|
@@ -223,4 +226,74 @@ public class VersionFinderTests
|
|
223
226
|
var expected = new[] { "2.0.0" };
|
224
227
|
AssertEx.Equal(expected, actual);
|
225
228
|
}
|
229
|
+
|
230
|
+
[Fact]
|
231
|
+
public async Task FeedReturnsBadJson()
|
232
|
+
{
|
233
|
+
// arrange
|
234
|
+
using var http = TestHttpServer.CreateTestStringServer(url =>
|
235
|
+
{
|
236
|
+
var uri = new Uri(url, UriKind.Absolute);
|
237
|
+
var baseUrl = $"{uri.Scheme}://{uri.Host}:{uri.Port}";
|
238
|
+
return uri.PathAndQuery switch
|
239
|
+
{
|
240
|
+
// initial and search query are good, update should be possible...
|
241
|
+
"/index.json" => (200, $$"""
|
242
|
+
{
|
243
|
+
"version": "3.0.0",
|
244
|
+
"resources": [
|
245
|
+
{
|
246
|
+
"@id": "{{baseUrl}}/download",
|
247
|
+
"@type": "PackageBaseAddress/3.0.0"
|
248
|
+
},
|
249
|
+
{
|
250
|
+
"@id": "{{baseUrl}}/query",
|
251
|
+
"@type": "SearchQueryService"
|
252
|
+
},
|
253
|
+
{
|
254
|
+
"@id": "{{baseUrl}}/registrations",
|
255
|
+
"@type": "RegistrationsBaseUrl"
|
256
|
+
}
|
257
|
+
]
|
258
|
+
}
|
259
|
+
"""),
|
260
|
+
_ => (200, "") // empty string instead of expected JSON object
|
261
|
+
};
|
262
|
+
});
|
263
|
+
var feedUrl = $"{http.BaseUrl.TrimEnd('/')}/index.json";
|
264
|
+
using var tempDir = await TemporaryDirectory.CreateWithContentsAsync(
|
265
|
+
("NuGet.Config", $"""
|
266
|
+
<configuration>
|
267
|
+
<packageSources>
|
268
|
+
<clear />
|
269
|
+
<add key="private_feed" value="{feedUrl}" allowInsecureConnections="true" />
|
270
|
+
</packageSources>
|
271
|
+
</configuration>
|
272
|
+
""")
|
273
|
+
);
|
274
|
+
|
275
|
+
// act
|
276
|
+
var tfm = NuGetFramework.Parse("net9.0");
|
277
|
+
var dependencyInfo = new DependencyInfo
|
278
|
+
{
|
279
|
+
Name = "Some.Dependency",
|
280
|
+
Version = "1.0.0",
|
281
|
+
IsVulnerable = false,
|
282
|
+
IgnoredVersions = [],
|
283
|
+
Vulnerabilities = [],
|
284
|
+
};
|
285
|
+
var logger = new TestLogger();
|
286
|
+
var nugetContext = new NuGetContext(tempDir.DirectoryPath);
|
287
|
+
var exception = await Assert.ThrowsAsync<BadResponseException>(async () =>
|
288
|
+
{
|
289
|
+
await VersionFinder.GetVersionsAsync([tfm], dependencyInfo, nugetContext, logger, CancellationToken.None);
|
290
|
+
});
|
291
|
+
var error = JobErrorBase.ErrorFromException(exception, "TEST-JOB-ID", tempDir.DirectoryPath);
|
292
|
+
|
293
|
+
// assert
|
294
|
+
var expected = new PrivateSourceBadResponse([feedUrl]);
|
295
|
+
var expectedJson = JsonSerializer.Serialize(expected, RunWorker.SerializerOptions);
|
296
|
+
var actualJson = JsonSerializer.Serialize(error, RunWorker.SerializerOptions);
|
297
|
+
Assert.Equal(expectedJson, actualJson);
|
298
|
+
}
|
226
299
|
}
|
@@ -111,7 +111,7 @@ public class CloneWorkerTests
|
|
111
111
|
|
112
112
|
// assert
|
113
113
|
Assert.Equal(1, result);
|
114
|
-
var expectedParseErrorObject = testApiHandler.ReceivedMessages.
|
114
|
+
var expectedParseErrorObject = testApiHandler.ReceivedMessages.First(m => m.Type == typeof(UnknownError));
|
115
115
|
var unknownError = (UnknownError)expectedParseErrorObject.Object;
|
116
116
|
Assert.Equal("JsonException", unknownError.Details["error-class"]);
|
117
117
|
}
|