dependabot-nuget 0.322.0 → 0.322.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Analyze.cs +1 -1
  3. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Discover.cs +10 -23
  4. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Run.cs +9 -0
  5. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Update.cs +15 -232
  6. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/SdkProjectDiscovery.cs +1 -154
  7. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/ExperimentsManager.cs +3 -12
  8. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/PullRequestBodyGenerator/AzurePackageDetailFinder.cs +30 -0
  9. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/PullRequestBodyGenerator/DetailedPullRequestBodyGenerator.cs +237 -0
  10. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/PullRequestBodyGenerator/GitHubPackageDetailFinder.cs +101 -0
  11. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/PullRequestBodyGenerator/GitLabPackageDetailFinder.cs +107 -0
  12. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/PullRequestBodyGenerator/HttpFetcher.cs +32 -0
  13. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/PullRequestBodyGenerator/IHttpFetcher.cs +30 -0
  14. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/PullRequestBodyGenerator/IPackageDetailFinder.cs +47 -0
  15. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/PullRequestBodyGenerator/IPullRequestBodyGenerator.cs +11 -0
  16. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/PullRequestBodyGenerator/SimplePullRequestBodyGenerator.cs +15 -0
  17. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/PullRequestTextGenerator.cs +7 -3
  18. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/RunWorker.cs +3 -525
  19. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/UpdateHandlers/CreateSecurityUpdatePullRequestHandler.cs +1 -1
  20. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/UpdateHandlers/GroupUpdateAllVersionsHandler.cs +2 -2
  21. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/UpdateHandlers/RefreshGroupUpdatePullRequestHandler.cs +1 -1
  22. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/UpdateHandlers/RefreshSecurityUpdatePullRequestHandler.cs +1 -1
  23. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/UpdateHandlers/RefreshVersionUpdatePullRequestHandler.cs +1 -1
  24. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/FileWriters/FileWriterWorker.cs +1 -1
  25. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/FileWriters/XmlFileWriter.cs +10 -3
  26. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackageReferenceUpdater.cs +1 -856
  27. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/UpdaterWorker.cs +16 -200
  28. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +6 -556
  29. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/AnalyzeWorkerTests.cs +9 -73
  30. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Clone/CloneWorkerTests.cs +2 -2
  31. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/DependencySolver/MSBuildDependencySolverTests.cs +1 -1
  32. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTestBase.cs +1 -20
  33. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.PackagesConfig.cs +3 -62
  34. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.Project.cs +13 -563
  35. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.cs +20 -267
  36. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/SdkProjectDiscoveryTests.cs +2 -2
  37. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/EndToEndTests.cs +131 -131
  38. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/MiscellaneousTests.cs +0 -203
  39. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/PullRequestBodyGenerator/DetailedPullRequestBodyGeneratorTests.cs +871 -0
  40. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/PullRequestBodyGenerator/IPackageDetailFinderTests.cs +28 -0
  41. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/PullRequestBodyGenerator/TestHttpFetcher.cs +23 -0
  42. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/PullRequestTextTests.cs +3 -2
  43. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/SerializationTests.cs +6 -12
  44. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/UpdateHandlers/CreateSecurityUpdatePullRequestHandlerTests.cs +6 -6
  45. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/UpdateHandlers/GroupUpdateAllVersionsHandlerTests.cs +18 -18
  46. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/UpdateHandlers/RefreshGroupUpdatePullRequestHandlerTests.cs +15 -15
  47. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/UpdateHandlers/RefreshSecurityUpdatePullRequestHandlerTests.cs +21 -21
  48. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/UpdateHandlers/RefreshVersionUpdatePullRequestHandlerTests.cs +15 -15
  49. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/UpdateHandlers/UpdateHandlersTestsBase.cs +1 -8
  50. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/FileWriters/FileWriterWorkerTests.cs +1 -1
  51. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/FileWriters/XmlFileWriterTests.cs +85 -0
  52. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/PackageReferenceUpdaterTests.cs +1 -159
  53. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/MSBuildHelperTests.cs +10 -660
  54. metadata +16 -10
  55. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/RunResult.cs +0 -13
  56. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/PullRequestMessageTests.cs +0 -296
  57. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/RunWorkerTests.cs +0 -3592
  58. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/UpdatePermittedAndMessageTests.cs +0 -457
  59. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.DirsProj.cs +0 -378
  60. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/SdkPackageUpdaterHelperTests.cs +0 -175
@@ -1,12 +1,8 @@
1
1
  using System.Collections.Immutable;
2
- using System.IO;
3
2
  using System.IO.Enumeration;
4
- using System.Text;
5
3
  using System.Text.Json;
6
4
  using System.Text.Json.Serialization;
7
5
 
8
- using Microsoft.Extensions.FileSystemGlobbing;
9
-
10
6
  using NuGet.Versioning;
11
7
 
