dependabot-nuget 0.294.0 → 0.295.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/DiscoveryWorker.cs +2 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/SdkProjectDiscovery.cs +1 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/RunWorker.cs +165 -123
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackageReferenceUpdater.cs +1 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackagesConfigUpdater.cs +3 -6
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +36 -18
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/RunWorkerTests.cs +338 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/UpdateAllowedTests.cs +286 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/UpdatedDependencyListTests.cs +9 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/MSBuildHelperTests.cs +88 -2
- 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: 8ac3a95f5211adff79f8e7954308a4d3656b63c8be20b70a7b6f129f04402881
|
4
|
+
data.tar.gz: a79771c0cc6c0a08bfaa2a7cef8253902c7e8b92b868568c231064098ba77a1c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 800097bc4856927e983da508d16b4e6d0fd75cf40b5ca5ed25901fbb467f0f99e8f8dc724341c0da6cc6126303c6437a5bd8c7521132c3363ed752181402ee1b
|
7
|
+
data.tar.gz: c0a1675e7cdf06ec0314d58092de09d633fa46892ae8f01c20ec74553d756265fda431434babb6eb140184b3380b78576f6309c1ec010f41e0e09e6ad6b28adb
|
@@ -262,7 +262,8 @@ public partial class DiscoveryWorker : IDiscoveryWorker
|
|
262
262
|
}
|
263
263
|
}
|
264
264
|
|
265
|
-
|
265
|
+
var result = expandedProjects.OrderBy(p => p).ToImmutableArray();
|
266
|
+
return result;
|
266
267
|
}
|
267
268
|
|
268
269
|
private static IEnumerable<string> ExpandItemGroupFilesFromProject(string projectPath, params string[] itemTypes)
|
@@ -114,7 +114,7 @@ internal static class SdkProjectDiscovery
|
|
114
114
|
var (exitCode, stdOut, stdErr) = await ProcessEx.RunDotnetWithoutMSBuildEnvironmentVariablesAsync(args, startingProjectDirectory, experimentsManager);
|
115
115
|
return (exitCode, stdOut, stdErr);
|
116
116
|
}, logger, retainMSBuildSdks: true);
|
117
|
-
MSBuildHelper.
|
117
|
+
MSBuildHelper.ThrowOnError(stdOut);
|
118
118
|
if (stdOut.Contains("""error MSB4057: The target "GenerateBuildDependencyFile" does not exist in the project."""))
|
119
119
|
{
|
120
120
|
// this can happen if it's a non-SDK-style project; totally normal, not worth examining the binlog
|
@@ -4,6 +4,8 @@ using System.Text;
|
|
4
4
|
using System.Text.Json;
|
5
5
|
using System.Text.Json.Serialization;
|
6
6
|
|
7
|
+
using Microsoft.Extensions.FileSystemGlobbing;
|
8
|
+
|
7
9
|
using NuGet.Versioning;
|
8
10
|
|
9
11
|
using NuGetUpdater.Core.Analyze;
|
@@ -115,161 +117,145 @@ public class RunWorker
|
|
115
117
|
await _apiHandler.UpdateDependencyList(discoveredUpdatedDependencies);
|
116
118
|
|
117
119
|
// TODO: pull out relevant dependencies, then check each for updates and track the changes
|
118
|
-
// TODO: for each top-level dependency, _or_ specific dependency (if security, use transitive)
|
119
120
|
var originalDependencyFileContents = new Dictionary<string, string>();
|
120
121
|
var actualUpdatedDependencies = new List<ReportedDependency>();
|
121
|
-
|
122
|
+
await _apiHandler.IncrementMetric(new()
|
122
123
|
{
|
123
|
-
|
124
|
-
{
|
125
|
-
|
126
|
-
|
127
|
-
|
124
|
+
Metric = "updater.started",
|
125
|
+
Tags = { ["operation"] = "group_update_all_versions" },
|
126
|
+
});
|
127
|
+
|
128
|
+
// track original contents for later handling
|
129
|
+
async Task TrackOriginalContentsAsync(string directory, string fileName)
|
130
|
+
{
|
131
|
+
var repoFullPath = Path.Join(directory, fileName).FullyNormalizedRootedPath();
|
132
|
+
var localFullPath = Path.Join(repoContentsPath.FullName, repoFullPath);
|
133
|
+
var content = await File.ReadAllTextAsync(localFullPath);
|
134
|
+
originalDependencyFileContents[repoFullPath] = content;
|
135
|
+
}
|
128
136
|
|
129
|
-
|
130
|
-
|
137
|
+
foreach (var project in discoveryResult.Projects)
|
138
|
+
{
|
139
|
+
var projectDirectory = Path.GetDirectoryName(project.FilePath);
|
140
|
+
await TrackOriginalContentsAsync(discoveryResult.Path, project.FilePath);
|
141
|
+
foreach (var extraFile in project.ImportedFiles.Concat(project.AdditionalFiles))
|
131
142
|
{
|
132
|
-
var
|
133
|
-
|
134
|
-
var content = await File.ReadAllTextAsync(localFullPath);
|
135
|
-
originalDependencyFileContents[repoFullPath] = content;
|
143
|
+
var extraFilePath = Path.Join(projectDirectory, extraFile);
|
144
|
+
await TrackOriginalContentsAsync(discoveryResult.Path, extraFilePath);
|
136
145
|
}
|
146
|
+
// TODO: include global.json, etc.
|
147
|
+
}
|
137
148
|
|
138
|
-
|
149
|
+
// do update
|
150
|
+
_logger.Info($"Running update in directory {repoDirectory}");
|
151
|
+
foreach (var project in discoveryResult.Projects)
|
152
|
+
{
|
153
|
+
foreach (var dependency in project.Dependencies)
|
139
154
|
{
|
140
|
-
|
141
|
-
await TrackOriginalContentsAsync(discoveryResult.Path, project.FilePath);
|
142
|
-
foreach (var extraFile in project.ImportedFiles.Concat(project.AdditionalFiles))
|
155
|
+
if (!IsUpdateAllowed(job, dependency))
|
143
156
|
{
|
144
|
-
|
145
|
-
await TrackOriginalContentsAsync(discoveryResult.Path, extraFilePath);
|
157
|
+
continue;
|
146
158
|
}
|
147
|
-
// TODO: include global.json, etc.
|
148
|
-
}
|
149
159
|
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
foreach (var dependency in project.Dependencies.Where(d => !d.IsTransitive))
|
160
|
+
var dependencyInfo = GetDependencyInfo(job, dependency);
|
161
|
+
var analysisResult = await _analyzeWorker.RunAsync(repoContentsPath.FullName, discoveryResult, dependencyInfo);
|
162
|
+
// TODO: log analysisResult
|
163
|
+
if (analysisResult.CanUpdate)
|
155
164
|
{
|
156
|
-
|
157
|
-
{
|
158
|
-
// this can't be updated
|
159
|
-
// TODO: pull this out of discovery?
|
160
|
-
continue;
|
161
|
-
}
|
162
|
-
|
163
|
-
if (dependency.Version is null)
|
164
|
-
{
|
165
|
-
// if we don't know the version, there's nothing we can do
|
166
|
-
continue;
|
167
|
-
}
|
165
|
+
var dependencyLocation = Path.Join(discoveryResult.Path, project.FilePath).FullyNormalizedRootedPath();
|
168
166
|
|
169
|
-
|
170
|
-
var
|
171
|
-
|
172
|
-
|
167
|
+
// TODO: this is inefficient, but not likely causing a bottleneck
|
168
|
+
var previousDependency = discoveredUpdatedDependencies.Dependencies
|
169
|
+
.Single(d => d.Name == dependency.Name && d.Requirements.Single().File == dependencyLocation);
|
170
|
+
var updatedDependency = new ReportedDependency()
|
173
171
|
{
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
var updatedDependency = new ReportedDependency()
|
180
|
-
{
|
181
|
-
Name = dependency.Name,
|
182
|
-
Version = analysisResult.UpdatedVersion,
|
183
|
-
Requirements =
|
184
|
-
[
|
185
|
-
new ReportedRequirement()
|
186
|
-
{
|
187
|
-
File = dependencyLocation,
|
188
|
-
Requirement = analysisResult.UpdatedVersion,
|
189
|
-
Groups = previousDependency.Requirements.Single().Groups,
|
190
|
-
Source = new RequirementSource()
|
191
|
-
{
|
192
|
-
SourceUrl = analysisResult.UpdatedDependencies.FirstOrDefault(d => d.Name == dependency.Name)?.InfoUrl,
|
193
|
-
},
|
194
|
-
}
|
195
|
-
],
|
196
|
-
PreviousVersion = dependency.Version,
|
197
|
-
PreviousRequirements = previousDependency.Requirements,
|
198
|
-
};
|
199
|
-
|
200
|
-
var dependencyFilePath = Path.Join(discoveryResult.Path, project.FilePath).FullyNormalizedRootedPath();
|
201
|
-
var updateResult = await _updaterWorker.RunAsync(repoContentsPath.FullName, dependencyFilePath, dependency.Name, dependency.Version!, analysisResult.UpdatedVersion, isTransitive: false);
|
202
|
-
// TODO: need to report if anything was actually updated
|
203
|
-
if (updateResult.Error is null)
|
204
|
-
{
|
205
|
-
if (dependencyLocation != dependencyFilePath)
|
172
|
+
Name = dependency.Name,
|
173
|
+
Version = analysisResult.UpdatedVersion,
|
174
|
+
Requirements =
|
175
|
+
[
|
176
|
+
new ReportedRequirement()
|
206
177
|
{
|
207
|
-
|
178
|
+
File = dependencyLocation,
|
179
|
+
Requirement = analysisResult.UpdatedVersion,
|
180
|
+
Groups = previousDependency.Requirements.Single().Groups,
|
181
|
+
Source = new RequirementSource()
|
182
|
+
{
|
183
|
+
SourceUrl = analysisResult.UpdatedDependencies.FirstOrDefault(d => d.Name == dependency.Name)?.InfoUrl,
|
184
|
+
},
|
208
185
|
}
|
186
|
+
],
|
187
|
+
PreviousVersion = dependency.Version,
|
188
|
+
PreviousRequirements = previousDependency.Requirements,
|
189
|
+
};
|
209
190
|
|
210
|
-
|
191
|
+
var dependencyFilePath = Path.Join(discoveryResult.Path, project.FilePath).FullyNormalizedRootedPath();
|
192
|
+
var updateResult = await _updaterWorker.RunAsync(repoContentsPath.FullName, dependencyFilePath, dependency.Name, dependency.Version!, analysisResult.UpdatedVersion, isTransitive: dependency.IsTransitive);
|
193
|
+
// TODO: need to report if anything was actually updated
|
194
|
+
if (updateResult.Error is null)
|
195
|
+
{
|
196
|
+
if (dependencyLocation != dependencyFilePath)
|
197
|
+
{
|
198
|
+
updatedDependency.Requirements.All(r => r.File == dependencyFilePath);
|
211
199
|
}
|
200
|
+
|
201
|
+
actualUpdatedDependencies.Add(updatedDependency);
|
212
202
|
}
|
213
203
|
}
|
214
204
|
}
|
205
|
+
}
|
215
206
|
|
216
|
-
|
217
|
-
|
218
|
-
|
207
|
+
// create PR - we need to manually check file contents; we can't easily use `git status` in tests
|
208
|
+
var updatedDependencyFiles = new Dictionary<string, DependencyFile>();
|
209
|
+
async Task AddUpdatedFileIfDifferentAsync(string directory, string fileName)
|
210
|
+
{
|
211
|
+
var repoFullPath = Path.Join(directory, fileName).FullyNormalizedRootedPath();
|
212
|
+
var localFullPath = Path.GetFullPath(Path.Join(repoContentsPath.FullName, repoFullPath));
|
213
|
+
var originalContent = originalDependencyFileContents[repoFullPath];
|
214
|
+
var updatedContent = await File.ReadAllTextAsync(localFullPath);
|
215
|
+
if (updatedContent != originalContent)
|
219
216
|
{
|
220
|
-
|
221
|
-
var localFullPath = Path.GetFullPath(Path.Join(repoContentsPath.FullName, repoFullPath));
|
222
|
-
var originalContent = originalDependencyFileContents[repoFullPath];
|
223
|
-
var updatedContent = await File.ReadAllTextAsync(localFullPath);
|
224
|
-
if (updatedContent != originalContent)
|
217
|
+
updatedDependencyFiles[localFullPath] = new DependencyFile()
|
225
218
|
{
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
Content = updatedContent,
|
231
|
-
};
|
232
|
-
}
|
219
|
+
Name = Path.GetFileName(repoFullPath),
|
220
|
+
Directory = Path.GetDirectoryName(repoFullPath)!.NormalizePathToUnix(),
|
221
|
+
Content = updatedContent,
|
222
|
+
};
|
233
223
|
}
|
224
|
+
}
|
234
225
|
|
235
|
-
|
226
|
+
foreach (var project in discoveryResult.Projects)
|
227
|
+
{
|
228
|
+
await AddUpdatedFileIfDifferentAsync(discoveryResult.Path, project.FilePath);
|
229
|
+
var projectDirectory = Path.GetDirectoryName(project.FilePath);
|
230
|
+
foreach (var extraFile in project.ImportedFiles.Concat(project.AdditionalFiles))
|
236
231
|
{
|
237
|
-
|
238
|
-
|
239
|
-
foreach (var extraFile in project.ImportedFiles.Concat(project.AdditionalFiles))
|
240
|
-
{
|
241
|
-
var extraFilePath = Path.Join(projectDirectory, extraFile);
|
242
|
-
await AddUpdatedFileIfDifferentAsync(discoveryResult.Path, extraFilePath);
|
243
|
-
}
|
244
|
-
// TODO: handle global.json, etc.
|
232
|
+
var extraFilePath = Path.Join(projectDirectory, extraFile);
|
233
|
+
await AddUpdatedFileIfDifferentAsync(discoveryResult.Path, extraFilePath);
|
245
234
|
}
|
235
|
+
// TODO: handle global.json, etc.
|
236
|
+
}
|
246
237
|
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
{
|
255
|
-
Dependencies = actualUpdatedDependencies.ToArray(),
|
256
|
-
UpdatedDependencyFiles = updatedDependencyFileList,
|
257
|
-
BaseCommitSha = baseCommitSha,
|
258
|
-
CommitMessage = "TODO: message",
|
259
|
-
PrTitle = "TODO: title",
|
260
|
-
PrBody = "TODO: body",
|
261
|
-
};
|
262
|
-
await _apiHandler.CreatePullRequest(createPullRequest);
|
263
|
-
// TODO: log updated dependencies to console
|
264
|
-
}
|
265
|
-
else
|
238
|
+
if (updatedDependencyFiles.Count > 0)
|
239
|
+
{
|
240
|
+
var updatedDependencyFileList = updatedDependencyFiles
|
241
|
+
.OrderBy(kvp => kvp.Key)
|
242
|
+
.Select(kvp => kvp.Value)
|
243
|
+
.ToArray();
|
244
|
+
var createPullRequest = new CreatePullRequest()
|
266
245
|
{
|
267
|
-
|
268
|
-
|
246
|
+
Dependencies = actualUpdatedDependencies.ToArray(),
|
247
|
+
UpdatedDependencyFiles = updatedDependencyFileList,
|
248
|
+
BaseCommitSha = baseCommitSha,
|
249
|
+
CommitMessage = "TODO: message",
|
250
|
+
PrTitle = "TODO: title",
|
251
|
+
PrBody = "TODO: body",
|
252
|
+
};
|
253
|
+
await _apiHandler.CreatePullRequest(createPullRequest);
|
254
|
+
// TODO: log updated dependencies to console
|
269
255
|
}
|
270
256
|
else
|
271
257
|
{
|
272
|
-
// TODO: throw if
|
258
|
+
// TODO: log or throw if nothing was updated, but was expected to be
|
273
259
|
}
|
274
260
|
|
275
261
|
var result = new RunResult()
|
@@ -289,6 +275,62 @@ public class RunWorker
|
|
289
275
|
return result;
|
290
276
|
}
|
291
277
|
|
278
|
+
internal static bool IsUpdateAllowed(Job job, Dependency dependency)
|
279
|
+
{
|
280
|
+
if (dependency.Name.Equals("Microsoft.NET.Sdk", StringComparison.OrdinalIgnoreCase))
|
281
|
+
{
|
282
|
+
// this can't be updated
|
283
|
+
// TODO: pull this out of discovery?
|
284
|
+
return false;
|
285
|
+
}
|
286
|
+
|
287
|
+
if (dependency.Version is null)
|
288
|
+
{
|
289
|
+
// if we don't know the version, there's nothing we can do
|
290
|
+
// TODO: pull this out of discovery?
|
291
|
+
return false;
|
292
|
+
}
|
293
|
+
|
294
|
+
var version = NuGetVersion.Parse(dependency.Version);
|
295
|
+
var dependencyInfo = GetDependencyInfo(job, dependency);
|
296
|
+
var isVulnerable = dependencyInfo.Vulnerabilities.Any(v => v.IsVulnerable(version));
|
297
|
+
var allowed = job.AllowedUpdates.Any(allowedUpdate =>
|
298
|
+
{
|
299
|
+
// check name restriction, if any
|
300
|
+
if (allowedUpdate.DependencyName is not null)
|
301
|
+
{
|
302
|
+
var matcher = new Matcher(StringComparison.OrdinalIgnoreCase)
|
303
|
+
.AddInclude(allowedUpdate.DependencyName);
|
304
|
+
var result = matcher.Match(dependency.Name);
|
305
|
+
if (!result.HasMatches)
|
306
|
+
{
|
307
|
+
return false;
|
308
|
+
}
|
309
|
+
}
|
310
|
+
|
311
|
+
var isSecurityUpdate = allowedUpdate.UpdateType == UpdateType.Security || job.SecurityUpdatesOnly;
|
312
|
+
if (isSecurityUpdate)
|
313
|
+
{
|
314
|
+
// only update if it's vulnerable
|
315
|
+
return isVulnerable;
|
316
|
+
}
|
317
|
+
else
|
318
|
+
{
|
319
|
+
// not a security update, so only update if...
|
320
|
+
// ...we've been explicitly asked to update this
|
321
|
+
if ((job.Dependencies ?? []).Any(d => d.Equals(dependency.Name, StringComparison.OrdinalIgnoreCase)))
|
322
|
+
{
|
323
|
+
return true;
|
324
|
+
}
|
325
|
+
|
326
|
+
// ...no specific update being performed, do it if it's not transitive
|
327
|
+
return !dependency.IsTransitive;
|
328
|
+
}
|
329
|
+
});
|
330
|
+
|
331
|
+
return allowed;
|
332
|
+
}
|
333
|
+
|
292
334
|
internal static ImmutableArray<Requirement> GetIgnoredRequirementsForDependency(Job job, string dependencyName)
|
293
335
|
{
|
294
336
|
var ignoreConditions = job.IgnoreConditions
|
@@ -375,7 +417,7 @@ public class RunWorker
|
|
375
417
|
new ReportedDependency()
|
376
418
|
{
|
377
419
|
Name = d.Name,
|
378
|
-
Requirements =
|
420
|
+
Requirements = [new ReportedRequirement()
|
379
421
|
{
|
380
422
|
File = GetFullRepoPath(p.FilePath),
|
381
423
|
Requirement = d.Version!,
|
@@ -347,7 +347,7 @@ internal static class PackageReferenceUpdater
|
|
347
347
|
projectDirectory,
|
348
348
|
experimentsManager
|
349
349
|
);
|
350
|
-
MSBuildHelper.
|
350
|
+
MSBuildHelper.ThrowOnError(stdout);
|
351
351
|
if (exitCode != 0)
|
352
352
|
{
|
353
353
|
logger.Warn($" Transitive dependency [{dependencyName}/{newDependencyVersion}] was not added.\nSTDOUT:\n{stdout}\nSTDERR:\n{stderr}");
|
@@ -148,18 +148,15 @@ internal static partial class PackagesConfigUpdater
|
|
148
148
|
|
149
149
|
if (exitCodeAgain != 0)
|
150
150
|
{
|
151
|
-
MSBuildHelper.
|
152
|
-
MSBuildHelper.
|
153
|
-
MSBuildHelper.ThrowOnMissingPackages(restoreOutput);
|
151
|
+
MSBuildHelper.ThrowOnError(fullOutput);
|
152
|
+
MSBuildHelper.ThrowOnError(restoreOutput);
|
154
153
|
throw new Exception($"Unable to restore.\nOutput:\n${restoreOutput}\n");
|
155
154
|
}
|
156
155
|
|
157
156
|
goto doRestore;
|
158
157
|
}
|
159
158
|
|
160
|
-
MSBuildHelper.
|
161
|
-
MSBuildHelper.ThrowOnMissingFile(fullOutput);
|
162
|
-
MSBuildHelper.ThrowOnMissingPackages(fullOutput);
|
159
|
+
MSBuildHelper.ThrowOnError(fullOutput);
|
163
160
|
throw new Exception(fullOutput);
|
164
161
|
}
|
165
162
|
}
|
@@ -903,21 +903,6 @@ internal static partial class MSBuildHelper
|
|
903
903
|
}
|
904
904
|
}
|
905
905
|
|
906
|
-
internal static void ThrowOnUnauthenticatedFeed(string stdout)
|
907
|
-
{
|
908
|
-
var unauthorizedMessageSnippets = new string[]
|
909
|
-
{
|
910
|
-
"The plugin credential provider could not acquire credentials",
|
911
|
-
"401 (Unauthorized)",
|
912
|
-
"error NU1301: Unable to load the service index for source",
|
913
|
-
"Response status code does not indicate success: 403",
|
914
|
-
};
|
915
|
-
if (unauthorizedMessageSnippets.Any(stdout.Contains))
|
916
|
-
{
|
917
|
-
throw new HttpRequestException(message: stdout, inner: null, statusCode: System.Net.HttpStatusCode.Unauthorized);
|
918
|
-
}
|
919
|
-
}
|
920
|
-
|
921
906
|
internal static string? GetMissingFile(string output)
|
922
907
|
{
|
923
908
|
var missingFilePatterns = new[]
|
@@ -934,7 +919,30 @@ internal static partial class MSBuildHelper
|
|
934
919
|
return null;
|
935
920
|
}
|
936
921
|
|
937
|
-
internal static void
|
922
|
+
internal static void ThrowOnError(string output)
|
923
|
+
{
|
924
|
+
ThrowOnUnauthenticatedFeed(output);
|
925
|
+
ThrowOnMissingFile(output);
|
926
|
+
ThrowOnMissingPackages(output);
|
927
|
+
ThrowOnUnresolvableDependencies(output);
|
928
|
+
}
|
929
|
+
|
930
|
+
private static void ThrowOnUnauthenticatedFeed(string stdout)
|
931
|
+
{
|
932
|
+
var unauthorizedMessageSnippets = new string[]
|
933
|
+
{
|
934
|
+
"The plugin credential provider could not acquire credentials",
|
935
|
+
"401 (Unauthorized)",
|
936
|
+
"error NU1301: Unable to load the service index for source",
|
937
|
+
"Response status code does not indicate success: 403",
|
938
|
+
};
|
939
|
+
if (unauthorizedMessageSnippets.Any(stdout.Contains))
|
940
|
+
{
|
941
|
+
throw new HttpRequestException(message: stdout, inner: null, statusCode: System.Net.HttpStatusCode.Unauthorized);
|
942
|
+
}
|
943
|
+
}
|
944
|
+
|
945
|
+
private static void ThrowOnMissingFile(string output)
|
938
946
|
{
|
939
947
|
var missingFile = GetMissingFile(output);
|
940
948
|
if (missingFile is not null)
|
@@ -943,9 +951,9 @@ internal static partial class MSBuildHelper
|
|
943
951
|
}
|
944
952
|
}
|
945
953
|
|
946
|
-
|
954
|
+
private static void ThrowOnMissingPackages(string output)
|
947
955
|
{
|
948
|
-
var missingPackagesPattern = new Regex(@"Package '(?<PackageName>[^']
|
956
|
+
var missingPackagesPattern = new Regex(@"Package '(?<PackageName>[^']*)' is not found on source");
|
949
957
|
var matchCollection = missingPackagesPattern.Matches(output);
|
950
958
|
var missingPackages = matchCollection.Select(m => m.Groups["PackageName"].Value).Distinct().ToArray();
|
951
959
|
if (missingPackages.Length > 0)
|
@@ -954,6 +962,16 @@ internal static partial class MSBuildHelper
|
|
954
962
|
}
|
955
963
|
}
|
956
964
|
|
965
|
+
private static void ThrowOnUnresolvableDependencies(string output)
|
966
|
+
{
|
967
|
+
var unresolvablePackagePattern = new Regex(@"Unable to resolve dependencies\. '(?<PackageName>[^ ]+) (?<PackageVersion>[^']+)'");
|
968
|
+
var match = unresolvablePackagePattern.Match(output);
|
969
|
+
if (match.Success)
|
970
|
+
{
|
971
|
+
throw new UpdateNotPossibleException([$"{match.Groups["PackageName"].Value}.{match.Groups["PackageVersion"].Value}"]);
|
972
|
+
}
|
973
|
+
}
|
974
|
+
|
957
975
|
internal static bool TryGetGlobalJsonPath(string repoRootPath, string workspacePath, [NotNullWhen(returnValue: true)] out string? globalJsonPath)
|
958
976
|
{
|
959
977
|
globalJsonPath = PathHelper.GetFileInDirectoryOrParent(workspacePath, repoRootPath, "global.json", caseSensitive: false);
|
@@ -1711,6 +1711,344 @@ public class RunWorkerTests
|
|
1711
1711
|
);
|
1712
1712
|
}
|
1713
1713
|
|
1714
|
+
[Fact]
|
1715
|
+
public async Task UpdatePackageWithDifferentVersionsInDifferentDirectories()
|
1716
|
+
{
|
1717
|
+
// this test passes `null` for discovery, analyze, and update workers to fully test the desired behavior
|
1718
|
+
|
1719
|
+
// the same dependency Some.Package is reported for 3 cases:
|
1720
|
+
// library1.csproj - top level dependency, already up to date
|
1721
|
+
// library2.csproj - top level dependency, needs direct update
|
1722
|
+
// library3.csproj - transitive dependency, needs pin
|
1723
|
+
await RunAsync(
|
1724
|
+
experimentsManager: new ExperimentsManager() { UseDirectDiscovery = true },
|
1725
|
+
packages: [
|
1726
|
+
MockNuGetPackage.CreateSimplePackage("Some.Package", "1.0.0", "net8.0"),
|
1727
|
+
MockNuGetPackage.CreateSimplePackage("Some.Package", "2.0.0", "net8.0"),
|
1728
|
+
MockNuGetPackage.CreateSimplePackage("Package.With.Transitive.Dependency", "0.1.0", "net8.0", [(null, [("Some.Package", "1.0.0")])]),
|
1729
|
+
],
|
1730
|
+
job: new Job()
|
1731
|
+
{
|
1732
|
+
AllowedUpdates = [new() { UpdateType = UpdateType.Security }],
|
1733
|
+
SecurityAdvisories =
|
1734
|
+
[
|
1735
|
+
new()
|
1736
|
+
{
|
1737
|
+
DependencyName = "Some.Package",
|
1738
|
+
AffectedVersions = [Requirement.Parse("= 1.0.0")]
|
1739
|
+
}
|
1740
|
+
],
|
1741
|
+
Source = new()
|
1742
|
+
{
|
1743
|
+
Provider = "github",
|
1744
|
+
Repo = "test/repo",
|
1745
|
+
Directory = "/"
|
1746
|
+
}
|
1747
|
+
},
|
1748
|
+
files: [
|
1749
|
+
("dirs.proj", """
|
1750
|
+
<Project>
|
1751
|
+
<ItemGroup>
|
1752
|
+
<ProjectFile Include="library1\library1.csproj" />
|
1753
|
+
<ProjectFile Include="library2\library2.csproj" />
|
1754
|
+
<ProjectFile Include="library3\library3.csproj" />
|
1755
|
+
</ItemGroup>
|
1756
|
+
</Project>
|
1757
|
+
"""),
|
1758
|
+
("Directory.Build.props", "<Project />"),
|
1759
|
+
("Directory.Build.targets", "<Project />"),
|
1760
|
+
("Directory.Packages.props", """
|
1761
|
+
<Project>
|
1762
|
+
<PropertyGroup>
|
1763
|
+
<ManagePackageVersionsCentrally>false</ManagePackageVersionsCentrally>
|
1764
|
+
</PropertyGroup>
|
1765
|
+
</Project>
|
1766
|
+
"""),
|
1767
|
+
("library1/library1.csproj", """
|
1768
|
+
<Project Sdk="Microsoft.NET.Sdk">
|
1769
|
+
<PropertyGroup>
|
1770
|
+
<TargetFramework>net8.0</TargetFramework>
|
1771
|
+
</PropertyGroup>
|
1772
|
+
<ItemGroup>
|
1773
|
+
<PackageReference Include="Some.Package" Version="2.0.0" />
|
1774
|
+
</ItemGroup>
|
1775
|
+
</Project>
|
1776
|
+
"""),
|
1777
|
+
("library2/library2.csproj", """
|
1778
|
+
<Project Sdk="Microsoft.NET.Sdk">
|
1779
|
+
<PropertyGroup>
|
1780
|
+
<TargetFramework>net8.0</TargetFramework>
|
1781
|
+
</PropertyGroup>
|
1782
|
+
<ItemGroup>
|
1783
|
+
<PackageReference Include="Some.Package" Version="1.0.0" />
|
1784
|
+
</ItemGroup>
|
1785
|
+
</Project>
|
1786
|
+
"""),
|
1787
|
+
("library3/library3.csproj", """
|
1788
|
+
<Project Sdk="Microsoft.NET.Sdk">
|
1789
|
+
<PropertyGroup>
|
1790
|
+
<TargetFramework>net8.0</TargetFramework>
|
1791
|
+
</PropertyGroup>
|
1792
|
+
<ItemGroup>
|
1793
|
+
<PackageReference Include="Package.With.Transitive.Dependency" Version="0.1.0" />
|
1794
|
+
</ItemGroup>
|
1795
|
+
</Project>
|
1796
|
+
"""),
|
1797
|
+
],
|
1798
|
+
discoveryWorker: null,
|
1799
|
+
analyzeWorker: null,
|
1800
|
+
updaterWorker: null,
|
1801
|
+
expectedResult: new RunResult()
|
1802
|
+
{
|
1803
|
+
Base64DependencyFiles =
|
1804
|
+
[
|
1805
|
+
new DependencyFile()
|
1806
|
+
{
|
1807
|
+
Directory = "/",
|
1808
|
+
Name = "Directory.Build.props",
|
1809
|
+
Content = Convert.ToBase64String(Encoding.UTF8.GetBytes("<Project />"))
|
1810
|
+
},
|
1811
|
+
new DependencyFile()
|
1812
|
+
{
|
1813
|
+
Directory = "/",
|
1814
|
+
Name = "Directory.Build.targets",
|
1815
|
+
Content = Convert.ToBase64String(Encoding.UTF8.GetBytes("<Project />"))
|
1816
|
+
},
|
1817
|
+
new DependencyFile()
|
1818
|
+
{
|
1819
|
+
Directory = "/",
|
1820
|
+
Name = "Directory.Packages.props",
|
1821
|
+
Content = Convert.ToBase64String(Encoding.UTF8.GetBytes("""
|
1822
|
+
<Project>
|
1823
|
+
<PropertyGroup>
|
1824
|
+
<ManagePackageVersionsCentrally>false</ManagePackageVersionsCentrally>
|
1825
|
+
</PropertyGroup>
|
1826
|
+
</Project>
|
1827
|
+
"""))
|
1828
|
+
},
|
1829
|
+
new DependencyFile()
|
1830
|
+
{
|
1831
|
+
Directory = "/library1",
|
1832
|
+
Name = "library1.csproj",
|
1833
|
+
Content = Convert.ToBase64String(Encoding.UTF8.GetBytes("""
|
1834
|
+
<Project Sdk="Microsoft.NET.Sdk">
|
1835
|
+
<PropertyGroup>
|
1836
|
+
<TargetFramework>net8.0</TargetFramework>
|
1837
|
+
</PropertyGroup>
|
1838
|
+
<ItemGroup>
|
1839
|
+
<PackageReference Include="Some.Package" Version="2.0.0" />
|
1840
|
+
</ItemGroup>
|
1841
|
+
</Project>
|
1842
|
+
"""))
|
1843
|
+
},
|
1844
|
+
new DependencyFile()
|
1845
|
+
{
|
1846
|
+
Directory = "/library2",
|
1847
|
+
Name = "library2.csproj",
|
1848
|
+
Content = Convert.ToBase64String(Encoding.UTF8.GetBytes("""
|
1849
|
+
<Project Sdk="Microsoft.NET.Sdk">
|
1850
|
+
<PropertyGroup>
|
1851
|
+
<TargetFramework>net8.0</TargetFramework>
|
1852
|
+
</PropertyGroup>
|
1853
|
+
<ItemGroup>
|
1854
|
+
<PackageReference Include="Some.Package" Version="1.0.0" />
|
1855
|
+
</ItemGroup>
|
1856
|
+
</Project>
|
1857
|
+
"""))
|
1858
|
+
},
|
1859
|
+
new DependencyFile()
|
1860
|
+
{
|
1861
|
+
Directory = "/library3",
|
1862
|
+
Name = "library3.csproj",
|
1863
|
+
Content = Convert.ToBase64String(Encoding.UTF8.GetBytes("""
|
1864
|
+
<Project Sdk="Microsoft.NET.Sdk">
|
1865
|
+
<PropertyGroup>
|
1866
|
+
<TargetFramework>net8.0</TargetFramework>
|
1867
|
+
</PropertyGroup>
|
1868
|
+
<ItemGroup>
|
1869
|
+
<PackageReference Include="Package.With.Transitive.Dependency" Version="0.1.0" />
|
1870
|
+
</ItemGroup>
|
1871
|
+
</Project>
|
1872
|
+
"""))
|
1873
|
+
}
|
1874
|
+
],
|
1875
|
+
BaseCommitSha = "TEST-COMMIT-SHA",
|
1876
|
+
},
|
1877
|
+
expectedApiMessages: [
|
1878
|
+
new UpdatedDependencyList()
|
1879
|
+
{
|
1880
|
+
Dependencies = [
|
1881
|
+
new()
|
1882
|
+
{
|
1883
|
+
Name = "Some.Package",
|
1884
|
+
Version = "2.0.0",
|
1885
|
+
Requirements = [
|
1886
|
+
new()
|
1887
|
+
{
|
1888
|
+
Requirement = "2.0.0",
|
1889
|
+
File = "/library1/library1.csproj",
|
1890
|
+
Groups = ["dependencies"],
|
1891
|
+
}
|
1892
|
+
]
|
1893
|
+
},
|
1894
|
+
new()
|
1895
|
+
{
|
1896
|
+
Name = "Some.Package",
|
1897
|
+
Version = "1.0.0",
|
1898
|
+
Requirements = [
|
1899
|
+
new()
|
1900
|
+
{
|
1901
|
+
Requirement = "1.0.0",
|
1902
|
+
File = "/library2/library2.csproj",
|
1903
|
+
Groups = ["dependencies"],
|
1904
|
+
}
|
1905
|
+
]
|
1906
|
+
},
|
1907
|
+
new()
|
1908
|
+
{
|
1909
|
+
Name = "Package.With.Transitive.Dependency",
|
1910
|
+
Version = "0.1.0",
|
1911
|
+
Requirements = [
|
1912
|
+
new()
|
1913
|
+
{
|
1914
|
+
Requirement = "0.1.0",
|
1915
|
+
File = "/library3/library3.csproj",
|
1916
|
+
Groups = ["dependencies"],
|
1917
|
+
}
|
1918
|
+
]
|
1919
|
+
},
|
1920
|
+
new()
|
1921
|
+
{
|
1922
|
+
Name = "Some.Package",
|
1923
|
+
Version = "1.0.0",
|
1924
|
+
Requirements = [
|
1925
|
+
new()
|
1926
|
+
{
|
1927
|
+
Requirement = "1.0.0",
|
1928
|
+
File = "/library3/library3.csproj",
|
1929
|
+
Groups = ["dependencies"],
|
1930
|
+
}
|
1931
|
+
]
|
1932
|
+
},
|
1933
|
+
],
|
1934
|
+
DependencyFiles = [
|
1935
|
+
"/Directory.Build.props",
|
1936
|
+
"/Directory.Build.targets",
|
1937
|
+
"/Directory.Packages.props",
|
1938
|
+
"/library1/library1.csproj",
|
1939
|
+
"/library2/library2.csproj",
|
1940
|
+
"/library3/library3.csproj",
|
1941
|
+
],
|
1942
|
+
},
|
1943
|
+
new IncrementMetric()
|
1944
|
+
{
|
1945
|
+
Metric = "updater.started",
|
1946
|
+
Tags = new()
|
1947
|
+
{
|
1948
|
+
["operation"] = "group_update_all_versions"
|
1949
|
+
}
|
1950
|
+
},
|
1951
|
+
new CreatePullRequest()
|
1952
|
+
{
|
1953
|
+
Dependencies = [
|
1954
|
+
new()
|
1955
|
+
{
|
1956
|
+
Name = "Some.Package",
|
1957
|
+
Version = "2.0.0",
|
1958
|
+
Requirements = [
|
1959
|
+
new()
|
1960
|
+
{
|
1961
|
+
Requirement = "2.0.0",
|
1962
|
+
File = "/library2/library2.csproj",
|
1963
|
+
Groups = ["dependencies"],
|
1964
|
+
Source = new()
|
1965
|
+
{
|
1966
|
+
SourceUrl = null,
|
1967
|
+
Type = "nuget_repo",
|
1968
|
+
}
|
1969
|
+
}
|
1970
|
+
],
|
1971
|
+
PreviousVersion = "1.0.0",
|
1972
|
+
PreviousRequirements = [
|
1973
|
+
new()
|
1974
|
+
{
|
1975
|
+
Requirement = "1.0.0",
|
1976
|
+
File = "/library2/library2.csproj",
|
1977
|
+
Groups = ["dependencies"],
|
1978
|
+
}
|
1979
|
+
],
|
1980
|
+
},
|
1981
|
+
new()
|
1982
|
+
{
|
1983
|
+
Name = "Some.Package",
|
1984
|
+
Version = "2.0.0",
|
1985
|
+
Requirements = [
|
1986
|
+
new()
|
1987
|
+
{
|
1988
|
+
Requirement = "2.0.0",
|
1989
|
+
File = "/library3/library3.csproj",
|
1990
|
+
Groups = ["dependencies"],
|
1991
|
+
Source = new()
|
1992
|
+
{
|
1993
|
+
SourceUrl = null,
|
1994
|
+
Type = "nuget_repo",
|
1995
|
+
}
|
1996
|
+
}
|
1997
|
+
],
|
1998
|
+
PreviousVersion = "1.0.0",
|
1999
|
+
PreviousRequirements = [
|
2000
|
+
new()
|
2001
|
+
{
|
2002
|
+
Requirement = "1.0.0",
|
2003
|
+
File = "/library3/library3.csproj",
|
2004
|
+
Groups = ["dependencies"],
|
2005
|
+
}
|
2006
|
+
],
|
2007
|
+
},
|
2008
|
+
],
|
2009
|
+
UpdatedDependencyFiles = [
|
2010
|
+
new()
|
2011
|
+
{
|
2012
|
+
Directory = "/library2",
|
2013
|
+
Name = "library2.csproj",
|
2014
|
+
Content = """
|
2015
|
+
<Project Sdk="Microsoft.NET.Sdk">
|
2016
|
+
<PropertyGroup>
|
2017
|
+
<TargetFramework>net8.0</TargetFramework>
|
2018
|
+
</PropertyGroup>
|
2019
|
+
<ItemGroup>
|
2020
|
+
<PackageReference Include="Some.Package" Version="2.0.0" />
|
2021
|
+
</ItemGroup>
|
2022
|
+
</Project>
|
2023
|
+
"""
|
2024
|
+
},
|
2025
|
+
new()
|
2026
|
+
{
|
2027
|
+
Directory = "/library3",
|
2028
|
+
Name = "library3.csproj",
|
2029
|
+
Content = """
|
2030
|
+
<Project Sdk="Microsoft.NET.Sdk">
|
2031
|
+
<PropertyGroup>
|
2032
|
+
<TargetFramework>net8.0</TargetFramework>
|
2033
|
+
</PropertyGroup>
|
2034
|
+
<ItemGroup>
|
2035
|
+
<PackageReference Include="Package.With.Transitive.Dependency" Version="0.1.0" />
|
2036
|
+
<PackageReference Include="Some.Package" Version="2.0.0" />
|
2037
|
+
</ItemGroup>
|
2038
|
+
</Project>
|
2039
|
+
"""
|
2040
|
+
}
|
2041
|
+
],
|
2042
|
+
BaseCommitSha = "TEST-COMMIT-SHA",
|
2043
|
+
CommitMessage = "TODO: message",
|
2044
|
+
PrTitle = "TODO: title",
|
2045
|
+
PrBody = "TODO: body"
|
2046
|
+
},
|
2047
|
+
new MarkAsProcessed("TEST-COMMIT-SHA")
|
2048
|
+
]
|
2049
|
+
);
|
2050
|
+
}
|
2051
|
+
|
1714
2052
|
private static async Task RunAsync(Job job, TestFile[] files, IDiscoveryWorker? discoveryWorker, IAnalyzeWorker? analyzeWorker, IUpdaterWorker? updaterWorker, RunResult expectedResult, object[] expectedApiMessages, MockNuGetPackage[]? packages = null, ExperimentsManager? experimentsManager = null, string? repoContentsPath = null)
|
1715
2053
|
{
|
1716
2054
|
// arrange
|
@@ -0,0 +1,286 @@
|
|
1
|
+
using System.Collections.Immutable;
|
2
|
+
|
3
|
+
using NuGetUpdater.Core.Analyze;
|
4
|
+
using NuGetUpdater.Core.Run;
|
5
|
+
using NuGetUpdater.Core.Run.ApiModel;
|
6
|
+
|
7
|
+
using Xunit;
|
8
|
+
|
9
|
+
using DepType = NuGetUpdater.Core.Run.ApiModel.DependencyType;
|
10
|
+
|
11
|
+
namespace NuGetUpdater.Core.Test.Run;
|
12
|
+
|
13
|
+
public class UpdateAllowedTests
|
14
|
+
{
|
15
|
+
[Theory]
|
16
|
+
[MemberData(nameof(IsUpdateAllowedTestData))]
|
17
|
+
public void IsUpdateAllowed(Job job, Dependency dependency, bool expectedResult)
|
18
|
+
{
|
19
|
+
var actualResult = RunWorker.IsUpdateAllowed(job, dependency);
|
20
|
+
Assert.Equal(expectedResult, actualResult);
|
21
|
+
}
|
22
|
+
|
23
|
+
public static IEnumerable<object[]> IsUpdateAllowedTestData()
|
24
|
+
{
|
25
|
+
// with default allowed updates on a transitive dependency
|
26
|
+
yield return
|
27
|
+
[
|
28
|
+
CreateJob(
|
29
|
+
allowedUpdates: [
|
30
|
+
new AllowedUpdate() { DependencyType = DepType.Direct, UpdateType = UpdateType.All }
|
31
|
+
],
|
32
|
+
securityAdvisories: [
|
33
|
+
new Advisory() { DependencyName = "Some.Package", AffectedVersions = [], PatchedVersions = [Requirement.Parse(">= 1.11.0")], UnaffectedVersions = [] }
|
34
|
+
],
|
35
|
+
securityUpdatesOnly: false),
|
36
|
+
new Dependency("Some.Package", "1.8.0", DependencyType.PackageReference, IsTransitive: true),
|
37
|
+
// expectedResult
|
38
|
+
false,
|
39
|
+
];
|
40
|
+
|
41
|
+
// when dealing with a security update
|
42
|
+
yield return
|
43
|
+
[
|
44
|
+
CreateJob(
|
45
|
+
allowedUpdates: [
|
46
|
+
new AllowedUpdate() { DependencyType = DepType.Direct, UpdateType = UpdateType.All }
|
47
|
+
],
|
48
|
+
securityAdvisories: [
|
49
|
+
new Advisory() { DependencyName = "Some.Package", AffectedVersions = [], PatchedVersions = [Requirement.Parse(">= 1.11.0")], UnaffectedVersions = [] }
|
50
|
+
],
|
51
|
+
securityUpdatesOnly: true),
|
52
|
+
new Dependency("Some.Package", "1.8.0", DependencyType.PackageReference, IsTransitive: true),
|
53
|
+
// expectedResult
|
54
|
+
true,
|
55
|
+
];
|
56
|
+
|
57
|
+
// with a top-level dependency
|
58
|
+
yield return
|
59
|
+
[
|
60
|
+
CreateJob(
|
61
|
+
allowedUpdates: [
|
62
|
+
new AllowedUpdate() { DependencyType = DepType.Direct, UpdateType = UpdateType.All },
|
63
|
+
new AllowedUpdate() { DependencyType = DepType.Indirect, UpdateType = UpdateType.Security }
|
64
|
+
],
|
65
|
+
securityAdvisories: [],
|
66
|
+
securityUpdatesOnly: false),
|
67
|
+
new Dependency("Some.Package", "1.8.0", DependencyType.PackageReference, IsTransitive: false),
|
68
|
+
// expectedResult
|
69
|
+
true,
|
70
|
+
];
|
71
|
+
|
72
|
+
// with a sub-dependency
|
73
|
+
yield return
|
74
|
+
[
|
75
|
+
CreateJob(
|
76
|
+
allowedUpdates: [
|
77
|
+
new AllowedUpdate() { DependencyType = DepType.Direct, UpdateType = UpdateType.All },
|
78
|
+
new AllowedUpdate() { DependencyType = DepType.Indirect, UpdateType = UpdateType.Security }
|
79
|
+
],
|
80
|
+
securityAdvisories: [],
|
81
|
+
securityUpdatesOnly: false),
|
82
|
+
new Dependency("Some.Package", "1.8.0", DependencyType.PackageReference, IsTransitive: true),
|
83
|
+
// expectedResult
|
84
|
+
false,
|
85
|
+
];
|
86
|
+
|
87
|
+
// when insecure
|
88
|
+
yield return
|
89
|
+
[
|
90
|
+
CreateJob(
|
91
|
+
allowedUpdates: [
|
92
|
+
new AllowedUpdate() { DependencyType = DepType.Direct, UpdateType = UpdateType.All },
|
93
|
+
new AllowedUpdate() { DependencyType = DepType.Indirect, UpdateType = UpdateType.Security }
|
94
|
+
],
|
95
|
+
securityAdvisories: [
|
96
|
+
new Advisory() { DependencyName = "Some.Package", AffectedVersions = [], PatchedVersions = [Requirement.Parse(">= 1.11.0")], UnaffectedVersions = [] }
|
97
|
+
],
|
98
|
+
securityUpdatesOnly: false),
|
99
|
+
new Dependency("Some.Package", "1.8.0", DependencyType.PackageReference, IsTransitive: true),
|
100
|
+
// expectedResult
|
101
|
+
true,
|
102
|
+
];
|
103
|
+
|
104
|
+
// when only security fixes are allowed
|
105
|
+
yield return
|
106
|
+
[
|
107
|
+
CreateJob(
|
108
|
+
allowedUpdates: [
|
109
|
+
new AllowedUpdate() { DependencyType = DepType.Direct, UpdateType = UpdateType.All },
|
110
|
+
new AllowedUpdate() { DependencyType = DepType.Indirect, UpdateType = UpdateType.Security }
|
111
|
+
],
|
112
|
+
securityAdvisories: [],
|
113
|
+
securityUpdatesOnly: true),
|
114
|
+
new Dependency("Some.Package", "1.8.0", DependencyType.PackageReference, IsTransitive: false),
|
115
|
+
// expectedResult
|
116
|
+
false,
|
117
|
+
];
|
118
|
+
|
119
|
+
// when dealing with a security fix
|
120
|
+
yield return
|
121
|
+
[
|
122
|
+
CreateJob(
|
123
|
+
allowedUpdates: [
|
124
|
+
new AllowedUpdate() { DependencyType = DepType.Direct, UpdateType = UpdateType.All },
|
125
|
+
new AllowedUpdate() { DependencyType = DepType.Indirect, UpdateType = UpdateType.Security }
|
126
|
+
],
|
127
|
+
securityAdvisories: [
|
128
|
+
new Advisory() { DependencyName = "Some.Package", AffectedVersions = [], PatchedVersions = [Requirement.Parse(">= 1.11.0")], UnaffectedVersions = [] }
|
129
|
+
],
|
130
|
+
securityUpdatesOnly: true),
|
131
|
+
new Dependency("Some.Package", "1.8.0", DependencyType.PackageReference, IsTransitive: false),
|
132
|
+
// expectedResult
|
133
|
+
true,
|
134
|
+
];
|
135
|
+
|
136
|
+
// when dealing with a security fix that doesn't apply
|
137
|
+
yield return
|
138
|
+
[
|
139
|
+
CreateJob(
|
140
|
+
allowedUpdates: [
|
141
|
+
new AllowedUpdate() { DependencyType = DepType.Direct, UpdateType = UpdateType.All },
|
142
|
+
new AllowedUpdate() { DependencyType = DepType.Indirect, UpdateType = UpdateType.Security }
|
143
|
+
],
|
144
|
+
securityAdvisories: [
|
145
|
+
new Advisory() { DependencyName = "Some.Package", AffectedVersions = [Requirement.Parse("> 1.8.0")], PatchedVersions = [], UnaffectedVersions = [] }
|
146
|
+
],
|
147
|
+
securityUpdatesOnly: true),
|
148
|
+
new Dependency("Some.Package", "1.8.0", DependencyType.PackageReference, IsTransitive: false),
|
149
|
+
// expectedResult
|
150
|
+
false,
|
151
|
+
];
|
152
|
+
|
153
|
+
// when dealing with a security fix that doesn't apply to some versions
|
154
|
+
yield return
|
155
|
+
[
|
156
|
+
CreateJob(
|
157
|
+
allowedUpdates: [
|
158
|
+
new AllowedUpdate() { DependencyType = DepType.Direct, UpdateType = UpdateType.All },
|
159
|
+
new AllowedUpdate() { DependencyType = DepType.Indirect, UpdateType = UpdateType.Security }
|
160
|
+
],
|
161
|
+
securityAdvisories: [
|
162
|
+
new Advisory() { DependencyName = "Some.Package", AffectedVersions = [Requirement.Parse("< 1.8.0"), Requirement.Parse("> 1.8.0")], PatchedVersions = [], UnaffectedVersions = [] }
|
163
|
+
],
|
164
|
+
securityUpdatesOnly: true),
|
165
|
+
new Dependency("Some.Package", "1.8.1", DependencyType.PackageReference, IsTransitive: false),
|
166
|
+
// expectedResult
|
167
|
+
true,
|
168
|
+
];
|
169
|
+
|
170
|
+
// when a dependency allow list that includes the dependency
|
171
|
+
yield return
|
172
|
+
[
|
173
|
+
CreateJob(
|
174
|
+
allowedUpdates: [
|
175
|
+
new AllowedUpdate() { DependencyName = "Some.Package" }
|
176
|
+
],
|
177
|
+
securityAdvisories: [],
|
178
|
+
securityUpdatesOnly: false),
|
179
|
+
new Dependency("Some.Package", "1.8.0", DependencyType.PackageReference, IsTransitive: false),
|
180
|
+
// expectedResult
|
181
|
+
true,
|
182
|
+
];
|
183
|
+
|
184
|
+
// with a dependency allow list that uses a wildcard
|
185
|
+
yield return
|
186
|
+
[
|
187
|
+
CreateJob(
|
188
|
+
allowedUpdates: [
|
189
|
+
new AllowedUpdate() { DependencyName = "Some.*" }
|
190
|
+
],
|
191
|
+
securityAdvisories: [],
|
192
|
+
securityUpdatesOnly: false),
|
193
|
+
new Dependency("Some.Package", "1.8.0", DependencyType.PackageReference, IsTransitive: false),
|
194
|
+
// expectedResult
|
195
|
+
true,
|
196
|
+
];
|
197
|
+
|
198
|
+
// when dependency allow list that excludes the dependency
|
199
|
+
yield return
|
200
|
+
[
|
201
|
+
CreateJob(
|
202
|
+
allowedUpdates: [
|
203
|
+
new AllowedUpdate() { DependencyName = "Unrelated.Package" }
|
204
|
+
],
|
205
|
+
securityAdvisories: [],
|
206
|
+
securityUpdatesOnly: false),
|
207
|
+
new Dependency("Some.Package", "1.8.0", DependencyType.PackageReference, IsTransitive: false),
|
208
|
+
// expectedResult
|
209
|
+
false,
|
210
|
+
];
|
211
|
+
|
212
|
+
// when matching with an incomplete dependency name
|
213
|
+
yield return
|
214
|
+
[
|
215
|
+
CreateJob(
|
216
|
+
allowedUpdates: [
|
217
|
+
new AllowedUpdate() { DependencyName = "Some" }
|
218
|
+
],
|
219
|
+
securityAdvisories: [],
|
220
|
+
securityUpdatesOnly: false),
|
221
|
+
new Dependency("Some.Package", "1.8.0", DependencyType.PackageReference, IsTransitive: false),
|
222
|
+
// expectedResult
|
223
|
+
false,
|
224
|
+
];
|
225
|
+
|
226
|
+
// with a dependency allow list that uses a wildcard
|
227
|
+
yield return
|
228
|
+
[
|
229
|
+
CreateJob(
|
230
|
+
allowedUpdates: [
|
231
|
+
new AllowedUpdate() { DependencyName = "Unrelated.*" }
|
232
|
+
],
|
233
|
+
securityAdvisories: [],
|
234
|
+
securityUpdatesOnly: false),
|
235
|
+
new Dependency("Some.Package", "1.8.0", DependencyType.PackageReference, IsTransitive: false),
|
236
|
+
// expectedResult
|
237
|
+
false,
|
238
|
+
];
|
239
|
+
|
240
|
+
// when security fixes are also allowed
|
241
|
+
yield return
|
242
|
+
[
|
243
|
+
CreateJob(
|
244
|
+
allowedUpdates: [
|
245
|
+
new AllowedUpdate() { DependencyName = "Unrelated.Package" },
|
246
|
+
new AllowedUpdate() { UpdateType = UpdateType.Security }
|
247
|
+
],
|
248
|
+
securityAdvisories: [],
|
249
|
+
securityUpdatesOnly: false),
|
250
|
+
new Dependency("Some.Package", "1.8.0", DependencyType.PackageReference, IsTransitive: false),
|
251
|
+
// expectedResult
|
252
|
+
false,
|
253
|
+
];
|
254
|
+
|
255
|
+
// when dealing with a security fix
|
256
|
+
yield return
|
257
|
+
[
|
258
|
+
CreateJob(
|
259
|
+
allowedUpdates: [
|
260
|
+
new AllowedUpdate() { DependencyName = "Unrelated.Package"}, new AllowedUpdate(){ UpdateType = UpdateType.Security }
|
261
|
+
],
|
262
|
+
securityAdvisories: [
|
263
|
+
new Advisory() { DependencyName = "Some.Package", AffectedVersions = [], PatchedVersions = [Requirement.Parse(">= 1.11.0")], UnaffectedVersions = [] }
|
264
|
+
],
|
265
|
+
securityUpdatesOnly: false),
|
266
|
+
new Dependency("Some.Package", "1.8.0", DependencyType.PackageReference, IsTransitive: false),
|
267
|
+
// expectedResult
|
268
|
+
true,
|
269
|
+
];
|
270
|
+
}
|
271
|
+
|
272
|
+
private static Job CreateJob(AllowedUpdate[] allowedUpdates, Advisory[] securityAdvisories, bool securityUpdatesOnly)
|
273
|
+
{
|
274
|
+
return new Job()
|
275
|
+
{
|
276
|
+
AllowedUpdates = allowedUpdates.ToImmutableArray(),
|
277
|
+
SecurityAdvisories = securityAdvisories.ToImmutableArray(),
|
278
|
+
SecurityUpdatesOnly = securityUpdatesOnly,
|
279
|
+
Source = new()
|
280
|
+
{
|
281
|
+
Provider = "nuget",
|
282
|
+
Repo = "test/repo",
|
283
|
+
}
|
284
|
+
};
|
285
|
+
}
|
286
|
+
}
|
@@ -94,7 +94,15 @@ public class UpdatedDependencyListTests
|
|
94
94
|
{
|
95
95
|
Name = "System.Text.Json",
|
96
96
|
Version = "6.0.0",
|
97
|
-
Requirements =
|
97
|
+
Requirements =
|
98
|
+
[
|
99
|
+
new ReportedRequirement()
|
100
|
+
{
|
101
|
+
Requirement = "6.0.0",
|
102
|
+
File = "/src/c/project.csproj",
|
103
|
+
Groups = ["dependencies"],
|
104
|
+
}
|
105
|
+
],
|
98
106
|
},
|
99
107
|
new ReportedDependency()
|
100
108
|
{
|
@@ -1,7 +1,8 @@
|
|
1
1
|
using System.Collections.Immutable;
|
2
|
+
using System.Text.Json;
|
2
3
|
|
3
|
-
using
|
4
|
-
|
4
|
+
using NuGetUpdater.Core.Run;
|
5
|
+
using NuGetUpdater.Core.Run.ApiModel;
|
5
6
|
using NuGetUpdater.Core.Test.Update;
|
6
7
|
|
7
8
|
using Xunit;
|
@@ -1366,6 +1367,91 @@ public class MSBuildHelperTests : TestBase
|
|
1366
1367
|
}
|
1367
1368
|
#endregion
|
1368
1369
|
|
1370
|
+
[Theory]
|
1371
|
+
[MemberData(nameof(GenerateErrorFromToolOutputTestData))]
|
1372
|
+
public async Task GenerateErrorFromToolOutput(string output, JobErrorBase? expectedError)
|
1373
|
+
{
|
1374
|
+
Exception? exception = null;
|
1375
|
+
try
|
1376
|
+
{
|
1377
|
+
MSBuildHelper.ThrowOnError(output);
|
1378
|
+
}
|
1379
|
+
catch (Exception ex)
|
1380
|
+
{
|
1381
|
+
exception = ex;
|
1382
|
+
}
|
1383
|
+
|
1384
|
+
if (expectedError is null)
|
1385
|
+
{
|
1386
|
+
Assert.Null(exception);
|
1387
|
+
}
|
1388
|
+
else
|
1389
|
+
{
|
1390
|
+
Assert.NotNull(exception);
|
1391
|
+
using var tempDir = await TemporaryDirectory.CreateWithContentsAsync([("NuGet.Config", """
|
1392
|
+
<configuration>
|
1393
|
+
<packageSources>
|
1394
|
+
<clear />
|
1395
|
+
<add key="test-feed" value="http://localhost/test-feed" />
|
1396
|
+
</packageSources>
|
1397
|
+
</configuration>
|
1398
|
+
""")]);
|
1399
|
+
var actualError = JobErrorBase.ErrorFromException(exception, "TEST-JOB-ID", tempDir.DirectoryPath);
|
1400
|
+
if (actualError is DependencyFileNotFound notFound)
|
1401
|
+
{
|
1402
|
+
// normalize default message for the test
|
1403
|
+
actualError = new DependencyFileNotFound(notFound.Details["file-path"].ToString()!, "test message");
|
1404
|
+
}
|
1405
|
+
|
1406
|
+
var actualErrorJson = JsonSerializer.Serialize(actualError, RunWorker.SerializerOptions);
|
1407
|
+
var expectedErrorJson = JsonSerializer.Serialize(expectedError, RunWorker.SerializerOptions);
|
1408
|
+
Assert.Equal(expectedErrorJson, actualErrorJson);
|
1409
|
+
}
|
1410
|
+
}
|
1411
|
+
|
1412
|
+
public static IEnumerable<object?[]> GenerateErrorFromToolOutputTestData()
|
1413
|
+
{
|
1414
|
+
yield return
|
1415
|
+
[
|
1416
|
+
// output
|
1417
|
+
"Everything was good.",
|
1418
|
+
// expectedError
|
1419
|
+
null,
|
1420
|
+
];
|
1421
|
+
|
1422
|
+
yield return
|
1423
|
+
[
|
1424
|
+
// output
|
1425
|
+
"Response status code does not indicate success: 403",
|
1426
|
+
// expectedError
|
1427
|
+
new PrivateSourceAuthenticationFailure(["http://localhost/test-feed"]),
|
1428
|
+
];
|
1429
|
+
|
1430
|
+
yield return
|
1431
|
+
[
|
1432
|
+
// output
|
1433
|
+
"The imported file \"some.file\" does not exist",
|
1434
|
+
// expectedError
|
1435
|
+
new DependencyFileNotFound("some.file", "test message"),
|
1436
|
+
];
|
1437
|
+
|
1438
|
+
yield return
|
1439
|
+
[
|
1440
|
+
// output
|
1441
|
+
"Package 'Some.Package' is not found on source",
|
1442
|
+
// expectedError
|
1443
|
+
new UpdateNotPossible(["Some.Package"]),
|
1444
|
+
];
|
1445
|
+
|
1446
|
+
yield return
|
1447
|
+
[
|
1448
|
+
// output
|
1449
|
+
"Unable to resolve dependencies. 'Some.Package 1.2.3' is not compatible with",
|
1450
|
+
// expectedError
|
1451
|
+
new UpdateNotPossible(["Some.Package.1.2.3"]),
|
1452
|
+
];
|
1453
|
+
}
|
1454
|
+
|
1369
1455
|
public static IEnumerable<object[]> GetTopLevelPackageDependencyInfosTestData()
|
1370
1456
|
{
|
1371
1457
|
// simple case
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dependabot-nuget
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.295.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dependabot
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-01-
|
11
|
+
date: 2025-01-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: dependabot-common
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - '='
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 0.
|
19
|
+
version: 0.295.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - '='
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 0.
|
26
|
+
version: 0.295.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rubyzip
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -351,6 +351,7 @@ files:
|
|
351
351
|
- helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/RunWorkerTests.cs
|
352
352
|
- helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/SerializationTests.cs
|
353
353
|
- helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/TestApiHandler.cs
|
354
|
+
- helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/UpdateAllowedTests.cs
|
354
355
|
- helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/UpdatedDependencyListTests.cs
|
355
356
|
- helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/TemporaryDirectory.cs
|
356
357
|
- helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/TemporaryEnvironment.cs
|
@@ -528,7 +529,7 @@ licenses:
|
|
528
529
|
- MIT
|
529
530
|
metadata:
|
530
531
|
bug_tracker_uri: https://github.com/dependabot/dependabot-core/issues
|
531
|
-
changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.
|
532
|
+
changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.295.0
|
532
533
|
post_install_message:
|
533
534
|
rdoc_options: []
|
534
535
|
require_paths:
|