dependabot-nuget 0.312.0 → 0.314.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 +16 -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 +24 -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: 63addcf7753f130114a786641a1c8f3b82b2d8a3cd4c46e4f3d072c3d1b189f7
|
4
|
+
data.tar.gz: 36ad00f20c6beb2fd1146ddba62f9cffe9c42246e358f70fc4ec115372c93540
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 967973760977d8dce179417fe4a786c134db3a0b14fce254b303ca52c930a25b43fa990447b5fbddc36dfd6223129ba727764b628fd817598cab3f7f77feddec
|
7
|
+
data.tar.gz: 1616f671a195834522840194d936d3a301f1664f00d66201f31b27276436bdcbe5c1ed604b4d0ec06bae55276e183dfa6f464d1178f2fe0536edc421d3756f49
|
@@ -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)
|
@@ -978,6 +979,7 @@ internal static partial class MSBuildHelper
|
|
978
979
|
"The plugin credential provider could not acquire credentials",
|
979
980
|
"401 (Unauthorized)",
|
980
981
|
"error NU1301: Unable to load the service index for source",
|
982
|
+
"Response status code does not indicate success: 401",
|
981
983
|
"Response status code does not indicate success: 403",
|
982
984
|
};
|
983
985
|
if (unauthorizedMessageSnippets.Any(stdout.Contains))
|
@@ -1058,6 +1060,7 @@ internal static partial class MSBuildHelper
|
|
1058
1060
|
new Regex(@"Could not install package '(?<PackageName>[^ ]+) (?<PackageVersion>[^']+)'. You are trying to install this package"),
|
1059
1061
|
new Regex(@"Unable to find a version of '[^']+' that is compatible with '[^ ]+ [^ ]+ constraint: (?<PackageName>[^ ]+) \([^ ]+ (?<PackageVersion>[^)]+)\)'"),
|
1060
1062
|
new Regex(@"the following error\(s\) may be blocking the current package operation: '(?<PackageName>[^ ]+) (?<PackageVersion>[^ ]+) constraint:"),
|
1063
|
+
new Regex(@"Unable to resolve '(?<PackageName>[^']+)'. An additional constraint '\((?<PackageVersion>[^)]+)\)' defined in packages.config prevents this operation."),
|
1061
1064
|
};
|
1062
1065
|
var matches = patterns.Select(p => p.Match(output)).Where(m => m.Success);
|
1063
1066
|
if (matches.Any())
|
@@ -1067,6 +1070,19 @@ internal static partial class MSBuildHelper
|
|
1067
1070
|
}
|
1068
1071
|
}
|
1069
1072
|
|
1073
|
+
private static void ThrowOnUnparseableFile(string output)
|
1074
|
+
{
|
1075
|
+
var patterns = new[]
|
1076
|
+
{
|
1077
|
+
new Regex(@"\nAn error occurred while reading file '(?<FilePath>[^']+)': (?<Message>[^\n]*)\n"),
|
1078
|
+
};
|
1079
|
+
var match = patterns.Select(p => p.Match(output)).Where(m => m.Success).FirstOrDefault();
|
1080
|
+
if (match is not null)
|
1081
|
+
{
|
1082
|
+
throw new UnparseableFileException(match.Groups["Message"].Value, match.Groups["FilePath"].Value);
|
1083
|
+
}
|
1084
|
+
}
|
1085
|
+
|
1070
1086
|
internal static bool TryGetGlobalJsonPath(string repoRootPath, string workspacePath, [NotNullWhen(returnValue: true)] out string? globalJsonPath)
|
1071
1087
|
{
|
1072
1088
|
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
|
+
}
|
@@ -1773,6 +1773,14 @@ public class MSBuildHelperTests : TestBase
|
|
1773
1773
|
null,
|
1774
1774
|
];
|
1775
1775
|
|
1776
|
+
yield return
|
1777
|
+
[
|
1778
|
+
// output
|
1779
|
+
"Response status code does not indicate success: 401",
|
1780
|
+
// expectedError
|
1781
|
+
new PrivateSourceAuthenticationFailure(["http://localhost/test-feed"]),
|
1782
|
+
];
|
1783
|
+
|
1776
1784
|
yield return
|
1777
1785
|
[
|
1778
1786
|
// output
|
@@ -1853,6 +1861,14 @@ public class MSBuildHelperTests : TestBase
|
|
1853
1861
|
new UpdateNotPossible(["Some.Package.1.2.3"]),
|
1854
1862
|
];
|
1855
1863
|
|
1864
|
+
yield return
|
1865
|
+
[
|
1866
|
+
// output
|
1867
|
+
"Unable to resolve 'Some.Package'. An additional constraint '(= 1.2.3)' defined in packages.config prevents this operation.",
|
1868
|
+
// expectedError
|
1869
|
+
new UpdateNotPossible(["Some.Package.= 1.2.3"]),
|
1870
|
+
];
|
1871
|
+
|
1856
1872
|
yield return
|
1857
1873
|
[
|
1858
1874
|
// output
|
@@ -1860,6 +1876,14 @@ public class MSBuildHelperTests : TestBase
|
|
1860
1876
|
// expectedError
|
1861
1877
|
new DependencyNotFound("Some.Package"),
|
1862
1878
|
];
|
1879
|
+
|
1880
|
+
yield return
|
1881
|
+
[
|
1882
|
+
// output
|
1883
|
+
"This part is not reported.\nAn error occurred while reading file '/path/to/packages.config': Some error message.\nThis part is not reported.",
|
1884
|
+
// expectedError
|
1885
|
+
new DependencyFileNotParseable("/path/to/packages.config", "Some error message."),
|
1886
|
+
];
|
1863
1887
|
}
|
1864
1888
|
|
1865
1889
|
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.314.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-22 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.314.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.314.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.314.0
|
561
562
|
rdoc_options: []
|
562
563
|
require_paths:
|
563
564
|
- lib
|