12
8
  using NuGetUpdater.Core.Analyze;
@@ -49,35 +45,12 @@ public class RunWorker
49
45
  var jobFileContent = await File.ReadAllTextAsync(jobFilePath.FullName);
50
46
  var jobWrapper = Deserialize(jobFileContent);
51
47
  var experimentsManager = ExperimentsManager.GetExperimentsManager(jobWrapper.Job.Experiments);
52
- var result = await RunAsync(jobWrapper.Job, repoContentsPath, caseInsensitiveRepoContentsPath, baseCommitSha, experimentsManager);
53
- if (experimentsManager.UseLegacyUpdateHandler)
54
- {
55
- // only the legacy handler writes this file
56
- var resultJson = JsonSerializer.Serialize(result, SerializerOptions);
57
- await File.WriteAllTextAsync(outputFilePath.FullName, resultJson);
58
- }
48
+ await RunAsync(jobWrapper.Job, repoContentsPath, caseInsensitiveRepoContentsPath, baseCommitSha, experimentsManager);
59
49
  }
60
50
 
61
- public async Task<RunResult> RunAsync(Job job, DirectoryInfo repoContentsPath, DirectoryInfo? caseInsensitiveRepoContentsPath, string baseCommitSha, ExperimentsManager experimentsManager)
51
+ public async Task RunAsync(Job job, DirectoryInfo repoContentsPath, DirectoryInfo? caseInsensitiveRepoContentsPath, string baseCommitSha, ExperimentsManager experimentsManager)
62
52
  {
63
- RunResult result;
64
- if (experimentsManager.UseLegacyUpdateHandler)
65
- {
66
- result = await RunWithErrorHandlingAsync(job, repoContentsPath, caseInsensitiveRepoContentsPath, baseCommitSha, experimentsManager);
67
- }
68
- else
69
- {
70
- await RunScenarioHandlersWithErrorHandlingAsync(job, repoContentsPath, caseInsensitiveRepoContentsPath, baseCommitSha, experimentsManager);
71
-
72
- // the group updater doesn't return this, so we provide an empty object
73
- result = new RunResult()
74
- {
75
- Base64DependencyFiles = [],
76
- BaseCommitSha = baseCommitSha,
77
- };
78
- }
79
-
80
- return result;
53
+ await RunScenarioHandlersWithErrorHandlingAsync(job, repoContentsPath, caseInsensitiveRepoContentsPath, baseCommitSha, experimentsManager);
81
54
  }
82
55
 
83
56
  private static readonly ImmutableArray<IUpdateHandler> UpdateHandlers =
@@ -115,252 +88,6 @@ public class RunWorker
115
88
  await _apiHandler.MarkAsProcessed(new(baseCommitSha));
116
89
  }
117
90
 
