dependabot-nuget 0.297.2 → 0.299.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 303dd4ae4de15c33ca64bc0a8957ee6a4fa27cac4ffd2fbb93047d518d6afdb6
4
- data.tar.gz: b4075db4bac953819932132e23d6478b6bc3e06242032d79235d2025caa1ed6f
3
+ metadata.gz: 47471c96a0fec7881b5454528d6cd07199cd600b1ec4dde66656fc754db318fe
4
+ data.tar.gz: 2364b4e071736115c1cb9503dc40562819e4c376198321761812c70dc309f1bd
5
5
  SHA512:
6
- metadata.gz: 700973559052453bdfe9d999df2c980d1a5d95ecf296393edb1d1e030c050fa24eafd344898ed3017e2b6a19f7f61cf1503388ec875ffa093fc05b26cbbfc3a5
7
- data.tar.gz: 3c3464e7f039d06e3fc55b3406e966767b2ed2e33d31c604f8034c7bde706aea942bcd5499b415ed1d8fae1b19323f57439e82b749e08ba0591aa53cab80aecb
6
+ metadata.gz: d31e631d9c1dd411843c4c911ad2db4e8a19f4d1fd15ffb0e5c2d4af1b092520bcc4aea2b31996c765870973e0da47b85910fb7d14f81ca973a7069554dc6733
7
+ data.tar.gz: 48714dea50d6fab935c7ca952e5d992f9aaf0699a54f99f576db55575b36251af0b7503744d229d187245b7e1eab687ce639fad574c8b15bb795f569720a0c99
@@ -62,23 +62,24 @@ internal static class VersionFinder
62
62
 
63
63
  foreach (var source in sources)
