dependabot-nuget 0.267.0 → 0.270.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.
@@ -13,9 +13,12 @@ using Microsoft.Build.Exceptions;
13
13
  using Microsoft.Build.Locator;
14
14
  using Microsoft.Extensions.FileSystemGlobbing;
15
15
 
16
+ using NuGet;
16
17
  using NuGet.Configuration;
18
+ using NuGet.Frameworks;
17
19
  using NuGet.Versioning;
18
20
 
21
+ using NuGetUpdater.Core.Analyze;
19
22
  using NuGetUpdater.Core.Utilities;
20
23
 
21
24
  namespace NuGetUpdater.Core;
@@ -31,29 +34,54 @@ internal static partial class MSBuildHelper
31
34
  // Ensure MSBuild types are registered before calling a method that loads the types
32
35
  if (!IsMSBuildRegistered)
33
36
  {
34
- var candidateDirectories = PathHelper.GetAllDirectoriesToRoot(currentDirectory, rootDirectory);
35
- var globalJsonPaths = candidateDirectories.Select(d => Path.Combine(d, "global.json")).Where(File.Exists).Select(p => (p, p + Guid.NewGuid().ToString())).ToArray();
36
- foreach (var (globalJsonPath, tempGlobalJsonPath) in globalJsonPaths)
37
- {
38
- Console.WriteLine($"Temporarily removing `global.json` from `{Path.GetDirectoryName(globalJsonPath)}` for MSBuild detection.");
39
- File.Move(globalJsonPath, tempGlobalJsonPath);
40
- }
41
-
42
- try
37
+ SidelineGlobalJsonAsync(currentDirectory, rootDirectory, () =>
43
38
  {
44
39
  var defaultInstance = MSBuildLocator.QueryVisualStudioInstances().First();
45
40
  MSBuildPath = defaultInstance.MSBuildPath;
46
41
  MSBuildLocator.RegisterInstance(defaultInstance);
47
- }
48
- finally
42
+ return Task.CompletedTask;
43
+ }).Wait();
44
+ }
45
+ }
46
+
47
+ public static async Task SidelineGlobalJsonAsync(string currentDirectory, string rootDirectory, Func<Task> action, bool retainMSBuildSdks = false)
48
+ {
49
+ var candidateDirectories = PathHelper.GetAllDirectoriesToRoot(currentDirectory, rootDirectory);
50
+ var globalJsonPaths = candidateDirectories.Select(d => Path.Combine(d, "global.json")).Where(File.Exists).Select(p => (p, p + Guid.NewGuid().ToString())).ToArray();
51
+ foreach (var (globalJsonPath, tempGlobalJsonPath) in globalJsonPaths)
52
+ {
53
+ Console.WriteLine($"Temporarily removing `global.json` from `{Path.GetDirectoryName(globalJsonPath)}`{(retainMSBuildSdks ? " and retaining MSBuild SDK declarations" : string.Empty)}.");
54
+ File.Move(globalJsonPath, tempGlobalJsonPath);
55
+ if (retainMSBuildSdks)
49
56
  {
50
- foreach (var (globalJsonpath, tempGlobalJsonPath) in globalJsonPaths)
57
+ // custom SDKs might need to be retained for other operations; rebuild `global.json` with only the relevant key
58
+ var originalContent = await File.ReadAllTextAsync(tempGlobalJsonPath);
59
+ var jsonNode = JsonHelper.ParseNode(originalContent);
60
+ if (jsonNode is JsonObject obj &&
61
+ obj.TryGetPropertyValue("msbuild-sdks", out var sdks) &&
62
+ sdks is not null)
51
63
  {
52
- Console.WriteLine($"Restoring `global.json` to `{Path.GetDirectoryName(globalJsonpath)}` after MSBuild discovery.");
53
- File.Move(tempGlobalJsonPath, globalJsonpath);
64
+ var newObj = new JsonObject()
65
+ {
66
+ ["msbuild-sdks"] = sdks.DeepClone(),
67
+ };
68
+ await File.WriteAllTextAsync(globalJsonPath, newObj.ToJsonString());
54
69
  }
55
70
  }
56
71
  }
72
+
73
+ try
74
+ {
75
+ await action();
76
+ }
77
+ finally
78
+ {
79
+ foreach (var (globalJsonpath, tempGlobalJsonPath) in globalJsonPaths)
80
+ {
81
+ Console.WriteLine($"Restoring `global.json` to `{Path.GetDirectoryName(globalJsonpath)}`.");
82
+ File.Move(tempGlobalJsonPath, globalJsonpath, overwrite: retainMSBuildSdks);
83
+ }
84
+ }
57
85
  }