118
- private async Task<RunResult> RunWithErrorHandlingAsync(Job job, DirectoryInfo repoContentsPath, DirectoryInfo? caseInsensitiveRepoContentsPath, string baseCommitSha, ExperimentsManager experimentsManager)
119
- {
120
- JobErrorBase? error = null;
121
- var currentDirectory = repoContentsPath.FullName; // used for error reporting below
122
- var runResult = new RunResult()
123
- {
124
- Base64DependencyFiles = [],
125
- BaseCommitSha = baseCommitSha,
126
- };
127
-
128
- try
129
- {
130
- MSBuildHelper.RegisterMSBuild(repoContentsPath.FullName, repoContentsPath.FullName, _logger);
131
-
132
- var allDependencyFiles = new Dictionary<string, DependencyFile>();
133
- foreach (var directory in job.GetAllDirectories())
134
- {
135
- var localPath = PathHelper.JoinPath(repoContentsPath.FullName, directory);
136
- currentDirectory = localPath;
137
- var result = await RunForDirectory(job, repoContentsPath, caseInsensitiveRepoContentsPath, directory, baseCommitSha, experimentsManager);
138
- foreach (var dependencyFile in result.Base64DependencyFiles)
139
- {
140
- var uniqueKey = Path.GetFullPath(Path.Join(dependencyFile.Directory, dependencyFile.Name)).NormalizePathToUnix().EnsurePrefix("/");
141
- allDependencyFiles[uniqueKey] = dependencyFile;
142
- }
143
- }
144
-
145
- runResult = new RunResult()
146
- {
147
- Base64DependencyFiles = allDependencyFiles.Values.ToArray(),
148
- BaseCommitSha = baseCommitSha,
149
- };
150
- }
151
- catch (Exception ex)
152
- {
153
- error = JobErrorBase.ErrorFromException(ex, _jobId, currentDirectory);
154
- }
155
-
156
- if (error is not null)
157
- {
158
- await _apiHandler.RecordUpdateJobError(error, _logger);
159
- }
160
-
161
- await _apiHandler.MarkAsProcessed(new(baseCommitSha));
162
-
163
- return runResult;
164
- }
165
-
166
- private async Task<RunResult> RunForDirectory(Job job, DirectoryInfo originalRepoContentsPath, DirectoryInfo? caseInsensitiveRepoContentsPath, string repoDirectory, string baseCommitSha, ExperimentsManager experimentsManager)
167
- {
168
- var repoContentsPath = caseInsensitiveRepoContentsPath ?? originalRepoContentsPath;
169
- var discoveryResult = await _discoveryWorker.RunAsync(repoContentsPath.FullName, repoDirectory);
170
- _logger.ReportDiscovery(discoveryResult);
171
-
172
- if (discoveryResult.Error is not null)
173
- {
174
- // this is unrecoverable
175
- await _apiHandler.RecordUpdateJobError(discoveryResult.Error, _logger);
176
- return new()
177
- {
178
- Base64DependencyFiles = [],
179
- BaseCommitSha = baseCommitSha,
180
- };
181
- }
182
-
183
- // report dependencies
184
- var discoveredUpdatedDependencies = GetUpdatedDependencyListFromDiscovery(discoveryResult, originalRepoContentsPath.FullName, _logger);
185
- await _apiHandler.UpdateDependencyList(discoveredUpdatedDependencies);
186
-
187
- var incrementMetric = GetIncrementMetric(job);
188
- await _apiHandler.IncrementMetric(incrementMetric);
189
-
190
- // TODO: pull out relevant dependencies, then check each for updates and track the changes
191
- var actualUpdatedDependencies = new List<ReportedDependency>();
192
-
193
- // track original contents for later handling
194
- var tracker = new ModifiedFilesTracker(originalRepoContentsPath, _logger);
195
- await tracker.StartTrackingAsync(discoveryResult);
196
-
197
- // do update
198
- var updateOperationsPerformed = new List<UpdateOperationBase>();
199
- var existingPullRequests = job.GetAllExistingPullRequests();
200
- var unhandledPullRequestDependenciesSet = existingPullRequests
201
- .Select(pr => pr.Item2.Select(d => d.DependencyName).ToHashSet(StringComparer.OrdinalIgnoreCase))
202
- .ToHashSet();
203
- var remainingSecurityIssues = job.SecurityAdvisories
204
- .Select(s => s.DependencyName)
205
- .ToHashSet(StringComparer.OrdinalIgnoreCase);
206
- var updateOperations = GetUpdateOperations(discoveryResult).ToArray();
207
-
208
- foreach (var updateOperation in updateOperations)
209
- {
210
- var dependency = updateOperation.Dependency;
211
- var (isAllowed, message) = UpdatePermittedAndMessage(job, updateOperation.Dependency);
212
- if (message is SecurityUpdateNotNeeded sec)
213
- {
214
- // flag this update operation as having been handled
215
- remainingSecurityIssues.RemoveWhere(r => r.Equals(dependency.Name, StringComparison.OrdinalIgnoreCase));
216
-
217
- // we only want to send this message if we're in the only update operation for this dependency, otherwise it's ambiguous
218
- var updateOperationsWithSameName = updateOperations.Where(u => u.Dependency.Name.Equals(dependency.Name, StringComparison.OrdinalIgnoreCase))
219
- .ToArray();
220
- if (updateOperationsWithSameName.Length > 1)
221
- {
222
- // suppress the message
223
- message = null;
224
- }
225
- }
226
-
227
- await SendApiMessage(message);
228
- if (!isAllowed)
229
- {
230
- continue;
231
- }
232
-
233
- _logger.Info($"Updating [{dependency.Name}] in [{updateOperation.ProjectPath}]");
234
-
235
- var dependencyInfo = GetDependencyInfo(job, dependency);
236
- var analysisResult = await _analyzeWorker.RunAsync(repoContentsPath.FullName, discoveryResult, dependencyInfo);
237
- _logger.ReportAnalysis(analysisResult);
238
-
239
- if (analysisResult.Error is not null)
240
- {
241
- await _apiHandler.RecordUpdateJobError(analysisResult.Error, _logger);
242
- continue;
243
- }
244
-
245
- if (analysisResult.CanUpdate)
246
- {
247
- if (!job.UpdatingAPullRequest)
248
- {
249
- var updatedDependencyFromAnalysis = analysisResult.UpdatedDependencies
250
- .FirstOrDefault(d => d.Name.Equals(dependency.Name, StringComparison.OrdinalIgnoreCase));
251
- if (updatedDependencyFromAnalysis is not null)
252
- {
253
- var existingPullRequest = job.GetExistingPullRequestForDependencies([updatedDependencyFromAnalysis], considerVersions: true);
254
- if (existingPullRequest is not null)
255
- {
256
- await SendApiMessage(new PullRequestExistsForLatestVersion(dependency.Name, analysisResult.UpdatedVersion));
257
- unhandledPullRequestDependenciesSet.RemoveWhere(handled => handled.Count == 1 && handled.Contains(dependency.Name));
258
- continue;
259
- }
260
- }
261
- }
262
-
263
- // TODO: this is inefficient, but not likely causing a bottleneck
264
- var previousDependency = discoveredUpdatedDependencies.Dependencies
265
- .Single(d => d.Name == dependency.Name && d.Requirements.Single().File == updateOperation.ProjectPath);
266
- var updatedDependency = new ReportedDependency()
267
- {
268
- Name = dependency.Name,
269
- Version = analysisResult.UpdatedVersion,
270
- Requirements =
271
- [
272
- new ReportedRequirement()
273
- {
274
- File = updateOperation.ProjectPath,
275
- Requirement = analysisResult.UpdatedVersion,
276
- Groups = previousDependency.Requirements.Single().Groups,
277
- Source = new RequirementSource()
278
- {
279
- SourceUrl = analysisResult.UpdatedDependencies.FirstOrDefault(d => d.Name == dependency.Name)?.InfoUrl,
280
- },
281
- }
282
- ],
283
- PreviousVersion = dependency.Version,
284
- PreviousRequirements = previousDependency.Requirements,
285
- };
286
-
287
- var projectDiscovery = discoveryResult.GetProjectDiscoveryFromPath(updateOperation.ProjectPath);
288
- var updateResult = await _updaterWorker.RunAsync(repoContentsPath.FullName, updateOperation.ProjectPath, dependency.Name, dependency.Version!, analysisResult.UpdatedVersion, isTransitive: dependency.IsTransitive);
289
- if (updateResult.Error is not null)
290
- {
291
- await _apiHandler.RecordUpdateJobError(updateResult.Error, _logger);
292
- }
293
- else
294
- {
295
- actualUpdatedDependencies.Add(updatedDependency);
296
- }
297
-
298
- var patchedUpdateOperations = PatchInOldVersions(updateResult.UpdateOperations, projectDiscovery);
299
- updateOperationsPerformed.AddRange(patchedUpdateOperations);
300
- }
301
- }
302
-
303
- // create PR - we need to manually check file contents; we can't easily use `git status` in tests
304
- var updatedDependencyFiles = await tracker.StopTrackingAsync();
305
- var normalizedUpdateOperationsPerformed = UpdateOperationBase.NormalizeUpdateOperationCollection(repoContentsPath.FullName, updateOperationsPerformed);
306
- var report = UpdateOperationBase.GenerateUpdateOperationReport(normalizedUpdateOperationsPerformed);
307
- _logger.Info(report);
308
-
309
- var sortedUpdatedDependencies = actualUpdatedDependencies.OrderBy(d => d.Name, StringComparer.OrdinalIgnoreCase).ToArray();
310
- var resultMessage = GetPullRequestApiMessage(job, [.. updatedDependencyFiles], sortedUpdatedDependencies, normalizedUpdateOperationsPerformed, baseCommitSha);
311
- switch (resultMessage)
312
- {
313
- case ClosePullRequest close:
314
- var closePrDependencies = close.DependencyNames.ToHashSet(StringComparer.OrdinalIgnoreCase);
315
- remainingSecurityIssues.RemoveWhere(closePrDependencies.Contains);
316
- if (!unhandledPullRequestDependenciesSet.Remove(closePrDependencies))
317
- {
318
- // this PR was handled earlier, we don't want to now close it; suppress the message
319
- resultMessage = null;
320
- }
321
- break;
322
- case CreatePullRequest create:
323
- var createPrDependencies = create.Dependencies.Select(d => d.Name).ToHashSet(StringComparer.OrdinalIgnoreCase);
324
- remainingSecurityIssues.RemoveWhere(createPrDependencies.Contains);
325
- break;
326
- case UpdatePullRequest update:
327
- var updatePrDependencies = update.DependencyNames.ToHashSet(StringComparer.OrdinalIgnoreCase);
328
- remainingSecurityIssues.RemoveWhere(updatePrDependencies.Contains);
329
- break;
330
- }
331
-
332
- await SendApiMessage(resultMessage);
333
-
334
- // for each security advisory that _didn't_ result in a pr, report it
335
- foreach (var depName in remainingSecurityIssues)
336
- {
337
- await SendApiMessage(new SecurityUpdateNotNeeded(depName));
338
- }
339
-
340
- var result = new RunResult()
341
- {
342
- Base64DependencyFiles = tracker.OriginalDependencyFileContents.OrderBy(kvp => kvp.Key).Select(kvp =>
343
- {
344
- var fullPath = kvp.Key.FullyNormalizedRootedPath();
345
- var rawContent = Encoding.UTF8.GetBytes(kvp.Value);
346
- if (tracker.OriginalDependencyFileBOMs[kvp.Key])
347
- {
348
- rawContent = Encoding.UTF8.GetPreamble().Concat(rawContent).ToArray();
349
- }
350
-
351
- return new DependencyFile()
352
- {
353
- Name = Path.GetFileName(fullPath),
354
- Content = Convert.ToBase64String(rawContent),
355
- ContentEncoding = "base64",
356
- Directory = Path.GetDirectoryName(fullPath)!.NormalizePathToUnix(),
357
- };
358
- }).ToArray(),
359
- BaseCommitSha = baseCommitSha,
360
- };
361
- return result;
362
- }
363
-
364
91
  internal static ImmutableArray<UpdateOperationBase> PatchInOldVersions(ImmutableArray<UpdateOperationBase> updateOperations, ProjectDiscoveryResult? projectDiscovery)