64
64
  {
65
- var sourceRepository = Repository.Factory.GetCoreV3(source);
66
- var feed = await sourceRepository.GetResourceAsync<MetadataResource>();
67
- if (feed is null)
65
+ MetadataResource? feed = null;
66
+ try
68
67
  {
69
- logger.Warn($"Failed to get {nameof(MetadataResource)} for [{source.Source}]");
70
- continue;
71
- }
68
+ var sourceRepository = Repository.Factory.GetCoreV3(source);
69
+ feed = await sourceRepository.GetResourceAsync<MetadataResource>();
70
+ if (feed is null)
71
+ {
72
+ logger.Warn($"Failed to get {nameof(MetadataResource)} for [{source.Source}]");
73
+ continue;
74
+ }
72
75
 
73
- var packageFinder = await sourceRepository.GetResourceAsync<FindPackageByIdResource>();
74
- if (packageFinder is null)
75
- {
76
- logger.Warn($"Failed to get {nameof(FindPackageByIdResource)} for [{source.Source}]");
77
- continue;
78
- }
76
+ var packageFinder = await sourceRepository.GetResourceAsync<FindPackageByIdResource>();
77
+ if (packageFinder is null)
78
+ {
79
+ logger.Warn($"Failed to get {nameof(FindPackageByIdResource)} for [{source.Source}]");
80
+ continue;
81
+ }
79
82
 
80
- try
81
- {
82
83
  // a non-compliant v2 API returning 404 can cause this to throw
83
84
  var existsInFeed = await feed.Exists(
84
85
  packageId,
@@ -20,7 +20,8 @@ namespace NuGetGallery.Frameworks
20
20
  /// </remarks>
21
21
  public static class SupportedFrameworks
22
22
  {
23
- public static readonly Version Version8 = new Version(8, 0, 0, 0); // https://github.com/NuGet/Engineering/issues/5112
23
+ public static readonly Version Version8 = new Version(8, 0, 0, 0);
24
+ public static readonly Version Version9 = new Version(9, 0, 0, 0);
24
25
 
25
26
  public static readonly NuGetFramework MonoAndroid = new NuGetFramework(FrameworkIdentifiers.MonoAndroid, EmptyVersion);
26
27
  public static readonly NuGetFramework MonoTouch = new NuGetFramework(FrameworkIdentifiers.MonoTouch, EmptyVersion);
@@ -29,12 +30,14 @@ namespace NuGetGallery.Frameworks
29
30
  public static readonly NuGetFramework Net48 = new NuGetFramework(FrameworkIdentifiers.Net, new Version(4, 8, 0, 0));
30
31
  public static readonly NuGetFramework Net481 = new NuGetFramework(FrameworkIdentifiers.Net, new Version(4, 8, 1, 0));
31
32
  public static readonly NuGetFramework Net50Windows = new NuGetFramework(FrameworkIdentifiers.NetCoreApp, Version5, "windows", EmptyVersion);
33
+
32
34
  public static readonly NuGetFramework Net60Android = new NuGetFramework(FrameworkIdentifiers.NetCoreApp, Version6, "android", EmptyVersion);
33
35
  public static readonly NuGetFramework Net60Ios = new NuGetFramework(FrameworkIdentifiers.NetCoreApp, Version6, "ios", EmptyVersion);
34
36
  public static readonly NuGetFramework Net60MacOs = new NuGetFramework(FrameworkIdentifiers.NetCoreApp, Version6, "macos", EmptyVersion);
35
37
  public static readonly NuGetFramework Net60MacCatalyst = new NuGetFramework(FrameworkIdentifiers.NetCoreApp, Version6, "maccatalyst", EmptyVersion);
36
38
  public static readonly NuGetFramework Net60TvOs = new NuGetFramework(FrameworkIdentifiers.NetCoreApp, Version6, "tvos", EmptyVersion);
37
39
  public static readonly NuGetFramework Net60Windows = new NuGetFramework(FrameworkIdentifiers.NetCoreApp, Version6, "windows", EmptyVersion);
40
+
38
41
  public static readonly NuGetFramework Net70Android = new NuGetFramework(FrameworkIdentifiers.NetCoreApp, Version7, "android", EmptyVersion);
39
42
  public static readonly NuGetFramework Net70Ios = new NuGetFramework(FrameworkIdentifiers.NetCoreApp, Version7, "ios", EmptyVersion);
40
43
  public static readonly NuGetFramework Net70MacOs = new NuGetFramework(FrameworkIdentifiers.NetCoreApp, Version7, "macos", EmptyVersion);
@@ -42,14 +45,22 @@ namespace NuGetGallery.Frameworks
42
45
  public static readonly NuGetFramework Net70TvOs = new NuGetFramework(FrameworkIdentifiers.NetCoreApp, Version7, "tvos", EmptyVersion);
43
46
  public static readonly NuGetFramework Net70Windows = new NuGetFramework(FrameworkIdentifiers.NetCoreApp, Version7, "windows", EmptyVersion);
44
47
 
45
- public static readonly NuGetFramework Net80 = new NuGetFramework(FrameworkIdentifiers.NetCoreApp, Version8); // https://github.com/NuGet/Engineering/issues/5112
46
-
48
+ public static readonly NuGetFramework Net80 = new NuGetFramework(FrameworkIdentifiers.NetCoreApp, Version8);
47
49
  public static readonly NuGetFramework Net80Android = new NuGetFramework(FrameworkIdentifiers.NetCoreApp, Version8, "android", EmptyVersion);
48
50
  public static readonly NuGetFramework Net80Ios = new NuGetFramework(FrameworkIdentifiers.NetCoreApp, Version8, "ios", EmptyVersion);
49
51
  public static readonly NuGetFramework Net80MacOs = new NuGetFramework(FrameworkIdentifiers.NetCoreApp, Version8, "macos", EmptyVersion);
50
52
  public static readonly NuGetFramework Net80MacCatalyst = new NuGetFramework(FrameworkIdentifiers.NetCoreApp, Version8, "maccatalyst", EmptyVersion);
51
53
  public static readonly NuGetFramework Net80TvOs = new NuGetFramework(FrameworkIdentifiers.NetCoreApp, Version8, "tvos", EmptyVersion);
52
54
  public static readonly NuGetFramework Net80Windows = new NuGetFramework(FrameworkIdentifiers.NetCoreApp, Version8, "windows", EmptyVersion);
55
+
56
+ public static readonly NuGetFramework Net90 = new NuGetFramework(FrameworkIdentifiers.NetCoreApp, Version9);
57
+ public static readonly NuGetFramework Net90Android = new NuGetFramework(FrameworkIdentifiers.NetCoreApp, Version9, "android", EmptyVersion);
58
+ public static readonly NuGetFramework Net90Ios = new NuGetFramework(FrameworkIdentifiers.NetCoreApp, Version9, "ios", EmptyVersion);
59
+ public static readonly NuGetFramework Net90MacOs = new NuGetFramework(FrameworkIdentifiers.NetCoreApp, Version9, "macos", EmptyVersion);
60
+ public static readonly NuGetFramework Net90MacCatalyst = new NuGetFramework(FrameworkIdentifiers.NetCoreApp, Version9, "maccatalyst", EmptyVersion);
61
+ public static readonly NuGetFramework Net90TvOs = new NuGetFramework(FrameworkIdentifiers.NetCoreApp, Version9, "tvos", EmptyVersion);
62
+ public static readonly NuGetFramework Net90Windows = new NuGetFramework(FrameworkIdentifiers.NetCoreApp, Version9, "windows", EmptyVersion);
63
+
53
64
  public static readonly NuGetFramework NetCore = new NuGetFramework(FrameworkIdentifiers.NetCore, EmptyVersion);
54
65
  public static readonly NuGetFramework NetMf = new NuGetFramework(FrameworkIdentifiers.NetMicro, EmptyVersion);
55
66
  public static readonly NuGetFramework UAP = new NuGetFramework(FrameworkIdentifiers.UAP, EmptyVersion);
@@ -75,6 +86,7 @@ namespace NuGetGallery.Frameworks
75
86
  Net60, Net60Android, Net60Ios, Net60MacCatalyst, Net60MacOs, Net60TvOs, Net60Windows,
76
87
  Net70, Net70Android, Net70Ios, Net70MacCatalyst, Net70MacOs, Net70TvOs, Net70Windows,
77
88
  Net80, Net80Android, Net80Ios, Net80MacCatalyst, Net80MacOs, Net80TvOs, Net80Windows,
89
+ Net90, Net90Android, Net90Ios, Net90MacCatalyst, Net90MacOs, Net90TvOs, Net90Windows,
78
90
  NetCore, NetCore45, NetCore451,
79
91
  NetCoreApp10, NetCoreApp11, NetCoreApp20, NetCoreApp21, NetCoreApp22, NetCoreApp30, NetCoreApp31,
80
92
  NetMf,
@@ -94,6 +106,7 @@ namespace NuGetGallery.Frameworks
94
106
  {
95
107
  public static readonly List<NuGetFramework> NetTfms =
96
108
  [
109
+ Net90,
97
110
  Net80,
98
111
  Net70,
99
112
  Net60,
@@ -3,6 +3,7 @@ using System.Net;
3
3
  using System.Text;
4
4
  using System.Text.Json;
5
5
  using System.Text.Json.Serialization;
6
+ using System.Text.RegularExpressions;
6
7
 
7
8
  using Microsoft.Extensions.FileSystemGlobbing;
8
9
 
@@ -13,6 +14,8 @@ using NuGetUpdater.Core.Discover;
13
14
  using NuGetUpdater.Core.Run.ApiModel;
14
15
  using NuGetUpdater.Core.Utilities;
15
16
 
17
+ using static NuGetUpdater.Core.Utilities.EOLHandling;
18
+
16
19
  namespace NuGetUpdater.Core.Run;
17
20
 
18
21
  public class RunWorker
@@ -122,6 +125,7 @@ public class RunWorker
122
125
 
123
126
  // TODO: pull out relevant dependencies, then check each for updates and track the changes
124
127
  var originalDependencyFileContents = new Dictionary<string, string>();
128
+ var originalDependencyFileEOFs = new Dictionary<string, EOLType>();
125
129
  var actualUpdatedDependencies = new List<ReportedDependency>();
126
130
 
127
131
  // track original contents for later handling
@@ -131,6 +135,7 @@ public class RunWorker
131
135
  var localFullPath = Path.Join(repoContentsPath.FullName, repoFullPath);
132
136
  var content = await File.ReadAllTextAsync(localFullPath);
133
137
  originalDependencyFileContents[repoFullPath] = content;
138
+ originalDependencyFileEOFs[repoFullPath] = content.GetPredominantEOL();
134
139
  }
135
140
 
136
141
  foreach (var project in discoveryResult.Projects)
@@ -142,20 +147,43 @@ public class RunWorker
142
147
  var extraFilePath = Path.Join(projectDirectory, extraFile);
143
148
  await TrackOriginalContentsAsync(discoveryResult.Path, extraFilePath);
144
149
  }
145
- // TODO: include global.json, etc.
150
+ }
151
+
152
+ var nonProjectFiles = new[]
153
+ {
154
+ discoveryResult.GlobalJson?.FilePath,
155
+ discoveryResult.DotNetToolsJson?.FilePath,
156
+ }.Where(f => f is not null).Cast<string>().ToArray();
157
+ foreach (var nonProjectFile in nonProjectFiles)
158
+ {
159
+ await TrackOriginalContentsAsync(discoveryResult.Path, nonProjectFile);
146
160
  }
147
161
 
148
162
  // do update
149
163
  var updateOperations = GetUpdateOperations(discoveryResult).ToArray();
150
- foreach (var updateOperation in updateOperations)
164
+ var allowedUpdateOperations = updateOperations.Where(u => IsUpdateAllowed(job, u.Dependency)).ToArray();
165
+
166
+ // requested update isn't listed => SecurityUpdateNotNeeded
167
+ var expectedSecurityUpdateDependencyNames = job.SecurityAdvisories
168
+ .Select(s => s.DependencyName)
169
+ .ToHashSet(StringComparer.OrdinalIgnoreCase);
170
+ var actualUpdateDependencyNames = allowedUpdateOperations
171
+ .Select(u => u.Dependency.Name)
172
+ .ToHashSet(StringComparer.OrdinalIgnoreCase);
173
+ var expectedDependencyUpdateMissingInActual = expectedSecurityUpdateDependencyNames
174
+ .Except(actualUpdateDependencyNames, StringComparer.OrdinalIgnoreCase)
175
+ .OrderBy(d => d, StringComparer.OrdinalIgnoreCase)
176
+ .ToArray();
177
+
178
+ foreach (var missingSecurityUpdate in expectedDependencyUpdateMissingInActual)
151
179
  {
152
- var dependency = updateOperation.Dependency;
153
- if (!IsUpdateAllowed(job, dependency))
154
- {
155
- continue;
156
- }
180
+ await _apiHandler.RecordUpdateJobError(new SecurityUpdateNotNeeded(missingSecurityUpdate));
181
+ }
157
182
 
158
- _logger.Info($"Updating [{dependency.Name}] in [{updateOperation.ProjectPath}]");
183
+ foreach (var updateOperation in allowedUpdateOperations)
184
+ {
185
+ var dependency = updateOperation.Dependency;
186
+ _logger.Info($"Updating [{dependency.Name}] in [{updateOperation.FilePath}]");
159
187
 
160
188
  var dependencyInfo = GetDependencyInfo(job, dependency);
161
189
  var analysisResult = await _analyzeWorker.RunAsync(repoContentsPath.FullName, discoveryResult, dependencyInfo);
@@ -164,7 +192,7 @@ public class RunWorker
164
192
  {
165
193
  // TODO: this is inefficient, but not likely causing a bottleneck
166
194
  var previousDependency = discoveredUpdatedDependencies.Dependencies
167
- .Single(d => d.Name == dependency.Name && d.Requirements.Single().File == updateOperation.ProjectPath);
195
+ .Single(d => d.Name == dependency.Name && d.Requirements.Single().File == updateOperation.FilePath);
168
196
  var updatedDependency = new ReportedDependency()
169
197
  {
170
198
  Name = dependency.Name,
@@ -173,7 +201,7 @@ public class RunWorker
173
201
  [
174
202
  new ReportedRequirement()
175
203
  {
176
- File = updateOperation.ProjectPath,
204
+ File = updateOperation.FilePath,
177
205
  Requirement = analysisResult.UpdatedVersion,
178
206
  Groups = previousDependency.Requirements.Single().Groups,
179
207
  Source = new RequirementSource()
@@ -186,7 +214,7 @@ public class RunWorker
186
214
  PreviousRequirements = previousDependency.Requirements,
187
215
  };
188
216
 
189
- var updateResult = await _updaterWorker.RunAsync(repoContentsPath.FullName, updateOperation.ProjectPath, dependency.Name, dependency.Version!, analysisResult.UpdatedVersion, isTransitive: dependency.IsTransitive);
217
+ var updateResult = await _updaterWorker.RunAsync(repoContentsPath.FullName, updateOperation.FilePath, dependency.Name, dependency.Version!, analysisResult.UpdatedVersion, isTransitive: dependency.IsTransitive);
190
218
  // TODO: need to report if anything was actually updated
191
219
  if (updateResult.Error is null)
192
220
  {
@@ -203,6 +231,10 @@ public class RunWorker
203
231
  var localFullPath = Path.GetFullPath(Path.Join(repoContentsPath.FullName, repoFullPath));
204
232
  var originalContent = originalDependencyFileContents[repoFullPath];
205
233
  var updatedContent = await File.ReadAllTextAsync(localFullPath);
234
+
235
+ updatedContent = updatedContent.SetEOL(originalDependencyFileEOFs[repoFullPath]);
236
+ await File.WriteAllTextAsync(localFullPath, updatedContent);
237
+
206
238
  if (updatedContent != originalContent)
207
239
  {
208
240
  updatedDependencyFiles[localFullPath] = new DependencyFile()
@@ -223,7 +255,11 @@ public class RunWorker
223
255
  var extraFilePath = Path.Join(projectDirectory, extraFile);
224
256
  await AddUpdatedFileIfDifferentAsync(discoveryResult.Path, extraFilePath);
225
257
  }
226
- // TODO: handle global.json, etc.
258
+ }
259
+
260
+ foreach (var nonProjectFile in nonProjectFiles)
261
+ {
262
+ await AddUpdatedFileIfDifferentAsync(discoveryResult.Path, nonProjectFile);
227
263
  }
228
264
 
229
265
  if (updatedDependencyFiles.Count > 0)
@@ -266,34 +302,50 @@ public class RunWorker
266
302
  return result;
267
303
  }
268
304
 
269
- internal static IEnumerable<(string ProjectPath, Dependency Dependency)> GetUpdateOperations(WorkspaceDiscoveryResult discovery)
305
+ internal static IEnumerable<(string FilePath, Dependency Dependency)> GetUpdateOperations(WorkspaceDiscoveryResult discovery)
270
306
  {
271
- // discovery is grouped by project then dependency, but we want to pivot and return a list of update operations sorted by dependency name then project path
307
+ // 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
272
308
 
273
309
  var updateOrder = new Dictionary<string, Dictionary<string, Dictionary<string, Dependency>>>(StringComparer.OrdinalIgnoreCase);
274
- // <dependency name, <project path, specific dependencies>>
310
+ // <dependency name, <file path, specific dependencies>>
275
311
 
276
312
  // collect
277
- foreach (var project in discovery.Projects)
313
+ void CollectDependenciesForFile(string filePath, IEnumerable<Dependency> dependencies)
278
314
  {
279
- var projectPath = Path.Join(discovery.Path, project.FilePath).FullyNormalizedRootedPath();
280
- foreach (var dependency in project.Dependencies)
315
+ foreach (var dependency in dependencies)
281
316
  {
282
317
  var dependencyGroup = updateOrder.GetOrAdd(dependency.Name, () => new Dictionary<string, Dictionary<string, Dependency>>(PathComparer.Instance));
283
- var dependenciesForProject = dependencyGroup.GetOrAdd(projectPath, () => new Dictionary<string, Dependency>(StringComparer.OrdinalIgnoreCase));
284
- dependenciesForProject[dependency.Name] = dependency;
318
+ var dependenciesForFile = dependencyGroup.GetOrAdd(filePath, () => new Dictionary<string, Dependency>(StringComparer.OrdinalIgnoreCase));
319
+ dependenciesForFile[dependency.Name] = dependency;
285
320
  }
286
321
  }
322
+ foreach (var project in discovery.Projects)
323
+ {
324
+ var projectPath = Path.Join(discovery.Path, project.FilePath).FullyNormalizedRootedPath();
325
+ CollectDependenciesForFile(projectPath, project.Dependencies);
326
+ }
327
+
328
+ if (discovery.GlobalJson is not null)
329
+ {
330
+ var globalJsonPath = Path.Join(discovery.Path, discovery.GlobalJson.FilePath).FullyNormalizedRootedPath();
331
+ CollectDependenciesForFile(globalJsonPath, discovery.GlobalJson.Dependencies);
332
+ }
333
+
334
+ if (discovery.DotNetToolsJson is not null)
335
+ {
336
+ var dotnetToolsJsonPath = Path.Join(discovery.Path, discovery.DotNetToolsJson.FilePath).FullyNormalizedRootedPath();
337
+ CollectDependenciesForFile(dotnetToolsJsonPath, discovery.DotNetToolsJson.Dependencies);
338
+ }
287
339
 
288
340
  // return
289
341
  foreach (var dependencyName in updateOrder.Keys.OrderBy(k => k, StringComparer.OrdinalIgnoreCase))
290
342
  {
291
- var projectDependencies = updateOrder[dependencyName];
292
- foreach (var projectPath in projectDependencies.Keys.OrderBy(p => p, PathComparer.Instance))
343
+ var fileDependencies = updateOrder[dependencyName];
344
+ foreach (var filePath in fileDependencies.Keys.OrderBy(p => p, PathComparer.Instance))
293
345
  {
294
- var dependencies = projectDependencies[projectPath];
346
+ var dependencies = fileDependencies[filePath];
295
347
  var dependency = dependencies[dependencyName];
296
- yield return (projectPath, dependency);
348
+ yield return (filePath, dependency);
297
349
  }
298
350
  }
299
351
  }
@@ -440,35 +492,69 @@ public class RunWorker
440
492
  }
441
493
  }
442
494
 
495
+ var allDependenciesWithFilePath = discoveryResult.Projects.SelectMany(p =>
496
+ {
497
+ return p.Dependencies
498
+ .Where(d => d.Version is not null)
499
+ .Select(d =>
500
+ (p.FilePath, new ReportedDependency()
501
+ {
502
+ Name = d.Name,
503
+ Requirements = [new ReportedRequirement()
504
+ {
505
+ File = GetFullRepoPath(p.FilePath),
506
+ Requirement = d.Version!,
507
+ Groups = ["dependencies"],
508
+ }],
509
+ Version = d.Version,
510
+ }));
511
+ }).ToList();
512
+
513
+ var nonProjectDependencySet = new (string?, IEnumerable<Dependency>)[]
514
+ {
515
+ (discoveryResult.GlobalJson?.FilePath, discoveryResult.GlobalJson?.Dependencies ?? []),
516
+ (discoveryResult.DotNetToolsJson?.FilePath, discoveryResult.DotNetToolsJson?.Dependencies ?? []),
517
+ };
518
+
519
+ foreach (var (filePath, dependencies) in nonProjectDependencySet)
520
+ {
521
+ if (filePath is null)
522
+ {
523
+ continue;
524
+ }
525
+
526
+ allDependenciesWithFilePath.AddRange(dependencies
527
+ .Where(d => d.Version is not null)
528
+ .Select(d =>
529
+ (filePath, new ReportedDependency()
530
+ {
531
+ Name = d.Name,
532
+ Requirements = [new ReportedRequirement()
533
+ {
534
+ File = GetFullRepoPath(filePath),
535
+ Requirement = d.Version!,
536
+ Groups = ["dependencies"],
537
+ }],
538
+ Version = d.Version,
539
+ })));
540
+ }
541
+
542
+ var sortedDependencies = allDependenciesWithFilePath
543
+ .OrderBy(pair => Path.Join(discoveryResult.Path, pair.FilePath).FullyNormalizedRootedPath(), PathComparer.Instance)
544
+ .ThenBy(pair => pair.Item2.Name, StringComparer.OrdinalIgnoreCase)
545
+ .Select(pair => pair.Item2)
546
+ .ToArray();
547
+
443
548
  var dependencyFiles = discoveryResult.Projects
444
549
  .Select(p => GetFullRepoPath(p.FilePath))
445
550
  .Concat(auxiliaryFiles)
446
551
  .Distinct()
447
552
  .OrderBy(p => p)
448
553
  .ToArray();
449
- var orderedProjects = discoveryResult.Projects
450
- .OrderBy(p => Path.Join(discoveryResult.Path, p.FilePath).FullyNormalizedRootedPath(), PathComparer.Instance)
451
- .ToArray();
554
+
452
555
  var updatedDependencyList = new UpdatedDependencyList()
453
556
  {
454
- Dependencies = orderedProjects.SelectMany(p =>
455
- {
456
- return p.Dependencies
457
- .Where(d => d.Version is not null)
458
- .OrderBy(d => d.Name, StringComparer.OrdinalIgnoreCase)
459
- .Select(d =>
460
- new ReportedDependency()
461
- {
462
- Name = d.Name,
463
- Requirements = [new ReportedRequirement()
464
- {
465
- File = GetFullRepoPath(p.FilePath),
466
- Requirement = d.Version!,
467
- Groups = ["dependencies"],
468
- }],
469
- Version = d.Version,
470
- });
471
- }).ToArray(),
557
+ Dependencies = sortedDependencies,
472
558
  DependencyFiles = dependencyFiles,
473
559
  };
474
560
  return updatedDependencyList;
@@ -0,0 +1,78 @@
1
+ using System.Text.RegularExpressions;
2
+
3
+ namespace NuGetUpdater.Core.Utilities;
4
+
5
+ public static class EOLHandling
6
+ {
7
+ /// <summary>
8
+ /// Used to save (and then restore) which line endings are predominant in a file.
9
+ /// </summary>
10
+ public enum EOLType
11
+ {
12
+ /// <summary>
13
+ /// Line feed - \n
14
+ /// Typical on most systems.
15
+ /// </summary>
16
+ LF,
17
+ /// <summary>
18
+ /// Carriage return - \r
19
+ /// Typical on older MacOS, unlikely (but possible) to come up here
20
+ /// </summary>
21
+ CR,
22
+ /// <summary>
23
+ /// Carriage return and line feed - \r\n.
24
+ /// Typical on Windows
25
+ /// </summary>
26
+ CRLF
27
+ };
28
+
29
+ /// <summary>
30
+ /// Analyze the input string and find the most common line ending type.
31
+ /// </summary>
32
+ /// <param name="content">The string to analyze</param>
33
+ /// <returns>The most common type of line ending in the input string.</returns>
34
+ public static EOLType GetPredominantEOL(this string content)
35
+ {
36
+ // Get stats on EOL characters/character sequences, if one predominates choose that for writing later.
37
+ var lfcount = content.Count(c => c == '\n');
38
+ var crcount = content.Count(c => c == '\r');
39
+ var crlfcount = Regex.Matches(content, "\r\n").Count();
40
+
41
+ // Since CRLF contains both a CR and a LF, subtract it from those counts
42
+ lfcount -= crlfcount;
43
+ crcount -= crlfcount;
44
+ if (crcount > lfcount && crcount > crlfcount)
45
+ {
46
+ return EOLType.CR;
47
+ }
48
+ else if (crlfcount > lfcount)
49
+ {
50
+ return EOLType.CRLF;
51
+ }
52
+ else
53
+ {
54
+ return EOLType.LF;
55
+ }
56
+ }
57
+
58
+ /// <summary>
59
+ /// Given a line ending, modify the input string to uniformly use that line ending.
60
+ /// </summary>
61
+ /// <param name="content">The input string, which may have any combination of line endings.</param>
62
+ /// <param name="desiredEOL">The line ending type to use across the result.</param>
63
+ /// <returns>The string with any line endings swapped to the desired type.</returns>
64
+ /// <exception cref="ArgumentOutOfRangeException">If EOLType is an unexpected value.</exception>
65
+ public static string SetEOL(this string content, EOLType desiredEOL)
66
+ {
67
+ switch (desiredEOL)
68
+ {
69
+ case EOLType.LF:
70
+ return Regex.Replace(content, "(\r\n|\r)", "\n");
71
+ case EOLType.CR:
72
+ return Regex.Replace(content, "(\r\n|\n)", "\r");
73
+ case EOLType.CRLF:
74
+ return Regex.Replace(content, "(\r\n|\r|\n)", "\r\n");
75
+ }
76
+ throw new ArgumentOutOfRangeException(nameof(desiredEOL));
77
+ }
78
+ }
@@ -795,7 +795,7 @@ internal static partial class MSBuildHelper
795
795
  );
796
796
  return (exitCode, stdOut, stdErr);
797
797
  });
798
- ThrowOnUnauthenticatedFeed(stdOut);
798
+ ThrowOnError(stdOut);
799
799
  if (exitCode != 0)
800
800
  {
801
801
  logger.Warn($"Error determining target frameworks.\nSTDOUT:\n{stdOut}\nSTDERR:\n{stdErr}");
@@ -975,6 +975,7 @@ internal static partial class MSBuildHelper
975
975
  new Regex(@"Package '(?<PackageName>[^']*)' is not found on source '(?<PackageSource>[^$\r\n]*)'\."),
976
976
  new Regex(@"Unable to find package (?<PackageName>[^ ]+)\. No packages exist with this id in source\(s\): (?<PackageSource>.*)$", RegexOptions.Multiline),
977
977
  new Regex(@"Unable to find package (?<PackageName>[^ ]+) with version \((?<PackageVersion>[^)]+)\)"),
978
+ new Regex(@"Could not resolve SDK ""(?<PackageName>[^ ]+)""\."),
978
979
  };
979
980
  var matches = patterns.Select(p => p.Match(output)).Where(m => m.Success);
980
981
  if (matches.Any())
@@ -1300,6 +1300,56 @@ public partial class AnalyzeWorkerTests : AnalyzeWorkerTestBase
1300
1300
  );
