dependabot-nuget 0.312.0 → 0.313.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.Core/Run/ApiModel/JobErrorBase.cs +58 -18
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/IApiHandler.cs +26 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +14 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/HttpApiHandlerTests.cs +43 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/JobErrorBaseTests.cs +86 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/MSBuildHelperTests.cs +8 -0
- metadata +6 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ac365725d12dca90647d4d71f59d1038f9d678bc811f20ffcb2f48ec0fb5da7a
|
4
|
+
data.tar.gz: 7acd524ff51f1a2f2eba42db8711331410e740761deecb78e97365d6d5ada225
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 27bb68098a00a6e05e88e07c5adfe99c9a74fe9fe8cf43d367e77fee46e29046832f8cf7993ac02fe767709da11a570ef3900a0a8c0941bcde5f2bc19d0a6e07
|
7
|
+
data.tar.gz: 84654a79e5cfb88dbde80c8218ed8737d5211ba5f65cc974d3aca4ab5aac8feae869a5718cebb1ed9724e46891c11287854762d60d89f535a63716ea8fcca9ee
|
@@ -47,24 +47,64 @@ public abstract record JobErrorBase : MessageBase
|
|
47
47
|
|
48
48
|
public static JobErrorBase ErrorFromException(Exception ex, string jobId, string currentDirectory)
|
49
49
|
{
|
50
|
-
|
50
|
+
switch (ex)
|
51
51
|
{
|
52
|
-
BadRequirementException badRequirement
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
52
|
+
case BadRequirementException badRequirement:
|
53
|
+
return new BadRequirement(badRequirement.Message);
|
54
|
+
case BadResponseException badResponse:
|
55
|
+
return new PrivateSourceBadResponse([badResponse.Uri]);
|
56
|
+
case DependencyNotFoundException dependencyNotFound:
|
57
|
+
return new DependencyNotFound(string.Join(", ", dependencyNotFound.Dependencies));
|
58
|
+
case HttpRequestException httpRequest:
|
59
|
+
if (httpRequest.StatusCode is null)
|
60
|
+
{
|
61
|
+
if (httpRequest.InnerException is HttpIOException ioException &&
|
62
|
+
ioException.HttpRequestError == HttpRequestError.ResponseEnded)
|
63
|
+
{
|
64
|
+
// server hung up on us
|
65
|
+
return new PrivateSourceBadResponse(NuGetContext.GetPackageSourceUrls(currentDirectory));
|
66
|
+
}
|
67
|
+
|
68
|
+
return new UnknownError(ex, jobId);
|
69
|
+
}
|
70
|
+
|
71
|
+
switch (httpRequest.StatusCode)
|
72
|
+
{
|
73
|
+
case HttpStatusCode.Unauthorized:
|
74
|
+
case HttpStatusCode.Forbidden:
|
75
|
+
return new PrivateSourceAuthenticationFailure(NuGetContext.GetPackageSourceUrls(currentDirectory));
|
76
|
+
case HttpStatusCode.TooManyRequests:
|
77
|
+
case HttpStatusCode.ServiceUnavailable:
|
78
|
+
return new PrivateSourceBadResponse(NuGetContext.GetPackageSourceUrls(currentDirectory));
|
79
|
+
default:
|
80
|
+
if ((int)httpRequest.StatusCode / 100 == 5)
|
81
|
+
{
|
82
|
+
return new PrivateSourceBadResponse(NuGetContext.GetPackageSourceUrls(currentDirectory));
|
83
|
+
}
|
84
|
+
|
85
|
+
return new UnknownError(ex, jobId);
|
86
|
+
}
|
87
|
+
case InvalidProjectFileException invalidProjectFile:
|
88
|
+
return new DependencyFileNotParseable(invalidProjectFile.ProjectFile);
|
89
|
+
case MissingFileException missingFile:
|
90
|
+
return new DependencyFileNotFound(missingFile.FilePath, missingFile.Message);
|
91
|
+
case UnparseableFileException unparseableFile:
|
92
|
+
return new DependencyFileNotParseable(unparseableFile.FilePath, unparseableFile.Message);
|
93
|
+
case UpdateNotPossibleException updateNotPossible:
|
94
|
+
return new UpdateNotPossible(updateNotPossible.Dependencies);
|
95
|
+
default:
|
96
|
+
// if a more specific inner exception was encountered, use that, otherwise...
|
97
|
+
if (ex.InnerException is not null)
|
98
|
+
{
|
99
|
+
var innerError = ErrorFromException(ex.InnerException, jobId, currentDirectory);
|
100
|
+
if (innerError is not UnknownError)
|
101
|
+
{
|
102
|
+
return innerError;
|
103
|
+
}
|
104
|
+
}
|
105
|
+
|
106
|
+
// ...return the whole thing
|
107
|
+
return new UnknownError(ex, jobId);
|
108
|
+
}
|
69
109
|
}
|
70
110
|
}
|
@@ -35,6 +35,30 @@ public static class IApiHandlerExtensions
|
|
35
35
|
public static Task UpdatePullRequest(this IApiHandler handler, UpdatePullRequest updatePullRequest) => handler.PostAsJson("update_pull_request", updatePullRequest);
|
36
36
|
public static Task MarkAsProcessed(this IApiHandler handler, MarkAsProcessed markAsProcessed) => handler.PatchAsJson("mark_as_processed", markAsProcessed);
|
37
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");
|
38
|
+
private static Task PostAsJson(this IApiHandler handler, string endpoint, object body) => handler.WithRetries(() => handler.SendAsync(endpoint, body, "POST"));
|
39
|
+
private static Task PatchAsJson(this IApiHandler handler, string endpoint, object body) => handler.WithRetries(() => handler.SendAsync(endpoint, body, "PATCH"));
|
40
|
+
|
41
|
+
private const int MaxRetries = 3;
|
42
|
+
private const int MinRetryDelay = 3;
|
43
|
+
private const int MaxRetryDelay = 10;
|
44
|
+
|
45
|
+
private static async Task WithRetries(this IApiHandler handler, Func<Task> action)
|
46
|
+
{
|
47
|
+
var retryCount = 0;
|
48
|
+
while (true)
|
49
|
+
{
|
50
|
+
try
|
51
|
+
{
|
52
|
+
await action();
|
53
|
+
return;
|
54
|
+
}
|
55
|
+
catch (HttpRequestException ex)
|
56
|
+
when (retryCount < MaxRetries &&
|
57
|
+
(ex.StatusCode is null || ((int)ex.StatusCode) / 100 == 5))
|
58
|
+
{
|
59
|
+
retryCount++;
|
60
|
+
await Task.Delay(TimeSpan.FromSeconds(Random.Shared.Next(MinRetryDelay, MaxRetryDelay)));
|
61
|
+
}
|
62
|
+
}
|
63
|
+
}
|
40
64
|
}
|
@@ -969,6 +969,7 @@ internal static partial class MSBuildHelper
|
|
969
969
|
ThrowOnUpdateNotPossible(output);
|
970
970
|
ThrowOnRateLimitExceeded(output);
|
971
971
|
ThrowOnServiceUnavailable(output);
|
972
|
+
ThrowOnUnparseableFile(output);
|
972
973
|
}
|
973
974
|
|
974
975
|
private static void ThrowOnUnauthenticatedFeed(string stdout)
|
@@ -1067,6 +1068,19 @@ internal static partial class MSBuildHelper
|
|
1067
1068
|
}
|
1068
1069
|
}
|
1069
1070
|
|
1071
|
+
private static void ThrowOnUnparseableFile(string output)
|
1072
|
+
{
|
1073
|
+
var patterns = new[]
|
1074
|
+
{
|
1075
|
+
new Regex(@"\nAn error occurred while reading file '(?<FilePath>[^']+)': (?<Message>[^\n]*)\n"),
|
1076
|
+
};
|
1077
|
+
var match = patterns.Select(p => p.Match(output)).Where(m => m.Success).FirstOrDefault();
|
1078
|
+
if (match is not null)
|
1079
|
+
{
|
1080
|
+
throw new UnparseableFileException(match.Groups["Message"].Value, match.Groups["FilePath"].Value);
|
1081
|
+
}
|
1082
|
+
}
|
1083
|
+
|
1070
1084
|
internal static bool TryGetGlobalJsonPath(string repoRootPath, string workspacePath, [NotNullWhen(returnValue: true)] out string? globalJsonPath)
|
1071
1085
|
{
|
1072
1086
|
globalJsonPath = PathHelper.GetFileInDirectoryOrParent(workspacePath, repoRootPath, "global.json", caseSensitive: false);
|
@@ -39,7 +39,7 @@ public class HttpApiHandlerTests
|
|
39
39
|
using var http = TestHttpServer.CreateTestServer((method, url) =>
|
40
40
|
{
|
41
41
|
// no error content returned
|
42
|
-
return (
|
42
|
+
return (400, null);
|
43
43
|
});
|
44
44
|
var handler = new HttpApiHandler(http.BaseUrl, "TEST-ID");
|
45
45
|
|
@@ -51,10 +51,51 @@ public class HttpApiHandlerTests
|
|
51
51
|
}));
|
52
52
|
|
53
53
|
// assert
|
54
|
-
var expectedMessage = $"
|
54
|
+
var expectedMessage = $"400 (BadRequest)";
|
55
55
|
Assert.Equal(expectedMessage, exception.Message);
|
56
56
|
}
|
57
57
|
|
58
|
+
[Fact]
|
59
|
+
public async Task ApiCallsAreAutomaticallyRetriedWhenTheServerThrowsAnError()
|
60
|
+
{
|
61
|
+
// arrange
|
62
|
+
var requestCount = 0;
|
63
|
+
using var http = TestHttpServer.CreateTestServer((method, url) =>
|
64
|
+
{
|
65
|
+
if (requestCount < 2)
|
66
|
+
{
|
67
|
+
requestCount++;
|
68
|
+
return (500, Array.Empty<byte>());
|
69
|
+
}
|
70
|
+
|
71
|
+
return (200, Array.Empty<byte>());
|
72
|
+
});
|
73
|
+
var handler = new HttpApiHandler(http.BaseUrl, "TEST-ID");
|
74
|
+
|
75
|
+
// act
|
76
|
+
await handler.IncrementMetric(new()
|
77
|
+
{
|
78
|
+
Metric = "test",
|
79
|
+
});
|
80
|
+
}
|
81
|
+
|
82
|
+
[Fact]
|
83
|
+
public async Task ApiCallsAreNotRetriedOnABadRequest()
|
84
|
+
{
|
85
|
+
// arrange
|
86
|
+
var requestCount = 0;
|
87
|
+
using var http = TestHttpServer.CreateTestServer((method, url) =>
|
88
|
+
{
|
89
|
+
requestCount++;
|
90
|
+
return (400, Array.Empty<byte>());
|
91
|
+
});
|
92
|
+
var handler = new HttpApiHandler(http.BaseUrl, "TEST-ID");
|
93
|
+
|
94
|
+
// act
|
95
|
+
await Assert.ThrowsAsync<HttpRequestException>(() => handler.IncrementMetric(new() { Metric = "test" }));
|
96
|
+
Assert.True(requestCount == 1, $"Expected only 1 request, but received {requestCount}.");
|
97
|
+
}
|
98
|
+
|
58
99
|
[Theory]
|
59
100
|
[MemberData(nameof(ErrorsAreSentToTheCorrectEndpointTestData))]
|
60
101
|
public async Task ErrorsAreSentToTheCorrectEndpoint(JobErrorBase error, params string[] expectedEndpoints)
|
@@ -0,0 +1,86 @@
|
|
1
|
+
using System.Net;
|
2
|
+
using System.Text.Json;
|
3
|
+
|
4
|
+
using NuGet.Protocol.Core.Types;
|
5
|
+
|
6
|
+
using NuGetUpdater.Core.Run;
|
7
|
+
using NuGetUpdater.Core.Run.ApiModel;
|
8
|
+
|
9
|
+
using Xunit;
|
10
|
+
|
11
|
+
namespace NuGetUpdater.Core.Test.Run;
|
12
|
+
|
13
|
+
public class JobErrorBaseTests : TestBase
|
14
|
+
{
|
15
|
+
[Theory]
|
16
|
+
[MemberData(nameof(GenerateErrorFromExceptionTestData))]
|
17
|
+
public async Task GenerateErrorFromException(Exception exception, JobErrorBase expectedError)
|
18
|
+
{
|
19
|
+
// arrange
|
20
|
+
// some error types require a NuGet.Config file to be present
|
21
|
+
using var tempDir = await TemporaryDirectory.CreateWithContentsAsync(
|
22
|
+
("NuGet.Config", """
|
23
|
+
<configuration>
|
24
|
+
<packageSources>
|
25
|
+
<clear />
|
26
|
+
<add key="some_package_feed" value="http://nuget.example.com/v3/index.json" allowInsecureConnections="true" />
|
27
|
+
</packageSources>
|
28
|
+
</configuration>
|
29
|
+
""")
|
30
|
+
);
|
31
|
+
|
32
|
+
// act
|
33
|
+
var actualError = JobErrorBase.ErrorFromException(exception, "TEST-JOB-ID", tempDir.DirectoryPath);
|
34
|
+
|
35
|
+
// assert
|
36
|
+
var actualErrorJson = JsonSerializer.Serialize(actualError, RunWorker.SerializerOptions);
|
37
|
+
var expectedErrorJson = JsonSerializer.Serialize(expectedError, RunWorker.SerializerOptions);
|
38
|
+
Assert.Equal(expectedErrorJson, actualErrorJson);
|
39
|
+
}
|
40
|
+
|
41
|
+
public static IEnumerable<object[]> GenerateErrorFromExceptionTestData()
|
42
|
+
{
|
43
|
+
// internal error from package feed
|
44
|
+
yield return
|
45
|
+
[
|
46
|
+
new HttpRequestException("nope", null, HttpStatusCode.InternalServerError),
|
47
|
+
new PrivateSourceBadResponse(["http://nuget.example.com/v3/index.json"]),
|
48
|
+
];
|
49
|
+
|
50
|
+
// inner exception turns into private_source_bad_response; 500
|
51
|
+
yield return
|
52
|
+
[
|
53
|
+
new FatalProtocolException("nope", new HttpRequestException("nope", null, HttpStatusCode.InternalServerError)),
|
54
|
+
new PrivateSourceBadResponse(["http://nuget.example.com/v3/index.json"]),
|
55
|
+
];
|
56
|
+
|
57
|
+
// inner exception turns into private_source_bad_response; ResponseEnded
|
58
|
+
yield return
|
59
|
+
[
|
60
|
+
new FatalProtocolException("nope", new HttpRequestException("nope", new HttpIOException(HttpRequestError.ResponseEnded))),
|
61
|
+
new PrivateSourceBadResponse(["http://nuget.example.com/v3/index.json"]),
|
62
|
+
];
|
63
|
+
|
64
|
+
// top-level exception turns into private_source_authentication_failure
|
65
|
+
yield return
|
66
|
+
[
|
67
|
+
new HttpRequestException("nope", null, HttpStatusCode.Unauthorized),
|
68
|
+
new PrivateSourceAuthenticationFailure(["http://nuget.example.com/v3/index.json"]),
|
69
|
+
];
|
70
|
+
|
71
|
+
// inner exception turns into private_source_authentication_failure
|
72
|
+
yield return
|
73
|
+
[
|
74
|
+
// the NuGet libraries commonly do this
|
75
|
+
new FatalProtocolException("nope", new HttpRequestException("nope", null, HttpStatusCode.Unauthorized)),
|
76
|
+
new PrivateSourceAuthenticationFailure(["http://nuget.example.com/v3/index.json"]),
|
77
|
+
];
|
78
|
+
|
79
|
+
// unknown errors all the way down; report the initial top-level error
|
80
|
+
yield return
|
81
|
+
[
|
82
|
+
new Exception("outer", new Exception("inner")),
|
83
|
+
new UnknownError(new Exception("outer", new Exception("inner")), "TEST-JOB-ID"),
|
84
|
+
];
|
85
|
+
}
|
86
|
+
}
|
@@ -1860,6 +1860,14 @@ public class MSBuildHelperTests : TestBase
|
|
1860
1860
|
// expectedError
|
1861
1861
|
new DependencyNotFound("Some.Package"),
|
1862
1862
|
];
|
1863
|
+
|
1864
|
+
yield return
|
1865
|
+
[
|
1866
|
+
// output
|
1867
|
+
"This part is not reported.\nAn error occurred while reading file '/path/to/packages.config': Some error message.\nThis part is not reported.",
|
1868
|
+
// expectedError
|
1869
|
+
new DependencyFileNotParseable("/path/to/packages.config", "Some error message."),
|
1870
|
+
];
|
1863
1871
|
}
|
1864
1872
|
|
1865
1873
|
public static IEnumerable<object[]> GetTopLevelPackageDependencyInfosTestData()
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dependabot-nuget
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.313.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dependabot
|
8
8
|
bindir: bin
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-05-
|
10
|
+
date: 2025-05-15 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: dependabot-common
|
@@ -15,14 +15,14 @@ dependencies:
|
|
15
15
|
requirements:
|
16
16
|
- - '='
|
17
17
|
- !ruby/object:Gem::Version
|
18
|
-
version: 0.
|
18
|
+
version: 0.313.0
|
19
19
|
type: :runtime
|
20
20
|
prerelease: false
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
22
22
|
requirements:
|
23
23
|
- - '='
|
24
24
|
- !ruby/object:Gem::Version
|
25
|
-
version: 0.
|
25
|
+
version: 0.313.0
|
26
26
|
- !ruby/object:Gem::Dependency
|
27
27
|
name: rubyzip
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
@@ -348,6 +348,7 @@ files:
|
|
348
348
|
- helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/NuGetUpdater.Core.Test.csproj
|
349
349
|
- helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/EndToEndTests.cs
|
350
350
|
- helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/HttpApiHandlerTests.cs
|
351
|
+
- helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/JobErrorBaseTests.cs
|
351
352
|
- helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/MessageReportTests.cs
|
352
353
|
- helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/MiscellaneousTests.cs
|
353
354
|
- helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/PullRequestMessageTests.cs
|
@@ -557,7 +558,7 @@ licenses:
|
|
557
558
|
- MIT
|
558
559
|
metadata:
|
559
560
|
bug_tracker_uri: https://github.com/dependabot/dependabot-core/issues
|
560
|
-
changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.
|
561
|
+
changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.313.0
|
561
562
|
rdoc_options: []
|
562
563
|
require_paths:
|
563
564
|
- lib
|