365
92
  {
366
93
  if (projectDiscovery is null)
@@ -377,115 +104,6 @@ public class RunWorker
377
104
  return patchedUpdateOperations;
378
105
  }
379
106
 
380
- private async Task SendApiMessage(MessageBase? message)
381
- {
382
- if (message is null)
383
- {
384
- return;
385
- }
386
-
387
- var report = message.GetReport();
388
- _logger.Info(report);
389
- switch (message)
390
- {
391
- case JobErrorBase error:
392
- await _apiHandler.RecordUpdateJobError(error, _logger);
393
- break;
394
- case CreatePullRequest create:
395
- await _apiHandler.CreatePullRequest(create);
396
- break;
397
- case ClosePullRequest close:
398
- await _apiHandler.ClosePullRequest(close);
399
- break;
400
- case UpdatePullRequest update:
401
- await _apiHandler.UpdatePullRequest(update);
402
- break;
403
- default:
404
- throw new NotSupportedException($"unsupported api message: {message.GetType().Name}");
405
- }
406
- }
407
-
408
- internal static MessageBase? GetPullRequestApiMessage(
409
- Job job,
410
- DependencyFile[] updatedFiles,
411
- ReportedDependency[] updatedDependencies,
412
- ImmutableArray<UpdateOperationBase> updateOperationsPerformed,
413
- string baseCommitSha
414
- )
415
- {
416
- var updatedDependencyNames = updateOperationsPerformed.Select(u => u.DependencyName).OrderBy(d => d, StringComparer.OrdinalIgnoreCase).ToArray();
417
- var updatedDependenciesSet = updatedDependencyNames.ToHashSet(StringComparer.OrdinalIgnoreCase);
418
-
419
- // all pull request dependencies with optional group name
420
- var existingPullRequests = job.GetAllExistingPullRequests();
421
- var existingPullRequest = existingPullRequests.FirstOrDefault(pr => pr.Item2.Select(d => d.DependencyName).All(updatedDependenciesSet.Contains));
422
- if (existingPullRequest is null && updatedFiles.Length == 0)
423
- {
424
- // it's possible that we were asked to update a specific package, but it's no longer there; in that case find _that_ specific PR
425
- var requestedUpdates = job.Dependencies.ToHashSet(StringComparer.OrdinalIgnoreCase);
426
- existingPullRequest = existingPullRequests.FirstOrDefault(pr => pr.Item2.Select(d => d.DependencyName).All(requestedUpdates.Contains));
427
- }
428
-
429
- var expectedSecurityUpdateDependencyNames = job.SecurityAdvisories.Select(sa => sa.DependencyName).ToHashSet(StringComparer.OrdinalIgnoreCase);
430
- var isExpectedSecurityUpdate = updatedDependenciesSet.All(expectedSecurityUpdateDependencyNames.Contains);
431
-
432
- if (existingPullRequest is { })
433
- {
434
- if (job.UpdatingAPullRequest)
435
- {
436
- return new UpdatePullRequest()
437
- {
438
- DependencyGroup = existingPullRequest.Item1,
439
- DependencyNames = [.. updatedDependencyNames],
440
- UpdatedDependencyFiles = updatedFiles,
441
- BaseCommitSha = baseCommitSha,
442
- CommitMessage = PullRequestTextGenerator.GetPullRequestCommitMessage(job, updateOperationsPerformed, existingPullRequest.Item1),
443
- PrTitle = PullRequestTextGenerator.GetPullRequestTitle(job, updateOperationsPerformed, existingPullRequest.Item1),
444
- PrBody = PullRequestTextGenerator.GetPullRequestBody(job, updateOperationsPerformed, existingPullRequest.Item1),
445
- };
446
- }
447
- else
448
- {
449
- if (updatedDependenciesSet.Count == 0)
450
- {
451
- // nothing found, close current
452
- return new ClosePullRequest()
453
- {
454
- DependencyNames = [.. existingPullRequest.Item2.Select(d => d.DependencyName)],
455
- Reason = "dependency_removed",
456
- };
457
- }
458
- else
459
- {
460
- // found but no longer required
461
- return new ClosePullRequest()
462
- {
463
- DependencyNames = [.. updatedDependenciesSet],
464
- Reason = "up_to_date",
465
- };
466
- }
467
- }
468
- }
469
- else
470
- {
471
- if (updatedDependencyNames.Any())
472
- {
473
- return new CreatePullRequest()
474
- {
475
- Dependencies = updatedDependencies,
476
- UpdatedDependencyFiles = updatedFiles,
477
- BaseCommitSha = baseCommitSha,
478
- CommitMessage = PullRequestTextGenerator.GetPullRequestCommitMessage(job, updateOperationsPerformed, dependencyGroupName: null),
479
- PrTitle = PullRequestTextGenerator.GetPullRequestTitle(job, updateOperationsPerformed, dependencyGroupName: null),
480
- PrBody = PullRequestTextGenerator.GetPullRequestBody(job, updateOperationsPerformed, dependencyGroupName: null),
481
- DependencyGroup = null,
482
- };
483
- }
484
- }
485
-
486
- return null;
487
- }
488
-
489
107
  internal static IEnumerable<(string ProjectPath, Dependency Dependency)> GetUpdateOperations(WorkspaceDiscoveryResult discovery)