1301
1301
  }
1302
1302
 
1303
+ [Fact]
1304
+ public async Task NuGetSourceDoesNotExist()
1305
+ {
1306
+ // this test simulates a `NuGet.Config` that points to an internal-only domain that dependabot doesn't have access to
1307
+ await TestAnalyzeAsync(
1308
+ extraFiles: [
1309
+ ("NuGet.Config", """
1310
+ <configuration>
1311
+ <packageSources>
1312
+ <clear />
1313
+ <add key="feed_that_does_not_exist" value="https://this-domain-does-not-exist/nuget/v2" />
1314
+ </packageSources>
1315
+ </configuration>
1316
+ """)
1317
+ ],
1318
+ discovery: new()
1319
+ {
1320
+ Path = "/",
1321
+ Projects = [
1322
+ new()
1323
+ {
1324
+ FilePath = "./project.csproj",
1325
+ TargetFrameworks = ["net9.0"],
1326
+ Dependencies = [
1327
+ new("Some.Package", "1.0.0", DependencyType.PackageReference),
1328
+ ],
1329
+ ReferencedProjectPaths = [],
1330
+ ImportedFiles = [],
1331
+ AdditionalFiles = [],
1332
+ }
1333
+ ]
1334
+ },
1335
+ dependencyInfo: new()
1336
+ {
1337
+ Name = "Some.Package",
1338
+ Version = "1.0.0",
1339
+ IgnoredVersions = [],
1340
+ IsVulnerable = false,
1341
+ Vulnerabilities = [],
1342
+ },
1343
+ expectedResult: new()
1344
+ {
1345
+ CanUpdate = false,
1346
+ UpdatedVersion = "1.0.0",
1347
+ VersionComesFromMultiDependencyProperty = false,
1348
+ UpdatedDependencies = [],
1349
+ }
1350
+ );
1351
+ }
1352
+
1303
1353
  [Fact]