58
86
 
59
87
  public static IEnumerable<string> GetProjectPathsFromSolution(string solutionPath)
@@ -306,7 +334,182 @@ internal static partial class MSBuildHelper
306
334
  }
307
335
  }
308
336
 
309
- internal static async Task<Dependency[]?> ResolveDependencyConflicts(string repoRoot, string projectPath, string targetFramework, Dependency[] packages, Logger logger)
337
+ internal static bool UseNewDependencySolver()
338
+ {
339
+ return Environment.GetEnvironmentVariable("UseNewNugetPackageResolver") == "true";
340
+ }
341
+
342
+ internal static async Task<Dependency[]?> ResolveDependencyConflicts(string repoRoot, string projectPath, string targetFramework, Dependency[] packages, Dependency[] update, Logger logger)
343
+ {
344
+ if (UseNewDependencySolver())
345
+ {
346
+ return await ResolveDependencyConflictsNew(repoRoot, projectPath, targetFramework, packages, update, logger);
347
+ }
348
+ else
349
+ {
350
+ return await ResolveDependencyConflictsOld(repoRoot, projectPath, targetFramework, packages, logger);
351
+ }
352
+ }
353
+
354
+ internal static async Task<Dependency[]?> ResolveDependencyConflictsNew(string repoRoot, string projectPath, string targetFramework, Dependency[] packages, Dependency[] update, Logger logger)
355
+ {
356
+ var tempDirectory = Directory.CreateTempSubdirectory("package-dependency-coherence_");
357
+ PackageManager packageManager = new PackageManager(repoRoot, projectPath);
358
+
359
+ try
360
+ {
361
+ string tempProjectPath = await CreateTempProjectAsync(tempDirectory, repoRoot, projectPath, targetFramework, packages);
362
+ var (exitCode, stdOut, stdErr) = await ProcessEx.RunAsync("dotnet", $"restore \"{tempProjectPath}\"", workingDirectory: tempDirectory.FullName);
363
+
364
+ // Add Dependency[] packages to List<PackageToUpdate> existingPackages
365
+ List<PackageToUpdate> existingPackages = packages
366
+ .Select(existingPackage => new PackageToUpdate
367
+ {
368
+ PackageName = existingPackage.Name,
369
+ CurrentVersion = existingPackage.Version
370
+ })
371
+ .ToList();
372
+
373
+ // Add Dependency[] update to List<PackageToUpdate> packagesToUpdate
374
+ List<PackageToUpdate> packagesToUpdate = update
375
+ .Where(package => package.Version != null)
376
+ .Select(package => new PackageToUpdate
377
+ {
378
+ PackageName = package.Name,
379
+ NewVersion = package.Version.ToString()
380
+ })
381
+ .ToList();
382
+
383
+ foreach (PackageToUpdate existing in existingPackages)
384
+ {
385
+ var foundPackage = packagesToUpdate.Where(p => string.Equals(p.PackageName, existing.PackageName, StringComparison.OrdinalIgnoreCase));
386
+ if (!foundPackage.Any())
387
+ {
388
+ existing.NewVersion = existing.CurrentVersion;
389
+ }
390
+ }
391
+
392
+ // Create a duplicate set of existingPackages for flexible package reference addition and removal
393
+ List<PackageToUpdate> existingDuplicate = new List<PackageToUpdate>(existingPackages);
394
+
395
+ // Bool to keep track of if anything was added to the existingDuplicate list
396
+ bool added = false;
397
+
398
+ // If package 'isnt there, add it to the existingDuplicate list
399
+ foreach (PackageToUpdate package in packagesToUpdate)
400
+ {
401
+ if (!existingDuplicate.Any(p => string.Equals(p.PackageName, package.PackageName, StringComparison.OrdinalIgnoreCase)))
402
+ {
403
+ existingDuplicate.Add(package);
404
+ added = true;
405
+ }
406
+ }
407
+
408
+ // If you have to use the existingDuplicate list
409
+ if (added == true)
410
+ {
411
+ // Add existing versions to existing list
412
+ packageManager.UpdateExistingPackagesWithNewVersions(existingDuplicate, packagesToUpdate);
413
+
414
+ // Make relationships
415
+ await packageManager.PopulatePackageDependenciesAsync(existingDuplicate, targetFramework, Path.GetDirectoryName(projectPath));
416
+
417
+ // Update all to new versions
418
+ foreach (var package in existingDuplicate)
419
+ {
420
+ string updateResult = await packageManager.UpdateVersion(existingDuplicate, package, targetFramework, Path.GetDirectoryName(projectPath));
421
+ }
422
+ }
423
+
424
+ // Editing existing list because nothing was added to existingDuplicate
425
+ else
426
+ {
427
+ // Add existing versions to existing list
428
+ packageManager.UpdateExistingPackagesWithNewVersions(existingPackages, packagesToUpdate);
429
+
430
+ // Make relationships
431
+ await packageManager.PopulatePackageDependenciesAsync(existingPackages, targetFramework, Path.GetDirectoryName(projectPath));
432
+
433
+ // Update all to new versions
434
+ foreach (var package in existingPackages)
435
+ {
436
+ string updateResult = await packageManager.UpdateVersion(existingPackages, package, targetFramework, Path.GetDirectoryName(projectPath));
437
+ }
438
+ }
439
+
440
+ // Make new list to remove and differentiate between existingDuplicate and existingPackages lists
441
+ List<PackageToUpdate> packagesToRemove = existingDuplicate
442
+ .Where(existingPackageDupe => !existingPackages.Contains(existingPackageDupe) && existingPackageDupe.IsSpecific == true)
443
+ .ToList();
444
+
445
+ foreach (PackageToUpdate package in packagesToRemove)
446
+ {
447
+ existingDuplicate.Remove(package);
448
+ }
449
+
450
+ if (existingDuplicate != null)
451
+ {
452
+ existingPackages = existingDuplicate;
453
+ }
454
+
455
+ // Convert back to Dependency [], use NewVersion if available, otherwise use CurrentVersion
456
+ List<Dependency> candidatePackages = existingPackages
457
+ .Select(package => new Dependency(
458
+ package.PackageName,
459
+ package.NewVersion ?? package.CurrentVersion,
460
+ DependencyType.Unknown,
461
+ null,
462
+ null,
463
+ false,
464
+ false,
465
+ false,
466
+ false,
467
+ false
468
+ ))
469
+ .ToList();
470
+
471
+ // Return as array
472
+ Dependency[] candidatePackagesArray = candidatePackages.ToArray();
473
+
474
+ var targetFrameworks = new NuGetFramework[] { NuGetFramework.Parse(targetFramework) };
475
+
476
+ var resolveProjectPath = projectPath;
477
+
478
+ if (!Path.IsPathRooted(resolveProjectPath) || !File.Exists(resolveProjectPath))
479
+ {
480
+ resolveProjectPath = Path.GetFullPath(Path.Join(repoRoot, resolveProjectPath));
481
+ }
482
+
483
+ NuGetContext nugetContext = new NuGetContext(Path.GetDirectoryName(resolveProjectPath));
484
+
485
+ // Target framework compatibility check
486
+ foreach (var package in candidatePackages)
487
+ {
488
+ if (!NuGetVersion.TryParse(package.Version, out var nuGetVersion))
489
+ {
490
+ // If version is not valid, return original packages and revert
491
+ return packages;
492
+ }
493
+
494
+ var packageIdentity = new NuGet.Packaging.Core.PackageIdentity(package.Name, nuGetVersion);
495
+
496
+ bool isNewPackageCompatible = await CompatibilityChecker.CheckAsync(packageIdentity, targetFrameworks.ToImmutableArray(), nugetContext, logger, CancellationToken.None);
497
+ if (!isNewPackageCompatible)
498
+ {
499
+ // If the package target framework is not compatible, return original packages and revert
500
+ return packages;
501
+ }
502
+ }
503
+
504
+ return candidatePackagesArray;
505
+ }
506
+ finally
507
+ {
508
+ tempDirectory.Delete(recursive: true);
509
+ }
510
+ }
511
+
512
+ internal static async Task<Dependency[]?> ResolveDependencyConflictsOld(string repoRoot, string projectPath, string targetFramework, Dependency[] packages, Logger logger)
310
513
  {
311
514
  var tempDirectory = Directory.CreateTempSubdirectory("package-dependency-coherence_");
312
515
  try
@@ -334,22 +537,22 @@ internal static partial class MSBuildHelper
334
537
  Dictionary<string, HashSet<NuGetVersion>> badPackagesAndCandidateVersionsDictionary = new(StringComparer.OrdinalIgnoreCase);
335
538
 
336
539
  // and for each of those packages, find all versions greater than the one that's currently installed
337
- foreach ((string packageName, NuGetVersion packageVersion) in badPackagesAndVersions)
540
+ foreach ((string PackageName, NuGetVersion packageVersion) in badPackagesAndVersions)
338
541
  {
339
542
  // this command dumps a JSON object with all versions of the specified package from all package sources
340
- (exitCode, stdOut, stdErr) = await ProcessEx.RunAsync("dotnet", $"package search {packageName} --exact-match --format json", workingDirectory: tempDirectory.FullName);
543
+ (exitCode, stdOut, stdErr) = await ProcessEx.RunAsync("dotnet", $"package search {PackageName} --exact-match --format json", workingDirectory: tempDirectory.FullName);
341
544
  if (exitCode != 0)
342
545
  {
343
546
  continue;
344
547
  }
345
548
 
346
549
  // ensure collection exists
347
- if (!badPackagesAndCandidateVersionsDictionary.ContainsKey(packageName))
550
+ if (!badPackagesAndCandidateVersionsDictionary.ContainsKey(PackageName))
348
551
  {
349
- badPackagesAndCandidateVersionsDictionary.Add(packageName, new HashSet<NuGetVersion>());
552
+ badPackagesAndCandidateVersionsDictionary.Add(PackageName, new HashSet<NuGetVersion>());
350
553
  }
351
554
 
352
- HashSet<NuGetVersion> foundVersions = badPackagesAndCandidateVersionsDictionary[packageName];
555
+ HashSet<NuGetVersion> foundVersions = badPackagesAndCandidateVersionsDictionary[PackageName];
353
556
 
354
557
  var json = JsonHelper.ParseNode(stdOut);
355
558
  if (json?["searchResult"] is JsonArray searchResults)
@@ -580,9 +783,9 @@ internal static partial class MSBuildHelper
580
783
  .Where(match => match.Success)
581
784
  .Select(match =>
582
785
  {
583
- var packageName = match.Groups["PackageName"].Value;
584
- var isTransitive = !topLevelPackagesNames.Contains(packageName);
585
- return new Dependency(packageName, match.Groups["PackageVersion"].Value, DependencyType.Unknown, TargetFrameworks: tfms, IsTransitive: isTransitive);
786
+ var PackageName = match.Groups["PackageName"].Value;
787
+ var isTransitive = !topLevelPackagesNames.Contains(PackageName);
788
+ return new Dependency(PackageName, match.Groups["PackageVersion"].Value, DependencyType.Unknown, TargetFrameworks: tfms, IsTransitive: isTransitive);
586
789
  })
587
790
  .ToArray();
588
791
 
@@ -264,6 +264,46 @@ public partial class AnalyzeWorkerTests : AnalyzeWorkerTestBase
264
264
 
265
265
  [Fact]
266
266
  public async Task ReturnsUpToDate_ForMissingDependency()
267
+ {
268
+ await TestAnalyzeAsync(
269
+ packages:
270
+ [
271
+ // no packages listed
272
+ ],
273
+ discovery: new()
274
+ {
275
+ Path = "/",
276
+ Projects = [
277
+ new()
278
+ {
279
+ FilePath = "./project.csproj",
280
+ TargetFrameworks = ["net8.0"],
281
+ Dependencies = [
282
+ new("Some.Package", "1.0.0", DependencyType.PackageReference), // this was found in the source, but doesn't exist in any feed
283
+ ],
284
+ },
285
+ ],
286
+ },
287
+ dependencyInfo: new()
288
+ {
289
+ Name = "Some.Package",
290
+ Version = "1.0.0",
291
+ IgnoredVersions = [],
292
+ IsVulnerable = false,
293
+ Vulnerabilities = [],
294
+ },
295
+ expectedResult: new()
296
+ {
297
+ UpdatedVersion = "1.0.0",
298
+ CanUpdate = false,
299
+ VersionComesFromMultiDependencyProperty = false,
300
+ UpdatedDependencies = [],
301
+ }
302
+ );
303
+ }
304
+
305
+ [Fact]
306
+ public async Task ReturnsUpToDate_ForIgnoredRequirements()
267
307
  {
268
308
  await TestAnalyzeAsync(
269
309
  packages:
@@ -307,6 +347,50 @@ public partial class AnalyzeWorkerTests : AnalyzeWorkerTestBase
307
347
  );
308
348
  }
309
349
 
350
+ [Fact]
351
+ public async Task IgnoredVersionsCanHandleWildcardSpecification()
352
+ {
353
+ await TestAnalyzeAsync(
354
+ packages:
355
+ [
356
+ MockNuGetPackage.CreateSimplePackage("Some.Package", "1.0.0", "net8.0"), // initially this
357
+ MockNuGetPackage.CreateSimplePackage("Some.Package", "1.1.0", "net8.0"), // should update to this
358
+ MockNuGetPackage.CreateSimplePackage("Some.Package", "1.2.0", "net8.0"), // `IgnoredVersions` should prevent this from being selected
359
+ ],
360
+ discovery: new()
361
+ {
362
+ Path = "/",
363
+ Projects = [
364
+ new()
365
+ {
366
+ FilePath = "./project.csproj",
367
+ TargetFrameworks = ["net8.0"],
368
+ Dependencies = [
369
+ new("Some.Package", "1.0.0", DependencyType.PackageReference),
370
+ ],
371
+ },
372
+ ],
373
+ },
374
+ dependencyInfo: new()
375
+ {
376
+ Name = "Some.Package",
377
+ Version = "1.0.0",
378
+ IgnoredVersions = [Requirement.Parse("> 1.1.*")],
379
+ IsVulnerable = false,
380
+ Vulnerabilities = [],
381
+ },
382
+ expectedResult: new()
383
+ {
384
+ UpdatedVersion = "1.1.0",
385
+ CanUpdate = true,
386
+ VersionComesFromMultiDependencyProperty = false,
387
+ UpdatedDependencies = [
388
+ new("Some.Package", "1.1.0", DependencyType.Unknown, TargetFrameworks: ["net8.0"]),
389
+ ],
390
+ }
391
+ );
392
+ }
393
+
310
394
  [Fact]
311
395
  public async Task VersionFinderCanHandle404FromPackageSource_V2()
312
396
  {
@@ -33,6 +33,8 @@ public class RequirementTests
33
33
  [InlineData("1", "~> 1", true)]
34
34
  [InlineData("2", "~> 1", false)]
35
35
  [InlineData("5.3.8", "< 6, > 5.2.4", true)]
36
+ [InlineData("1.0-preview", ">= 1.x", false)] // wildcards
37
+ [InlineData("1.1-preview", ">= 1.x", true)]
36
38
  public void IsSatisfiedBy(string versionString, string requirementString, bool expected)
37
39
  {
38
40
  var version = NuGetVersion.Parse(versionString);
@@ -43,6 +45,18 @@ public class RequirementTests
43
45
  Assert.Equal(expected, actual);
44
46
  }
45
47
 
48
+ [Theory]
49
+ [InlineData("> 1.*", "> 1.0")] // standard wildcard, single digit
50
+ [InlineData("> 1.2.*", "> 1.2.0")] // standard wildcard, multiple digit
51
+ [InlineData("> 1.a", "> 1.0")] // alternate wildcard, single digit
52
+ [InlineData("> 1.2.a", "> 1.2.0")] // alternate wildcard, multiple digit
53
+ public void Parse_ConvertsWildcardInVersion(string givenRequirementString, string expectedRequirementString)
54
+ {
55
+ var parsedRequirement = Requirement.Parse(givenRequirementString);
56
+ var actualRequirementString = parsedRequirement.ToString();
57
+ Assert.Equal(expectedRequirementString, actualRequirementString);
58
+ }
59
+
46
60
  [Theory]
47
61
  [InlineData("> = 1.0.0")] // Invalid format
48
62
  [InlineData("<>= 1.0.0")] // Invalid Operator
@@ -0,0 +1,23 @@
1
+ namespace NuGetUpdater.Core.Test;
2
+
3
+ public class TemporaryEnvironment : IDisposable
4
+ {
5
+ private readonly List<(string Name, string? Value)> _originalVariables = new();
6
+
7
+ public TemporaryEnvironment((string Name, string Value)[] variables)
8
+ {
9
+ foreach (var (name, value) in variables)
10
+ {
11
+ _originalVariables.Add((name, Environment.GetEnvironmentVariable(name)));
12
+ Environment.SetEnvironmentVariable(name, value);
13
+ }
14
+ }
15
+
16
+ public void Dispose()
17
+ {
18
+ foreach (var (name, value) in _originalVariables)
19
+ {
20
+ Environment.SetEnvironmentVariable(name, value);
21
+ }
22
+ }
23
+ }