490
108
  {
491
109
  // discovery is grouped by project/file then dependency, but we want to pivot and return a list of update operations sorted by dependency name then file path
@@ -534,146 +152,6 @@ public class RunWorker
534
152
  }
535
153
  }
536
154
 
537
- internal static IncrementMetric GetIncrementMetric(Job job)
538
- {
539
- var isSecurityUpdate = job.AllowedUpdates.Any(a => a.UpdateType == UpdateType.Security) || job.SecurityUpdatesOnly;
540
- var metricOperation = isSecurityUpdate ?
541
- (job.UpdatingAPullRequest ? "update_security_pr" : "create_security_pr")
542
- : (job.UpdatingAPullRequest ? "update_version_pr" : "group_update_all_versions");
543
- var increment = new IncrementMetric()
544
- {
545
- Metric = "updater.started",
546
- Tags = { ["operation"] = metricOperation },
547
- };
548
- return increment;
549
- }
550
-
551
- internal static (bool, MessageBase?) UpdatePermittedAndMessage(Job job, Dependency dependency)
552
- {
553
- if (dependency.Name.Equals("Microsoft.NET.Sdk", StringComparison.OrdinalIgnoreCase))
554
- {
555
- // this can't be updated
556
- // TODO: pull this out of discovery?
557
- return (false, null);
558
- }
559
-
560
- if (dependency.Version is null)
561
- {
562
- // if we don't know the version, there's nothing we can do
563
- // TODO: pull this out of discovery?
564
- return (false, null);
565
- }
566
-
567
- var version = NuGetVersion.Parse(dependency.Version);
568
- var dependencyInfo = GetDependencyInfo(job, dependency);
569
- var isVulnerable = dependencyInfo.Vulnerabilities.Any(v => v.IsVulnerable(version));
570
- MessageBase? message = null;
571
- var allowed = job.AllowedUpdates.Any(allowedUpdate =>
572
- {
573
- // check name restriction, if any
574
- if (allowedUpdate.DependencyName is not null)
575
- {
576
- var matcher = new Matcher(StringComparison.OrdinalIgnoreCase)
577
- .AddInclude(allowedUpdate.DependencyName);
578
- var result = matcher.Match(dependency.Name);
579
- if (!result.HasMatches)
580
- {
581
- return false;
582
- }
583
- }
584
-
585
- var isSecurityUpdate = allowedUpdate.UpdateType == UpdateType.Security || job.SecurityUpdatesOnly;
586
- if (isSecurityUpdate)
587
- {
588
- if (isVulnerable)
589
- {
590
- // try to match to existing PR
591
- var dependencyVersion = NuGetVersion.Parse(dependency.Version);
592
- var existingPullRequests = job.GetAllExistingPullRequests()
593
- .Where(pr => pr.Item2.Any(d => d.DependencyName.Equals(dependency.Name, StringComparison.OrdinalIgnoreCase) && d.DependencyVersion >= dependencyVersion))
594
- .ToArray();
595
- if (existingPullRequests.Length > 0)
596
- {
597
- // found a matching pr...
598
- if (job.UpdatingAPullRequest)
599
- {
600
- // ...and we've been asked to update it
601
- return true;
602
- }
603
- else
604
- {
605
- // ...but no update requested => don't perform any update and report error
606
- var existingPrVersion = existingPullRequests[0].Item2.First(d => d.DependencyName.Equals(dependency.Name, StringComparison.OrdinalIgnoreCase)).DependencyVersion;
607
- message = new PullRequestExistsForLatestVersion(dependency.Name, existingPrVersion.ToString());
608
- return false;
609
- }
610
- }
611
- else
612
- {
613
- // no matching pr...
614
- if (job.UpdatingAPullRequest)
615
- {
616
- // ...but we've been asked to perform an update => no update possible, nothing to report
617
- return false;
618
- }
619
- else
620
- {
621
- // ...and no update specifically requested => create new
622
- return true;
623
- }
624
- }
625
- }
626
- else
627
- {
628
- // not vulnerable => no longer needed
629
- var specificJobDependencies = job.SecurityAdvisories
630
- .Select(a => a.DependencyName)
631
- .Concat(job.Dependencies)
632
- .ToHashSet(StringComparer.OrdinalIgnoreCase);
633
- if (specificJobDependencies.Contains(dependency.Name))
634
- {
635
- message = new SecurityUpdateNotNeeded(dependency.Name);
636
- }
637
- }
638
-
639
- return false;
640
- }
641
- else
642
- {
643
- // not a security update, so only update if...
644
- // ...we've been explicitly asked to update this
645
- if (job.Dependencies.Any(d => d.Equals(dependency.Name, StringComparison.OrdinalIgnoreCase)))
646
- {
647
- return true;
648
- }
649
-
650
- // ...no specific update being performed, do it if it's not transitive
651
- return !dependency.IsTransitive;
652
- }
653
- });
654
-
655
- return (allowed, message);
656
- }
657
-
658
- internal static ImmutableArray<Requirement> GetIgnoredRequirementsForDependency(Job job, string dependencyName)
659
- {
660
- var ignoreConditions = job.IgnoreConditions
661
- .Where(c => c.DependencyName.Equals(dependencyName, StringComparison.OrdinalIgnoreCase))
662
- .ToArray();
663
- if (ignoreConditions.Length == 1 && ignoreConditions[0].VersionRequirement is null)
664
- {
665
- // if only one match with no version requirement, ignore all versions
666
- return [Requirement.Parse("> 0.0.0")];
667
- }
668
-
669
- var ignoredVersions = ignoreConditions
670
- .Select(c => c.VersionRequirement)
671
- .Where(r => r is not null)
672
- .Cast<Requirement>()
673
- .ToImmutableArray();
674
- return ignoredVersions;
675
- }
676
-
677
155
  internal static DependencyInfo GetDependencyInfo(Job job, Dependency dependency)