1304
1354
  public void DeserializeDependencyInfo_UnsupportedIgnoredVersionsAreIgnored()
1305
1355
  {
@@ -1355,4 +1355,30 @@ public partial class DiscoveryWorkerTests : DiscoveryWorkerTestBase
1355
1355
  }
1356
1356
  );
1357
1357
  }
1358
+
1359
+ [Fact]
1360
+ public async Task MissingFileIsReported()
1361
+ {
1362
+ await TestDiscoveryAsync(
1363
+ packages: [],
1364
+ experimentsManager: new ExperimentsManager() { UseDirectDiscovery = true, InstallDotnetSdks = true },
1365
+ workspacePath: "",
1366
+ files: [
1367
+ ("project.csproj", """
1368
+ <Project Sdk="Microsoft.NET.Sdk">
1369
+ <Import Project="file-that-does-not-exist.props" />
1370
+ <PropertyGroup>
1371
+ <TargetFramework>net8.0</TargetFramework>
1372
+ </PropertyGroup>
1373
+ </Project>
1374
+ """)
1375
+ ],
1376
+ expectedResult: new()
1377
+ {
1378
+ Path = "",
1379
+ Projects = [],
1380
+ ErrorRegex = @"file-that-does-not-exist\.props",
1381
+ }
1382
+ );
1383
+ }
1358
1384
  }
@@ -7,6 +7,8 @@ namespace NuGetUpdater.Core.Test.FrameworkChecker;
7
7
  public class CompatibilityCheckerFacts
8
8
  {
9
9
  [Theory]
10
+ [InlineData("net9.0", "net9.0")]
11
+ [InlineData("net9.0", "net8.0")]
10
12
  [InlineData("net8.0", "net8.0")]
11
13
  [InlineData("net8.0", "net7.0")]
12
14
  [InlineData("net7.0", "net7.0")]
@@ -58,7 +58,7 @@ public class MiscellaneousTests
58
58
  public void GetUpdateOperations(WorkspaceDiscoveryResult discovery, (string ProjectPath, string DependencyName)[] expectedUpdateOperations)
59
59
  {
60
60
  var updateOperations = RunWorker.GetUpdateOperations(discovery).ToArray();
61
- var actualUpdateOperations = updateOperations.Select(uo => (uo.ProjectPath, uo.Dependency.Name)).ToArray();
61
+ var actualUpdateOperations = updateOperations.Select(uo => (uo.FilePath, uo.Dependency.Name)).ToArray();
62
62
  Assert.Equal(expectedUpdateOperations, actualUpdateOperations);
63
63
  }
64
64
 
@@ -94,6 +94,34 @@ public class MiscellaneousTests
94
94
  ("/src/Common.csproj", "Package.D"),
95
95
  },
96
96
  ];
97
+
98
+ yield return
99
+ [
100
+ new WorkspaceDiscoveryResult()
101
+ {
102
+ Path = "",
103
+ Projects = [],
104
+ GlobalJson = new()
105
+ {
106
+ FilePath = "global.json",
107
+ Dependencies = [
108
+ new("Some.MSBuild.Sdk", "1.0.0", DependencyType.MSBuildSdk)
109
+ ]
110
+ },
111
+ DotNetToolsJson = new()
112
+ {
113
+ FilePath = ".config/dotnet-tools.json",
114
+ Dependencies = [
115
+ new("some-tool", "2.0.0", DependencyType.DotNetTool)
116
+ ]
117
+ }
118
+ },
119
+ new (string, string)[]
120
+ {
121
+ ("/.config/dotnet-tools.json", "some-tool"),
122
+ ("/global.json", "Some.MSBuild.Sdk"),
123
+ }
124
+ ];
97
125
  }
98
126
 
99
127
  public static IEnumerable<object?[]> RequirementsFromIgnoredVersionsData()