678
156
  {
679
157
  var dependencyVersion = NuGetVersion.Parse(dependency.Version!);
@@ -161,7 +161,7 @@ internal class CreateSecurityUpdatePullRequestHandler : IUpdateHandler
161
161
  {
162
162
  var commitMessage = PullRequestTextGenerator.GetPullRequestCommitMessage(job, [.. updateOperationsPerformed], null);
163
163
  var prTitle = PullRequestTextGenerator.GetPullRequestTitle(job, [.. updateOperationsPerformed], null);
164
- var prBody = PullRequestTextGenerator.GetPullRequestBody(job, [.. updateOperationsPerformed], null);
164
+ var prBody = await PullRequestTextGenerator.GetPullRequestBodyAsync(job, [.. updateOperationsPerformed], [.. updatedDependencies], experimentsManager);
165
165
  await apiHandler.CreatePullRequest(new CreatePullRequest()
166
166
  {
167
167
  Dependencies = [.. updatedDependencies],
@@ -155,7 +155,7 @@ internal class GroupUpdateAllVersionsHandler : IUpdateHandler
155
155
  {
156
156
  var commitMessage = PullRequestTextGenerator.GetPullRequestCommitMessage(job, [.. updateOperationsPerformed], group.Name);
157
157
  var prTitle = PullRequestTextGenerator.GetPullRequestTitle(job, [.. updateOperationsPerformed], group.Name);
158
- var prBody = PullRequestTextGenerator.GetPullRequestBody(job, [.. updateOperationsPerformed], group.Name);
158
+ var prBody = await PullRequestTextGenerator.GetPullRequestBodyAsync(job, [.. updateOperationsPerformed], [.. updatedDependencies], experimentsManager);
159
159
  await apiHandler.CreatePullRequest(new CreatePullRequest()
160
160
  {
161
161
  Dependencies = [.. updatedDependencies],
@@ -251,7 +251,7 @@ internal class GroupUpdateAllVersionsHandler : IUpdateHandler
251
251
  {
252
252
  var commitMessage = PullRequestTextGenerator.GetPullRequestCommitMessage(job, [.. updateOperationsPerformed], null);
253
253
  var prTitle = PullRequestTextGenerator.GetPullRequestTitle(job, [.. updateOperationsPerformed], null);
254
- var prBody = PullRequestTextGenerator.GetPullRequestBody(job, [.. updateOperationsPerformed], null);
254
+ var prBody = await PullRequestTextGenerator.GetPullRequestBodyAsync(job, [.. updateOperationsPerformed], [.. updatedDependencies], experimentsManager);
255
255
  await apiHandler.CreatePullRequest(new CreatePullRequest()
256
256
  {
257
257
  Dependencies = [.. updatedDependencies],
@@ -154,7 +154,7 @@ internal class RefreshGroupUpdatePullRequestHandler : IUpdateHandler
154
154
 
155
155
  var commitMessage = PullRequestTextGenerator.GetPullRequestCommitMessage(job, [.. updateOperationsPerformed], null);
156
156
  var prTitle = PullRequestTextGenerator.GetPullRequestTitle(job, [.. updateOperationsPerformed], null);
157
- var prBody = PullRequestTextGenerator.GetPullRequestBody(job, [.. updateOperationsPerformed], null);
157
+ var prBody = await PullRequestTextGenerator.GetPullRequestBodyAsync(job, [.. updateOperationsPerformed], [.. updatedDependencies], experimentsManager);
158
158
  var existingPullRequest = job.GetExistingPullRequestForDependencies(rawDependencies, considerVersions: true);
159
159
  if (existingPullRequest is not null)
160
160
  {
@@ -161,7 +161,7 @@ internal class RefreshSecurityUpdatePullRequestHandler : IUpdateHandler
161
161
  {
162
162
  var commitMessage = PullRequestTextGenerator.GetPullRequestCommitMessage(job, [.. updateOperationsPerformed], null);
163
163
  var prTitle = PullRequestTextGenerator.GetPullRequestTitle(job, [.. updateOperationsPerformed], null);
164
- var prBody = PullRequestTextGenerator.GetPullRequestBody(job, [.. updateOperationsPerformed], null);
164
+ var prBody = await PullRequestTextGenerator.GetPullRequestBodyAsync(job, [.. updateOperationsPerformed], [.. updatedDependencies], experimentsManager);
165
165
 
166
166
  var existingPullRequest = job.GetExistingPullRequestForDependencies(rawDependencies, considerVersions: true);
167
167
  if (existingPullRequest is not null)
@@ -151,7 +151,7 @@ internal class RefreshVersionUpdatePullRequestHandler : IUpdateHandler
151
151
  {
152
152
  var commitMessage = PullRequestTextGenerator.GetPullRequestCommitMessage(job, [.. updateOperationsPerformed], null);
153
153
  var prTitle = PullRequestTextGenerator.GetPullRequestTitle(job, [.. updateOperationsPerformed], null);
154
- var prBody = PullRequestTextGenerator.GetPullRequestBody(job, [.. updateOperationsPerformed], null);
154
+ var prBody = await PullRequestTextGenerator.GetPullRequestBodyAsync(job, [.. updateOperationsPerformed], [.. updatedDependencies], experimentsManager);
155
155
 
156
156
  var existingPullRequest = job.GetExistingPullRequestForDependencies(rawDependencies, considerVersions: true);
157
157
  if (existingPullRequest is not null)
@@ -250,7 +250,7 @@ public class FileWriterWorker
250
250
  initialTopLevelDependencies,
251
251
  desiredDependencies,
252
252
  resolvedDependencies.Value,
253
- new ExperimentsManager() { UseDirectDiscovery = true },
253
+ new ExperimentsManager(),
254
254
  _logger);
255
255
  var filteredUpdateOperations = computedUpdateOperations
256
256
  .Where(op =>
@@ -1,5 +1,6 @@
1
1
  using System.Collections.Immutable;
2
2
  using System.Text.RegularExpressions;
3
+ using System.Xml;
3
4
  using System.Xml.Linq;
4
5
 
5
6
  using NuGet.Versioning;
@@ -369,7 +370,7 @@ public class XmlFileWriter : IFileWriter
369
370
  {
370
371
  foreach (var (path, contents) in filesAndContents)
371
372
  {
372
- await WriteFileContentsAsync(repoContentsPath, path, contents.ToString());
373
+ await WriteFileContentsAsync(repoContentsPath, path, contents);
373
374
  }
374
375
  }
375
376
 
@@ -433,10 +434,16 @@ public class XmlFileWriter : IFileWriter
433
434
  return contents;
434
435
  }
435
436
 
436
- private static async Task WriteFileContentsAsync(DirectoryInfo repoContentsPath, string path, string contents)
437
+ private static async Task WriteFileContentsAsync(DirectoryInfo repoContentsPath, string path, XDocument document)
437
438
  {
438
439
  var fullPath = Path.Join(repoContentsPath.FullName, path);
439
- await File.WriteAllTextAsync(fullPath, contents);
440
+ var writerSettings = new XmlWriterSettings()
441
+ {
442
+ Async = true,
443
+ OmitXmlDeclaration = document.Declaration is null,
444
+ };
445
+ using var writer = XmlWriter.Create(fullPath, writerSettings);
446
+ await document.SaveAsync(writer, CancellationToken.None);
440
447
  }
441
448
 
442
449
  public static string CreateUpdatedVersionRangeString(VersionRange existingRange, NuGetVersion existingVersion, NuGetVersion requiredVersion)