dependabot-nuget 0.281.0 → 0.283.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: 3afe0ac22f324c8dacf2c16437998a6b22ad23ae21092f3889d3973eb4a59ddb
4
- data.tar.gz: e3aca7ae87df62bb56d264c5eb88e6f14fc9077e3c3c853cd769e34488d95cfa
3
+ metadata.gz: 6a3154a8f2ecd254dc3463bcca6f6e188e033bcf1d6aea863090f348035a9fe8
4
+ data.tar.gz: 480467b2e57d224ed646f81cd6614fbad2af03154ba07fc0dc4dde36d2ab3440
5
5
  SHA512:
6
- metadata.gz: f25c4209f6aab5ae958a092b34c9ab9a5d1473e1322646dcd7f1a7d0e753e681b975516fe5b416ca62c7ba7be219590f17c4541f4635bbe3355b9213e3ff587d
7
- data.tar.gz: e058837c6034711c21fd672774b3c37940f0f286af89fbd1e4cdefa9af415647f926bd626f13356bae3304f1e18b98cfcc67390f492ce0d8b06fa4bbf301356b
6
+ metadata.gz: 8b56eff2a40277413354577fe830f472bf0c46e72ba22cd948f207de01e2c3e0990ba84f60af648ba5e833fe209f8a7cc46b1172aa44987da2a3a6669cab9df5
7
+ data.tar.gz: 3f0bec73a56ac9e6eeed3e61eee12b0dcac72875325b6e543797b893a7dcbc4e2099f318c8822c115e9db810560df61179f0df68c6008d2474d9b0569a116fb7
@@ -12,6 +12,6 @@
12
12
  <CommonTargetFramework>net9.0</CommonTargetFramework>
13
13
  <ImplicitUsings>enable</ImplicitUsings>
14
14
  <UseArtifactsOutput>true</UseArtifactsOutput>
15
- <ArtifactsPath>$(MSBuildThisFileDirectory)artifacts</ArtifactsPath>
15
+ <ArtifactsPath>$(MSBuildThisFileDirectory)artifacts\$(OS)</ArtifactsPath>
16
16
  </PropertyGroup>
17
17
  </Project>
@@ -18,6 +18,16 @@ internal static class BindingRedirectManager
18
18
  private static readonly XName DependentAssemblyName = AssemblyBinding.GetQualifiedName("dependentAssembly");
19
19
  private static readonly XName BindingRedirectName = AssemblyBinding.GetQualifiedName("bindingRedirect");
20
20
 
21
+ /// <summary>
22
+ /// Updates assembly binding redirects for a project build file.
23
+ /// </summary>
24
+ /// <remarks>
25
+ /// Assembly binding redirects are only applicable to projects targeting .NET Framework.
26
+ /// .NET Framework targets can appear in SDK-style OR non-SDK-style project files, using either packages.config OR `<PackageReference>` MSBuild items.
27
+ /// See: https://learn.microsoft.com/en-us/dotnet/framework/configure-apps/redirect-assembly-versions
28
+ /// https://learn.microsoft.com/en-us/nuget/resources/check-project-format
29
+ /// </remarks>
30
+ /// <param name="projectBuildFile">The project build file (*.xproj) to be updated</param>
21
31
  public static async ValueTask UpdateBindingRedirectsAsync(ProjectBuildFile projectBuildFile)
22
32
  {
23
33
  var configFile = await TryGetRuntimeConfigurationFile(projectBuildFile);
@@ -33,7 +43,7 @@ internal static class BindingRedirectManager
33
43
  var bindings = BindingRedirectResolver.GetBindingRedirects(projectBuildFile.Path, references.Select(static x => x.Include));
34
44
  if (!bindings.Any())
35
45
  {
36
- // no bindings to update
46
+ // no bindings found in the project file, nothing to update
37
47
  return;
38
48
  }
39
49
 
@@ -6,7 +6,18 @@ using NuGet.Versioning;
6
6
 
7
7
  namespace NuGetUpdater.Core;
8
8
 
9
- internal static class SdkPackageUpdater
9
+ /// <summary>
10
+ /// Handles package updates for projects containing `<PackageReference>` MSBuild items.
11
+ /// </summary>
12
+ /// <remarks>
13
+ /// PackageReference items can appear in both SDK-style AND non-SDK-style project files.
14
+ /// By default, PackageReference is used by [SDK-style] projects targeting .NET Core, .NET Standard, and UWP.
15
+ /// By default, packages.config is used by [non-SDK-style] projects targeting .NET Framework; However, they can be migrated to PackageReference too.
16
+ /// See: https://learn.microsoft.com/en-us/nuget/consume-packages/package-references-in-project-files#project-type-support
17
+ /// https://learn.microsoft.com/en-us/nuget/consume-packages/migrate-packages-config-to-package-reference
18
+ /// https://learn.microsoft.com/en-us/nuget/resources/check-project-format
19
+ /// </remarks>
20
+ internal static class PackageReferenceUpdater
10
21
  {
11
22
  public static async Task UpdateDependencyAsync(
12
23
  string repoRootPath,
@@ -17,8 +28,8 @@ internal static class SdkPackageUpdater
17
28
  bool isTransitive,
18
29
  ILogger logger)
19
30
  {
20
- // SDK-style project, modify the XML directly
21
- logger.Log(" Running for SDK-style project");
31
+ // PackageReference project; modify the XML directly
32
+ logger.Log(" Running 'PackageReference' project direct XML update");
22
33
 
23
34
  (ImmutableArray<ProjectBuildFile> buildFiles, string[] tfms) = await MSBuildHelper.LoadBuildFilesAndTargetFrameworksAsync(repoRootPath, projectPath);
24
35
 
@@ -30,19 +41,26 @@ internal static class SdkPackageUpdater
30
41
  return;
31
42
  }
32
43
 
33
- if (isTransitive)
44
+ var peerDependencies = await GetUpdatedPeerDependenciesAsync(repoRootPath, projectPath, tfms, dependencyName, newDependencyVersion, logger);
45
+ if (MSBuildHelper.UseNewDependencySolver())
34
46
  {
35
- await UpdateTransitiveDependencyAsync(repoRootPath, projectPath, dependencyName, newDependencyVersion, buildFiles, logger);
47
+ await UpdateDependencyWithConflictResolution(repoRootPath, buildFiles, tfms, projectPath, dependencyName, previousDependencyVersion, newDependencyVersion, isTransitive, peerDependencies, logger);
36
48
  }
37
49
  else
38
50
  {
39
- var peerDependencies = await GetUpdatedPeerDependenciesAsync(repoRootPath, projectPath, tfms, dependencyName, newDependencyVersion, logger);
40
- if (peerDependencies is null)
51
+ if (isTransitive)
41
52
  {
42
- return;
53
+ await UpdateTransitiveDependencyAsync(repoRootPath, projectPath, dependencyName, newDependencyVersion, buildFiles, logger);
43
54
  }
55
+ else
56
+ {
57
+ if (peerDependencies is null)
58
+ {
59
+ return;
60
+ }
44
61
 
45
- await UpdateTopLevelDepdendency(repoRootPath, buildFiles, tfms, dependencyName, previousDependencyVersion, newDependencyVersion, peerDependencies, logger);
62
+ await UpdateTopLevelDepdendency(repoRootPath, buildFiles, tfms, dependencyName, previousDependencyVersion, newDependencyVersion, peerDependencies, logger);
63
+ }
46
64
  }
47
65
 
48
66
  if (!await AreDependenciesCoherentAsync(repoRootPath, projectPath, dependencyName, logger, buildFiles, tfms))
@@ -53,6 +71,61 @@ internal static class SdkPackageUpdater
53
71
  await SaveBuildFilesAsync(buildFiles, logger);
54
72
  }
55
73
 
74
+ public static async Task UpdateDependencyWithConflictResolution(
75
+ string repoRootPath,
76
+ ImmutableArray<ProjectBuildFile> buildFiles,
77
+ string[] targetFrameworks,
78
+ string projectPath,
79
+ string dependencyName,
80
+ string previousDependencyVersion,
81
+ string newDependencyVersion,
82
+ bool isTransitive,
83
+ IDictionary<string, string> peerDependencies,
84
+ ILogger logger)
85
+ {
86
+ var topLevelDependencies = MSBuildHelper.GetTopLevelPackageDependencyInfos(buildFiles).ToArray();
87
+ var isDependencyTopLevel = topLevelDependencies.Any(d => d.Name.Equals(dependencyName, StringComparison.OrdinalIgnoreCase));
88
+ var dependenciesToUpdate = new[] { new Dependency(dependencyName, newDependencyVersion, DependencyType.PackageReference) };
89
+
90
+ // update the initial dependency...
91
+ TryUpdateDependencyVersion(buildFiles, dependencyName, previousDependencyVersion, newDependencyVersion, logger);
92
+
93
+ // ...and the peer dependencies...
94
+ foreach (var (packageName, packageVersion) in peerDependencies.Where(kvp => string.Compare(kvp.Key, dependencyName, StringComparison.OrdinalIgnoreCase) != 0))
95
+ {
96
+ TryUpdateDependencyVersion(buildFiles, packageName, previousDependencyVersion: null, newDependencyVersion: packageVersion, logger);
97
+ }
98
+
99
+ // ...and everything else
100
+ foreach (var projectFile in buildFiles)
101
+ {
102
+ foreach (var tfm in targetFrameworks)
103
+ {
104
+ var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflicts(repoRootPath, projectFile.Path, tfm, topLevelDependencies, dependenciesToUpdate, logger);
105
+ if (resolvedDependencies is null)
106
+ {
107
+ logger.Log($" Unable to resolve dependency conflicts for {projectFile.Path}.");
108
+ continue;
109
+ }
110
+
111
+ var isDependencyInResolutionSet = resolvedDependencies.Any(d => d.Name.Equals(dependencyName, StringComparison.OrdinalIgnoreCase));
112
+ if (isTransitive && !isDependencyTopLevel && isDependencyInResolutionSet)
113
+ {
114
+ // a transitive dependency had to be pinned; add it here
115
+ await UpdateTransitiveDependencyAsync(repoRootPath, projectPath, dependencyName, newDependencyVersion, buildFiles, logger);
116
+ }
117
+
118
+ // update all resolved dependencies that aren't the initial dependency
119
+ foreach (var resolvedDependency in resolvedDependencies
120
+ .Where(d => !d.Name.Equals(dependencyName, StringComparison.OrdinalIgnoreCase))
121
+ .Where(d => d.Version is not null))
122
+ {
123
+ TryUpdateDependencyVersion(buildFiles, resolvedDependency.Name, previousDependencyVersion: null, newDependencyVersion: resolvedDependency.Version!, logger);
124
+ }
125
+ }
126
+ }
127
+ }
128
+
56
129
  /// <summary>
57
130
  /// Verifies that the package does not already satisfy the requested dependency version.
58
131
  /// </summary>
@@ -307,7 +380,7 @@ internal static class SdkPackageUpdater
307
380
  IDictionary<string, string> peerDependencies,
308
381
  ILogger logger)
309
382
  {
310
-
383
+ // update dependencies...
311
384
  var result = TryUpdateDependencyVersion(buildFiles, dependencyName, previousDependencyVersion, newDependencyVersion, logger);
312
385
  if (result == UpdateResult.NotFound)
313
386
  {
@@ -320,26 +393,13 @@ internal static class SdkPackageUpdater
320
393
  TryUpdateDependencyVersion(buildFiles, packageName, previousDependencyVersion: null, newDependencyVersion: packageVersion, logger);
321
394
  }
322
395
 
323
- // now make all dependency requirements coherent
396
+ // ...and make them all coherent
324
397
  Dependency[] updatedTopLevelDependencies = MSBuildHelper.GetTopLevelPackageDependencyInfos(buildFiles).ToArray();
325
398
  foreach (ProjectBuildFile projectFile in buildFiles)
326
399
  {
327
400
  foreach (string tfm in targetFrameworks)
328
401
  {
329
- if (MSBuildHelper.UseNewDependencySolver())
330
- {
331
- // Find the index of the dependency we are updating and revert it to the previous version
332
- int dependencyIndex = Array.FindIndex(updatedTopLevelDependencies, d => string.Equals(d.Name, dependencyName, StringComparison.OrdinalIgnoreCase));
333
- if (dependencyIndex != -1)
334
- {
335
- var originalDependency = updatedTopLevelDependencies[dependencyIndex];
336
- updatedTopLevelDependencies[dependencyIndex] = originalDependency with { Version = previousDependencyVersion };
337
- }
338
-
339
- }
340
- Dependency[] update = [new Dependency(dependencyName, newDependencyVersion, DependencyType.PackageReference)];
341
- Dependency[]? resolvedDependencies = await MSBuildHelper.ResolveDependencyConflicts(repoRootPath, projectFile.Path, tfm, updatedTopLevelDependencies, update, logger);
342
-
402
+ var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflictsWithBruteForce(repoRootPath, projectFile.Path, tfm, updatedTopLevelDependencies, logger);
343
403
  if (resolvedDependencies is null)
344
404
  {
345
405
  logger.Log($" Unable to resolve dependency conflicts for {projectFile.Path}.");
@@ -360,7 +420,7 @@ internal static class SdkPackageUpdater
360
420
  continue;
361
421
  }
362
422
 
363
- // update all dependencies
423
+ // update all versions
364
424
  foreach (Dependency resolvedDependency in resolvedDependencies
365
425
  .Where(d => !d.Name.Equals(dependencyName, StringComparison.OrdinalIgnoreCase))
366
426
  .Where(d => d.Version is not null))
@@ -13,6 +13,14 @@ using Console = System.Console;
13
13
 
14
14
  namespace NuGetUpdater.Core;
15
15
 
16
+ /// <summary>
17
+ /// Handles package updates for projects that use packages.config.
18
+ /// </summary>
19
+ /// <remarks>
20
+ /// packages.config can appear in non-SDK-style projects, but not in SDK-style projects.
21
+ /// See: https://learn.microsoft.com/en-us/nuget/reference/packages-config
22
+ /// https://learn.microsoft.com/en-us/nuget/resources/check-project-format
23
+ /// <remarks>
16
24
  internal static class PackagesConfigUpdater
17
25
  {
18
26
  public static async Task UpdateDependencyAsync(
@@ -25,9 +33,8 @@ internal static class PackagesConfigUpdater
25
33
  ILogger logger
26
34
  )
27
35
  {
28
- logger.Log($" Found {NuGetHelper.PackagesConfigFileName}; running with NuGet.exe");
29
-
30
- // use NuGet.exe to perform update
36
+ // packages.config project; use NuGet.exe to perform update
37
+ logger.Log($" Found '{NuGetHelper.PackagesConfigFileName}' project; running NuGet.exe update");
31
38
 
32
39
  // ensure local packages directory exists
33
40
  var projectBuildFile = ProjectBuildFile.Open(repoRootPath, projectPath);
@@ -126,10 +133,10 @@ internal static class PackagesConfigUpdater
126
133
  // and doesn't appear in the cache. The message in this case will be "Could not install package
127
134
  // '<name> <version>'...the package does not contain any assembly references or content files that
128
135
  // are compatible with that framework.".
136
+ // 3. Yet another possibility is that the project explicitly imports a targets file without a condition
137
+ // of `Exists(...)`.
129
138
  // The solution in all cases is to run `restore` then try the update again.
130
- if (!retryingAfterRestore &&
131
- (fullOutput.Contains("Existing packages must be restored before performing an install or update.") ||
132
- fullOutput.Contains("the package does not contain any assembly references or content files that are compatible with that framework.")))
139
+ if (!retryingAfterRestore && OutputIndicatesRestoreIsRequired(fullOutput))
133
140
  {
134
141
  retryingAfterRestore = true;
135
142
  logger.Log($" Running NuGet.exe with args: {string.Join(" ", restoreArgs)}");
@@ -139,6 +146,8 @@ internal static class PackagesConfigUpdater
139
146
 
140
147
  if (exitCodeAgain != 0)
141
148
  {
149
+ MSBuildHelper.ThrowOnMissingFile(fullOutput);
150
+ MSBuildHelper.ThrowOnMissingFile(restoreOutput);
142
151
  MSBuildHelper.ThrowOnMissingPackages(restoreOutput);
143
152
  throw new Exception($"Unable to restore.\nOutput:\n${restoreOutput}\n");
144
153
  }
@@ -174,6 +183,13 @@ internal static class PackagesConfigUpdater
174
183
  }
175
184
  }
176
185
 
186
+ private static bool OutputIndicatesRestoreIsRequired(string output)
187
+ {
188
+ return output.Contains("Existing packages must be restored before performing an install or update.")
189
+ || output.Contains("the package does not contain any assembly references or content files that are compatible with that framework.")
190
+ || MSBuildHelper.GetMissingFile(output) is not null;
191
+ }
192
+
177
193
  private static Process[] GetLikelyNuGetSpawnedProcesses()
178
194
  {
179
195
  var processes = Process.GetProcesses().Where(p => p.ProcessName.StartsWith("CredentialProvider", StringComparison.OrdinalIgnoreCase) == true).ToArray();
@@ -206,7 +222,7 @@ internal static class PackagesConfigUpdater
206
222
  {
207
223
  // exact match was found, use it
208
224
  var subpath = GetUpToIndexWithoutTrailingDirectorySeparator(hintPath, hintPathSubStringLocation);
209
- return subpath;
225
+ return subpath.NormalizePathToUnix();
210
226
  }
211
227
 
212
228
  if (partialPathMatch is null)
@@ -244,7 +260,7 @@ internal static class PackagesConfigUpdater
244
260
  }
245
261
  }
246
262
 
247
- return partialPathMatch;
263
+ return partialPathMatch?.NormalizePathToUnix();
248
264
  }
249
265
 
250
266
  private static bool IsHintPathNodeForDependency(this IXmlElementSyntax element, string dependencyName)
@@ -221,7 +221,7 @@ public class UpdaterWorker
221
221
  }
222
222
 
223
223
  // Some repos use a mix of packages.config and PackageReference
224
- await SdkPackageUpdater.UpdateDependencyAsync(repoRootPath, projectPath, dependencyName, previousDependencyVersion, newDependencyVersion, isTransitive, _logger);
224
+ await PackageReferenceUpdater.UpdateDependencyAsync(repoRootPath, projectPath, dependencyName, previousDependencyVersion, newDependencyVersion, isTransitive, _logger);
225
225
 
226
226
  // Update lock file if exists
227
227
  if (File.Exists(Path.Combine(Path.GetDirectoryName(projectPath), "packages.lock.json")))
@@ -341,18 +341,6 @@ internal static partial class MSBuildHelper
341
341
  }
342
342
 
343
343
  internal static async Task<Dependency[]?> ResolveDependencyConflicts(string repoRoot, string projectPath, string targetFramework, Dependency[] packages, Dependency[] update, ILogger logger)
344
- {
345
- if (UseNewDependencySolver())
346
- {
347
- return await ResolveDependencyConflictsNew(repoRoot, projectPath, targetFramework, packages, update, logger);
348
- }
349
- else
350
- {
351
- return await ResolveDependencyConflictsOld(repoRoot, projectPath, targetFramework, packages, logger);
352
- }
353
- }
354
-
355
- internal static async Task<Dependency[]?> ResolveDependencyConflictsNew(string repoRoot, string projectPath, string targetFramework, Dependency[] packages, Dependency[] update, ILogger logger)
356
344
  {
357
345
  var tempDirectory = Directory.CreateTempSubdirectory("package-dependency-coherence_");
358
346
  PackageManager packageManager = new PackageManager(repoRoot, projectPath);
@@ -510,7 +498,7 @@ internal static partial class MSBuildHelper
510
498
  }
511
499
  }
512
500
 
513
- internal static async Task<Dependency[]?> ResolveDependencyConflictsOld(string repoRoot, string projectPath, string targetFramework, Dependency[] packages, ILogger logger)
501
+ internal static async Task<Dependency[]?> ResolveDependencyConflictsWithBruteForce(string repoRoot, string projectPath, string targetFramework, Dependency[] packages, ILogger logger)
514
502
  {
515
503
  var tempDirectory = Directory.CreateTempSubdirectory("package-dependency-coherence_");
516
504
  try
@@ -713,6 +701,8 @@ internal static partial class MSBuildHelper
713
701
  <GenerateDependencyFile>true</GenerateDependencyFile>
714
702
  <RunAnalyzers>false</RunAnalyzers>
715
703
  <NuGetInteractive>false</NuGetInteractive>
704
+ <DesignTimeBuild>true</DesignTimeBuild>
705
+ <TargetPlatformVersion Condition=" $(TargetFramework.Contains('-')) ">1.0</TargetPlatformVersion>
716
706
  </PropertyGroup>
717
707
  <ItemGroup>
718
708
  {packageReferences}
@@ -824,13 +814,24 @@ internal static partial class MSBuildHelper
824
814
  }
825
815
  }
826
816
 
827
- internal static void ThrowOnMissingFile(string output)
817
+ internal static string? GetMissingFile(string output)
828
818
  {
829
819
  var missingFilePattern = new Regex(@"The imported project \""(?<FilePath>.*)\"" was not found");
830
820
  var match = missingFilePattern.Match(output);
831
821
  if (match.Success)
832
822
  {
833
- throw new MissingFileException(match.Groups["FilePath"].Value);
823
+ return match.Groups["FilePath"].Value;
824
+ }
825
+
826
+ return null;
827
+ }
828
+
829
+ internal static void ThrowOnMissingFile(string output)
830
+ {
831
+ var missingFile = GetMissingFile(output);
832
+ if (missingFile is not null)
833
+ {
834
+ throw new MissingFileException(missingFile);
834
835
  }
835
836
  }
836
837
 
@@ -0,0 +1,12 @@
1
+ namespace NuGetUpdater.Core.Test;
2
+
3
+ /// <summary>
4
+ /// Prepares the environment to use the new dependency solver.
5
+ /// </summary>
6
+ public class DependencySolverEnvironment : TemporaryEnvironment
7
+ {
8
+ public DependencySolverEnvironment(bool useDependencySolver = true)
9
+ : base([("UseNewNugetPackageResolver", useDependencySolver ? "true" : "false")])
10
+ {
11
+ }
12
+ }
@@ -30,7 +30,7 @@ public class PackagesConfigUpdaterTests : TestBase
30
30
  """,
31
31
  "Newtonsoft.Json",
32
32
  "7.0.1",
33
- @"..\packages"
33
+ "../packages"
34
34
  ];
35
35
 
36
36
  // project without namespace
@@ -48,7 +48,7 @@ public class PackagesConfigUpdaterTests : TestBase
48
48
  """,
49
49
  "Newtonsoft.Json",
50
50
  "7.0.1",
51
- @"..\packages"
51
+ "../packages"
52
52
  ];
53
53
 
54
54
  // project with non-standard packages path
@@ -66,7 +66,7 @@ public class PackagesConfigUpdaterTests : TestBase
66
66
  """,
67
67
  "Newtonsoft.Json",
68
68
  "7.0.1",
69
- @"..\not-a-path-you-would-expect"
69
+ "../not-a-path-you-would-expect"
70
70
  ];
71
71
  }
72
72
  }
@@ -10,7 +10,7 @@ namespace NuGetUpdater.Core.Test.Update;
10
10
 
11
11
  public partial class UpdateWorkerTests
12
12
  {
13
- public class Sdk : UpdateWorkerTestBase
13
+ public class PackageReference : UpdateWorkerTestBase
14
14
  {
15
15
  [Theory]
16
16
  [InlineData("net472")]
@@ -54,12 +54,12 @@ public partial class UpdateWorkerTests
54
54
  }
55
55
 
56
56
  [Theory]
57
- [InlineData("true")]
58
- [InlineData(null)]
59
- public async Task UpdateVersionChildElement_InProjectFile_ForPackageReferenceIncludeTheory(string variableValue)
57
+ [InlineData(true)]
58
+ [InlineData(false)]
59
+ public async Task UpdateVersionChildElement_InProjectFile_ForPackageReferenceIncludeTheory(bool useDependencySolver)
60
60
  {
61
61
  // update Some.Package from 9.0.1 to 13.0.1
62
- using var env = new TemporaryEnvironment([("UseNewNugetPackageResolver", variableValue)]);
62
+ using var _ = new DependencySolverEnvironment(useDependencySolver);
63
63
  await TestUpdateForProject("Some.Package", "9.0.1", "13.0.1",
64
64
  packages:
65
65
  [
@@ -95,11 +95,72 @@ public partial class UpdateWorkerTests
95
95
  );
96
96
  }
97
97
 
98
+ [Theory]
99
+ [InlineData(true)]
100
+ [InlineData(false)]
101
+ public async Task PeerDependenciesAreUpdatedEvenWhenNotExplicit(bool useDependencySolver)
102
+ {
103
+ using var _ = new DependencySolverEnvironment(useDependencySolver);
104
+ await TestUpdateForProject("Some.Package", "1.0.0", "2.0.0",
105
+ packages:
106
+ [
107
+ MockNuGetPackage.CreateSimplePackage("Some.Package", "1.0.0", "net8.0", [(null, [("Transitive.Package", "[1.0.0]")])]),
108
+ MockNuGetPackage.CreateSimplePackage("Some.Package", "2.0.0", "net8.0", [(null, [("Transitive.Package", "[2.0.0]")])]),
109
+ MockNuGetPackage.CreateSimplePackage("Transitive.Package", "1.0.0", "net8.0"),
110
+ MockNuGetPackage.CreateSimplePackage("Transitive.Package", "2.0.0", "net8.0"),
111
+ ],
112
+ projectFile: ("a/a.csproj", """
113
+ <Project Sdk="Microsoft.NET.Sdk">
114
+ <PropertyGroup>
115
+ <TargetFramework>net8.0</TargetFramework>
116
+ <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
117
+ </PropertyGroup>
118
+ <ItemGroup>
119
+ <PackageReference Include="Some.Package" />
120
+ </ItemGroup>
121
+ </Project>
122
+ """),
123
+ additionalFiles:
124
+ [
125
+ ("Directory.Packages.props", """
126
+ <Project>
127
+ <ItemGroup>
128
+ <PackageVersion Include="Some.Package" Version="1.0.0" />
129
+ <PackageVersion Include="Transitive.Package" Version="1.0.0" />
130
+ </ItemGroup>
131
+ </Project>
132
+ """)
133
+ ],
134
+ expectedProjectContents: """
135
+ <Project Sdk="Microsoft.NET.Sdk">
136
+ <PropertyGroup>
137
+ <TargetFramework>net8.0</TargetFramework>
138
+ <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
139
+ </PropertyGroup>
140
+ <ItemGroup>
141
+ <PackageReference Include="Some.Package" />
142
+ </ItemGroup>
143
+ </Project>
144
+ """,
145
+ additionalFilesExpected:
146
+ [
147
+ ("Directory.Packages.props", """
148
+ <Project>
149
+ <ItemGroup>
150
+ <PackageVersion Include="Some.Package" Version="2.0.0" />
151
+ <PackageVersion Include="Transitive.Package" Version="2.0.0" />
152
+ </ItemGroup>
153
+ </Project>
154
+ """)
155
+ ]
156
+ );
157
+ }
158
+
98
159
  [Fact]
99
160
  public async Task CallingResolveDependencyConflictsNew()
100
161
  {
101
162
  // update Microsoft.CodeAnalysis.Common from 4.9.2 to 4.10.0
102
- using var env = new TemporaryEnvironment([("UseNewNugetPackageResolver", "true")]);
163
+ using var _ = new DependencySolverEnvironment();
103
164
  await TestUpdateForProject("Microsoft.CodeAnalysis.Common", "4.9.2", "4.10.0",
104
165
  // initial
105
166
  projectContents: $"""
@@ -535,11 +596,11 @@ public partial class UpdateWorkerTests
535
596
  }
536
597
 
537
598
  [Theory]
538
- [InlineData(null)]
539
- [InlineData("true")]
540
- public async Task AddPackageReference_InProjectFile_ForTransientDependency(string variableValue)
599
+ [InlineData(true)]
600
+ [InlineData(false)]
601
+ public async Task AddPackageReference_InProjectFile_ForTransientDependency(bool useDependencySolver)
541
602
  {
542
- using var env = new TemporaryEnvironment([("UseNewNugetPackageResolver", variableValue)]);
603
+ using var _ = new DependencySolverEnvironment(useDependencySolver);
543
604
  // add transient package Some.Transient.Dependency from 5.0.1 to 5.0.2
544
605
  await TestUpdateForProject("Some.Transient.Dependency", "5.0.1", "5.0.2", isTransitive: true,
545
606
  packages:
@@ -2914,12 +2975,51 @@ public partial class UpdateWorkerTests
2914
2975
  );
2915
2976
  }
2916
2977
 
2978
+ [Fact]
2979
+ public async Task UpdatingTransitiveDependencyWithNewSolverCanUpdateJustTheTopLevelPackage()
2980
+ {
2981
+ // we've been asked to explicitly update a transitive dependency, but we can solve it by updating the top-level package instead
2982
+ using var _ = new DependencySolverEnvironment();
2983
+ await TestUpdateForProject("Transitive.Package", "1.0.0", "2.0.0",
2984
+ isTransitive: true,
2985
+ packages:
2986
+ [
2987
+ MockNuGetPackage.CreateSimplePackage("Some.Package", "1.0.0", "net8.0", [("net8.0", [("Transitive.Package", "[1.0.0]")])]),
2988
+ MockNuGetPackage.CreateSimplePackage("Some.Package", "2.0.0", "net8.0", [("net8.0", [("Transitive.Package", "[2.0.0]")])]),
2989
+ MockNuGetPackage.CreateSimplePackage("Transitive.Package", "1.0.0", "net8.0"),
2990
+ MockNuGetPackage.CreateSimplePackage("Transitive.Package", "2.0.0", "net8.0"),
2991
+ ],
2992
+ projectContents: """
2993
+ <Project Sdk="Microsoft.NET.Sdk">
2994
+ <PropertyGroup>
2995
+ <TargetFramework>net8.0</TargetFramework>
2996
+ <ManagePackageVersionsCentrally>false</ManagePackageVersionsCentrally>
2997
+ </PropertyGroup>
2998
+ <ItemGroup>
2999
+ <PackageReference Include="Some.Package" Version="1.0.0" />
3000
+ </ItemGroup>
3001
+ </Project>
3002
+ """,
3003
+ expectedProjectContents: """
3004
+ <Project Sdk="Microsoft.NET.Sdk">
3005
+ <PropertyGroup>
3006
+ <TargetFramework>net8.0</TargetFramework>
3007
+ <ManagePackageVersionsCentrally>false</ManagePackageVersionsCentrally>
3008
+ </PropertyGroup>
3009
+ <ItemGroup>
3010
+ <PackageReference Include="Some.Package" Version="2.0.0" />
3011
+ </ItemGroup>
3012
+ </Project>
3013
+ """
3014
+ );
3015
+ }
3016
+
2917
3017
  [Theory]
2918
- [InlineData("true")]
2919
- [InlineData(null)]
2920
- public async Task NoChange_IfThereAreIncoherentVersions(string variableValue)
3018
+ [InlineData(true)]
3019
+ [InlineData(false)]
3020
+ public async Task NoChange_IfThereAreIncoherentVersions(bool useDependencySolver)
2921
3021
  {
2922
- using var env = new TemporaryEnvironment([("UseNewNugetPackageResolver", variableValue)]);
3022
+ using var _ = new DependencySolverEnvironment(useDependencySolver);
2923
3023
 
2924
3024
  // trying to update `Transitive.Dependency` to 1.1.0 would normally pull `Some.Package` from 1.0.0 to 1.1.0,
2925
3025
  // but the TFM doesn't allow it
@@ -2968,6 +3068,42 @@ public partial class UpdateWorkerTests
2968
3068
  );
2969
3069
  }
2970
3070
 
3071
+ [Fact]
3072
+ public async Task ProcessingProjectWithWorkloadReferencesDoesNotFail()
3073
+ {
3074
+ // enumerating the build files will fail if the Aspire workload is not installed; this test ensures we can
3075
+ // still process the update
3076
+ await TestUpdateForProject("Some.Package", "7.0.1", "13.0.1",
3077
+ packages:
3078
+ [
3079
+ MockNuGetPackage.CreateSimplePackage("Some.Package", "7.0.1", "net8.0"),
3080
+ MockNuGetPackage.CreateSimplePackage("Some.Package", "13.0.1", "net8.0"),
3081
+ ],
3082
+ projectContents: """
3083
+ <Project Sdk="Microsoft.NET.Sdk">
3084
+ <PropertyGroup>
3085
+ <TargetFrameworks>net8.0-ios;net8.0-android;net8.0-macos;net8.0-maccatalyst;</TargetFrameworks>
3086
+ <IsAspireHost>true</IsAspireHost>
3087
+ </PropertyGroup>
3088
+ <ItemGroup>
3089
+ <PackageReference Include="Some.Package" Version="7.0.1" />
3090
+ </ItemGroup>
3091
+ </Project>
3092
+ """,
3093
+ expectedProjectContents: """
3094
+ <Project Sdk="Microsoft.NET.Sdk">
3095
+ <PropertyGroup>
3096
+ <TargetFrameworks>net8.0-ios;net8.0-android;net8.0-macos;net8.0-maccatalyst;</TargetFrameworks>
3097
+ <IsAspireHost>true</IsAspireHost>
3098
+ </PropertyGroup>
3099
+ <ItemGroup>
3100
+ <PackageReference Include="Some.Package" Version="13.0.1" />
3101
+ </ItemGroup>
3102
+ </Project>
3103
+ """
3104
+ );
3105
+ }
3106
+
2971
3107
  [Fact]
2972
3108
  public async Task ProcessingProjectWithAspireDoesNotFailEvenThoughWorkloadIsNotInstalled()
2973
3109
  {
@@ -3005,11 +3141,11 @@ public partial class UpdateWorkerTests
3005
3141
  }
3006
3142
 
3007
3143
  [Theory]
3008
- [InlineData("true")]
3009
- [InlineData(null)]
3010
- public async Task UnresolvablePropertyDoesNotStopOtherUpdates(string variableValue)
3144
+ [InlineData(true)]
3145
+ [InlineData(false)]
3146
+ public async Task UnresolvablePropertyDoesNotStopOtherUpdates(bool useDependencySolver)
3011
3147
  {
3012
- using var env = new TemporaryEnvironment([("UseNewNugetPackageResolver", variableValue)]);
3148
+ using var _ = new DependencySolverEnvironment(useDependencySolver);
3013
3149
 
3014
3150
  // the property `$(SomeUnresolvableProperty)` cannot be resolved
3015
3151
  await TestUpdateForProject("Some.Package", "7.0.1", "13.0.1",
@@ -3044,12 +3180,53 @@ public partial class UpdateWorkerTests
3044
3180
  );
3045
3181
  }
3046
3182
 
3183
+
3184
+ [Theory]
3185
+ [InlineData(true)]
3186
+ [InlineData(false)]
3187
+ public async Task ProjectWithWorkloadsShouldNotFail(bool useDependencySolver)
3188
+ {
3189
+ using var _ = new DependencySolverEnvironment(useDependencySolver);
3190
+
3191
+ // the property `$(SomeUnresolvableProperty)` cannot be resolved
3192
+ await TestUpdateForProject("Some.Package", "7.0.1", "13.0.1",
3193
+ packages:
3194
+ [
3195
+ MockNuGetPackage.CreateSimplePackage("Some.Package", "7.0.1", "net8.0"),
3196
+ MockNuGetPackage.CreateSimplePackage("Some.Package", "13.0.1", "net8.0"),
3197
+ MockNuGetPackage.CreateSimplePackage("Some.Other.Package", "1.0.0", "net8.0"),
3198
+ ],
3199
+ projectContents: """
3200
+ <Project Sdk="Microsoft.NET.Sdk">
3201
+ <PropertyGroup>
3202
+ <TargetFrameworks>net8.0;net8.0-ios;net8.0-android;net8.0-macos;net8.0-maccatalyst</TargetFrameworks>
3203
+ </PropertyGroup>
3204
+ <ItemGroup>
3205
+ <PackageReference Include="Some.Other.Package" Version="$(SomeUnresolvableProperty)" />
3206
+ <PackageReference Include="Some.Package" Version="7.0.1" />
3207
+ </ItemGroup>
3208
+ </Project>
3209
+ """,
3210
+ expectedProjectContents: """
3211
+ <Project Sdk="Microsoft.NET.Sdk">
3212
+ <PropertyGroup>
3213
+ <TargetFrameworks>net8.0;net8.0-ios;net8.0-android;net8.0-macos;net8.0-maccatalyst</TargetFrameworks>
3214
+ </PropertyGroup>
3215
+ <ItemGroup>
3216
+ <PackageReference Include="Some.Other.Package" Version="$(SomeUnresolvableProperty)" />
3217
+ <PackageReference Include="Some.Package" Version="13.0.1" />
3218
+ </ItemGroup>
3219
+ </Project>
3220
+ """
3221
+ );
3222
+ }
3223
+
3047
3224
  [Theory]
3048
- [InlineData("true")]
3049
- [InlineData(null)]
3050
- public async Task UpdatingPackageAlsoUpdatesAnythingWithADependencyOnTheUpdatedPackage(string variableValue)
3225
+ [InlineData(true)]
3226
+ [InlineData(false)]
3227
+ public async Task UpdatingPackageAlsoUpdatesAnythingWithADependencyOnTheUpdatedPackage(bool useDependencySolver)
3051
3228
  {
3052
- using var env = new TemporaryEnvironment([("UseNewNugetPackageResolver", variableValue)]);
3229
+ using var _ = new DependencySolverEnvironment(useDependencySolver);
3053
3230
 
3054
3231
  // updating Some.Package from 3.3.30 requires that Some.Package.Extensions also be updated
3055
3232
  await TestUpdateForProject("Some.Package", "3.3.30", "3.4.3",
@@ -308,6 +308,83 @@ public partial class UpdateWorkerTests
308
308
  );
309
309
  }
310
310
 
311
+ [Fact]
312
+ public async Task UpdatePackageWithTargetsFileWhereProjectUsesBackslashes()
313
+ {
314
+ // The bug that caused this test to be written did not repro on Windows. The reason is that the packages
315
+ // directory is determined to be `..\packages`, but the backslash was retained. Later when packages were
316
+ // restored to that location, a directory with a name like `..?packages` would be created which didn't
317
+ // match the <Import> element's path of "..\packages\..." that had no `Condition="Exists(path)"` attribute.
318
+ await TestUpdateForProject("Some.Package", "1.0.0", "2.0.0",
319
+ packages:
320
+ [
321
+ MockNuGetPackage.CreateSimplePackage("Some.Package", "1.0.0", "net45"),
322
+ MockNuGetPackage.CreateSimplePackage("Some.Package", "2.0.0", "net45"),
323
+ new MockNuGetPackage("Package.With.Targets", "1.0.0", Files: [("build/SomeFile.targets", Encoding.UTF8.GetBytes("<Project />"))]),
324
+ ],
325
+ // existing
326
+ projectFile: ("src/project.csproj", """
327
+ <Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
328
+ <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
329
+ <PropertyGroup>
330
+ <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
331
+ </PropertyGroup>
332
+ <ItemGroup>
333
+ <None Include="packages.config" />
334
+ </ItemGroup>
335
+ <ItemGroup>
336
+ <Reference Include="Some.Package">
337
+ <HintPath>..\packages\Some.Package.1.0.0\lib\net45\Some.Package.dll</HintPath>
338
+ <Private>True</Private>
339
+ </Reference>
340
+ </ItemGroup>
341
+ <Import Project="..\packages\Package.With.Targets.1.0.0\build\SomeFile.targets" />
342
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
343
+ </Project>
344
+ """),
345
+ additionalFiles:
346
+ [
347
+ ("src/packages.config", """
348
+ <?xml version="1.0" encoding="utf-8"?>
349
+ <packages>
350
+ <package id="Package.With.Targets" version="1.0.0" targetFramework="net45" />
351
+ <package id="Some.Package" version="1.0.0" targetFramework="net45" />
352
+ </packages>
353
+ """)
354
+ ],
355
+ // expected
356
+ expectedProjectContents: """
357
+ <Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
358
+ <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
359
+ <PropertyGroup>
360
+ <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
361
+ </PropertyGroup>
362
+ <ItemGroup>
363
+ <None Include="packages.config" />
364
+ </ItemGroup>
365
+ <ItemGroup>
366
+ <Reference Include="Some.Package">
367
+ <HintPath>..\packages\Some.Package.2.0.0\lib\net45\Some.Package.dll</HintPath>
368
+ <Private>True</Private>
369
+ </Reference>
370
+ </ItemGroup>
371
+ <Import Project="..\packages\Package.With.Targets.1.0.0\build\SomeFile.targets" />
372
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
373
+ </Project>
374
+ """,
375
+ additionalFilesExpected:
376
+ [
377
+ ("src/packages.config", """
378
+ <?xml version="1.0" encoding="utf-8"?>
379
+ <packages>
380
+ <package id="Package.With.Targets" version="1.0.0" targetFramework="net45" />
381
+ <package id="Some.Package" version="2.0.0" targetFramework="net45" />
382
+ </packages>
383
+ """)
384
+ ]
385
+ );
386
+ }
387
+
311
388
  [Fact]
312
389
  public async Task UpdateSingleDependencyInPackagesConfigButNotToLatest()
313
390
  {
@@ -442,9 +442,9 @@ public class MSBuildHelperTests : TestBase
442
442
  }
443
443
 
444
444
  [Fact]
445
- public async Task DependencyConflictsCanBeResolved()
445
+ public async Task DependencyConflictsCanBeResolvedWithBruteForce()
446
446
  {
447
- var repoRoot = Directory.CreateTempSubdirectory($"test_{nameof(DependencyConflictsCanBeResolved)}_");
447
+ var repoRoot = Directory.CreateTempSubdirectory($"test_{nameof(DependencyConflictsCanBeResolvedWithBruteForce)}_");
448
448
  MockNuGetPackage[] testPackages =
449
449
  [
450
450
  // some base packages
@@ -483,7 +483,7 @@ public class MSBuildHelperTests : TestBase
483
483
  {
484
484
  new Dependency("Some.Other.Package", "1.2.0", DependencyType.PackageReference),
485
485
  };
486
- var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflicts(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger());
486
+ var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflictsWithBruteForce(repoRoot.FullName, projectPath, "net8.0", dependencies, new TestLogger());
487
487
  Assert.NotNull(resolvedDependencies);
488
488
  Assert.Equal(2, resolvedDependencies.Length);
489
489
  Assert.Equal("Some.Package", resolvedDependencies[0].Name);
@@ -497,6 +497,44 @@ public class MSBuildHelperTests : TestBase
497
497
  }
498
498
  }
499
499
 
500
+ [Fact]
501
+ public void UpdateWithWorkloadsTargetFrameworks()
502
+ {
503
+ // Arrange
504
+ var projectContents = """
505
+ <Project>
506
+ <PropertyGroup>
507
+ <TargetFrameworks>net8.0-ios;net8.0-android;net8.0-macos;net8.0-maccatalyst;</TargetFrameworks>
508
+ </PropertyGroup>
509
+ <ItemGroup>
510
+ <PackageReference Include="Some.Package" Version="$(PackageVersion1)" />
511
+ </ItemGroup>
512
+ </Project>
513
+ """;
514
+ var propertyInfo = new Dictionary<string, Property>
515
+ {
516
+ { "PackageVersion1", new("PackageVersion1", "1.1.1", "Packages.props") },
517
+ };
518
+
519
+ // Act
520
+ var (resultType, _, evaluatedValue, _, _) = MSBuildHelper.GetEvaluatedValue(projectContents, propertyInfo);
521
+
522
+ Assert.Equal(EvaluationResultType.Success, resultType);
523
+
524
+ // Assert
525
+ Assert.Equal("""
526
+ <Project>
527
+ <PropertyGroup>
528
+ <TargetFrameworks>net8.0-ios;net8.0-android;net8.0-macos;net8.0-maccatalyst;</TargetFrameworks>
529
+ </PropertyGroup>
530
+ <ItemGroup>
531
+ <PackageReference Include="Some.Package" Version="1.1.1" />
532
+ </ItemGroup>
533
+ </Project>
534
+ """, evaluatedValue);
535
+ }
536
+
537
+
500
538
  #region
501
539
  // Updating root package
502
540
  // CS-Script Code to 2.0.0 requires its dependency Microsoft.CodeAnalysis.CSharp.Scripting to be 3.6.0 and its transitive dependency Microsoft.CodeAnalysis.Common to be 3.6.0
@@ -533,7 +571,7 @@ public class MSBuildHelperTests : TestBase
533
571
  new Dependency("CS-Script.Core", "2.0.0", DependencyType.PackageReference),
534
572
  };
535
573
 
536
- var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflictsNew(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger());
574
+ var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflicts(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger());
537
575
  Assert.NotNull(resolvedDependencies);
538
576
  Assert.Equal(3, resolvedDependencies.Length);
539
577
  Assert.Equal("CS-Script.Core", resolvedDependencies[0].Name);
@@ -578,7 +616,7 @@ public class MSBuildHelperTests : TestBase
578
616
  new Dependency("Microsoft.Bcl.AsyncInterfaces", "1.1.1", DependencyType.Unknown)
579
617
  };
580
618
 
581
- var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflictsNew(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger());
619
+ var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflicts(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger());
582
620
  Assert.NotNull(resolvedDependencies);
583
621
  Assert.Single(resolvedDependencies);
584
622
  Assert.Equal("Azure.Core", resolvedDependencies[0].Name);
@@ -621,7 +659,7 @@ public class MSBuildHelperTests : TestBase
621
659
  new Dependency("Newtonsoft.Json", "13.0.1", DependencyType.Unknown)
622
660
  };
623
661
 
624
- var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflictsNew(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger());
662
+ var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflicts(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger());
625
663
  Assert.NotNull(resolvedDependencies);
626
664
  Assert.Equal(2, resolvedDependencies.Length);
627
665
  Assert.Equal("Newtonsoft.Json.Bson", resolvedDependencies[0].Name);
@@ -671,7 +709,7 @@ public class MSBuildHelperTests : TestBase
671
709
  new Dependency("Microsoft.CodeAnalysis.Common", "4.10.0", DependencyType.PackageReference)
672
710
  };
673
711
 
674
- var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflictsNew(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger());
712
+ var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflicts(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger());
675
713
  Assert.NotNull(resolvedDependencies);
676
714
  Assert.Equal(3, resolvedDependencies.Length);
677
715
  Assert.Equal("Microsoft.CodeAnalysis.Compilers", resolvedDependencies[0].Name);
@@ -723,7 +761,7 @@ public class MSBuildHelperTests : TestBase
723
761
  new Dependency("Microsoft.CodeAnalysis.Common", "4.10.0", DependencyType.PackageReference)
724
762
  };
725
763
 
726
- var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflictsNew(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger());
764
+ var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflicts(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger());
727
765
  Assert.NotNull(resolvedDependencies);
728
766
  Assert.Equal(4, resolvedDependencies.Length);
729
767
  Assert.Equal("Microsoft.CodeAnalysis.Compilers", resolvedDependencies[0].Name);
@@ -779,7 +817,7 @@ public class MSBuildHelperTests : TestBase
779
817
  new Dependency("Newtonsoft.Json", "13.0.1", DependencyType.Unknown)
780
818
  };
781
819
 
782
- var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflictsNew(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger());
820
+ var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflicts(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger());
783
821
  Assert.NotNull(resolvedDependencies);
784
822
  Assert.Equal(5, resolvedDependencies.Length);
785
823
  Assert.Equal("Microsoft.CodeAnalysis.Compilers", resolvedDependencies[0].Name);
@@ -838,7 +876,7 @@ public class MSBuildHelperTests : TestBase
838
876
  new Dependency("Buildalyzer", "7.0.1", DependencyType.PackageReference),
839
877
  };
840
878
 
841
- var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflictsNew(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger());
879
+ var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflicts(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger());
842
880
  Assert.NotNull(resolvedDependencies);
843
881
  Assert.Equal(4, resolvedDependencies.Length);
844
882
  Assert.Equal("Buildalyzer", resolvedDependencies[0].Name);
@@ -895,7 +933,7 @@ public class MSBuildHelperTests : TestBase
895
933
  new Dependency("Azure.Core", "1.22.0", DependencyType.PackageReference)
896
934
  };
897
935
 
898
- var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflictsNew(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger());
936
+ var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflicts(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger());
899
937
  Assert.NotNull(resolvedDependencies);
900
938
  Assert.Equal(4, resolvedDependencies.Length);
901
939
  Assert.Equal("System.Collections.Immutable", resolvedDependencies[0].Name);
@@ -952,7 +990,7 @@ public class MSBuildHelperTests : TestBase
952
990
  new Dependency("Azure.Core", "1.22.0", DependencyType.PackageReference)
953
991
  };
954
992
 
955
- var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflictsNew(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger());
993
+ var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflicts(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger());
956
994
  Assert.NotNull(resolvedDependencies);
957
995
  Assert.Equal(5, resolvedDependencies.Length);
958
996
  Assert.Equal("System.Collections.Immutable", resolvedDependencies[0].Name);
@@ -1007,7 +1045,7 @@ public class MSBuildHelperTests : TestBase
1007
1045
  new Dependency("AutoMapper.Collection", "10.0.0", DependencyType.PackageReference)
1008
1046
  };
1009
1047
 
1010
- var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflictsNew(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger());
1048
+ var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflicts(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger());
1011
1049
  Assert.NotNull(resolvedDependencies);
1012
1050
  Assert.Equal(3, resolvedDependencies.Length);
1013
1051
  Assert.Equal("AutoMapper.Extensions.Microsoft.DependencyInjection", resolvedDependencies[0].Name);
@@ -1054,7 +1092,7 @@ public class MSBuildHelperTests : TestBase
1054
1092
  new Dependency("Microsoft.Extensions.Caching.Memory", "8.0.0", DependencyType.PackageReference)
1055
1093
  };
1056
1094
 
1057
- var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflictsNew(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger());
1095
+ var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflicts(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger());
1058
1096
  Assert.NotNull(resolvedDependencies);
1059
1097
  Assert.Equal(2, resolvedDependencies.Length);
1060
1098
  Assert.Equal("Microsoft.EntityFrameworkCore", resolvedDependencies[0].Name);
@@ -1104,7 +1142,7 @@ public class MSBuildHelperTests : TestBase
1104
1142
  new Dependency("Microsoft.EntityFrameworkCore.Analyzers", "8.0.0", DependencyType.PackageReference)
1105
1143
  };
1106
1144
 
1107
- var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflictsNew(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger());
1145
+ var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflicts(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger());
1108
1146
  Assert.NotNull(resolvedDependencies);
1109
1147
  Assert.Equal(4, resolvedDependencies.Length);
1110
1148
  Assert.Equal("Microsoft.EntityFrameworkCore.Design", resolvedDependencies[0].Name);
@@ -1156,7 +1194,7 @@ public class MSBuildHelperTests : TestBase
1156
1194
  new Dependency("Microsoft.EntityFrameworkCore.Analyzers", "8.0.0", DependencyType.PackageReference)
1157
1195
  };
1158
1196
 
1159
- var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflictsNew(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger());
1197
+ var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflicts(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger());
1160
1198
  Assert.NotNull(resolvedDependencies);
1161
1199
  Assert.Equal(3, resolvedDependencies.Length);
1162
1200
  Assert.Equal("Microsoft.EntityFrameworkCore.Design", resolvedDependencies[0].Name);
@@ -1208,7 +1246,7 @@ public class MSBuildHelperTests : TestBase
1208
1246
  new Dependency("System.Collections.Immutable", "8.0.0", DependencyType.PackageReference),
1209
1247
  };
1210
1248
 
1211
- var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflictsNew(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger());
1249
+ var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflicts(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger());
1212
1250
  Assert.NotNull(resolvedDependencies);
1213
1251
  Assert.Equal(4, resolvedDependencies.Length);
1214
1252
  Assert.Equal("System.Collections.Immutable", resolvedDependencies[0].Name);
@@ -1260,7 +1298,7 @@ public class MSBuildHelperTests : TestBase
1260
1298
  new Dependency("System.Collections.Immutable", "8.0.0", DependencyType.PackageReference),
1261
1299
  };
1262
1300
 
1263
- var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflictsNew(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger());
1301
+ var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflicts(repoRoot.FullName, projectPath, "net8.0", dependencies, update, new TestLogger());
1264
1302
  Assert.NotNull(resolvedDependencies);
1265
1303
  Assert.Equal(3, resolvedDependencies.Length);
1266
1304
  Assert.Equal("Microsoft.CodeAnalysis.CSharp.Workspaces", resolvedDependencies[0].Name);
@@ -121,15 +121,11 @@ module Dependabot
121
121
  def packages_config_files
122
122
  return @packages_config_files if @packages_config_files
123
123
 
124
- candidate_paths =
125
- [*project_files.map { |f| File.dirname(f.name) }, "."].uniq
124
+ imported_project_files = imported_property_files.filter { |f| f.name.match?(/\.(cs|vb|fs)proj$/) }
126
125
 
127
- @packages_config_files =
128
- candidate_paths.filter_map do |dir|
129
- file = repo_contents(dir: dir)
130
- .find { |f| f.name.casecmp("packages.config").zero? }
131
- fetch_file_from_host(File.join(dir, file.name)) if file
132
- end
126
+ @packages_config_files = [*project_files, *imported_project_files].filter_map do |f|
127
+ named_file_next_to_project_file(f, "packages.config")
128
+ end
133
129
  end
134
130
 
135
131
  sig { returns(T::Array[Dependabot::DependencyFile]) }
@@ -312,6 +308,32 @@ module Dependabot
312
308
  found_expected_file
313
309
  end
314
310
 
311
+ sig do
312
+ params(
313
+ project_file: Dependabot::DependencyFile,
314
+ expected_file_name: String
315
+ )
316
+ .returns(T.nilable(Dependabot::DependencyFile))
317
+ end
318
+ def named_file_next_to_project_file(project_file, expected_file_name)
319
+ found_expected_file = T.let(nil, T.nilable(Dependabot::DependencyFile))
320
+ directory_path = Pathname.new(directory)
321
+ full_project_dir = Pathname.new(project_file.directory).join(project_file.name).dirname
322
+
323
+ candidate_file_path = Pathname.new(full_project_dir).join(expected_file_name).cleanpath.to_path
324
+ candidate_directory = Pathname.new(File.dirname(candidate_file_path))
325
+ relative_candidate_directory = candidate_directory.relative_path_from(directory_path)
326
+ candidate_file = repo_contents(dir: relative_candidate_directory).find do |f|
327
+ f.name.casecmp?(expected_file_name)
328
+ end
329
+ if candidate_file
330
+ found_expected_file = fetch_file_from_host(File.join(relative_candidate_directory,
331
+ candidate_file.name))
332
+ end
333
+
334
+ found_expected_file
335
+ end
336
+
315
337
  sig { returns(T.nilable(Dependabot::DependencyFile)) }
316
338
  def global_json
317
339
  @global_json ||= T.let(fetch_file_if_present("global.json"), T.nilable(Dependabot::DependencyFile))
@@ -355,6 +377,7 @@ module Dependabot
355
377
  end
356
378
  def fetch_imported_property_files(file:, previously_fetched_files:)
357
379
  file_id = file.directory + "/" + file.name
380
+
358
381
  if @fetched_files[file_id]
359
382
  T.must(@fetched_files[file_id])
360
383
  else
@@ -363,23 +386,37 @@ module Dependabot
363
386
  ImportPathsFinder.new(project_file: file).project_reference_paths +
364
387
  ImportPathsFinder.new(project_file: file).project_file_paths
365
388
 
366
- paths.filter_map do |path|
389
+ # Initialize a set to hold fetched files temporarily to avoid duplicates
390
+ fetched_files_set = Set.new([file])
391
+
392
+ paths.each do |path|
367
393
  next if previously_fetched_files.map(&:name).include?(path)
368
394
  next if file.name == path
369
395
  next if path.include?("$(")
370
396
 
371
- fetched_file = fetch_file_from_host(path)
372
- grandchild_property_files = fetch_imported_property_files(
373
- file: fetched_file,
374
- previously_fetched_files: previously_fetched_files + [file]
375
- )
376
- @fetched_files[file_id] = [fetched_file, *grandchild_property_files]
377
- @fetched_files[file_id]
378
- rescue Dependabot::DependencyFileNotFound
379
- # Don't worry about missing files too much for now (at least
380
- # until we start resolving properties)
381
- nil
382
- end.flatten
397
+ begin
398
+ fetched_file = fetch_file_from_host(path)
399
+ grandchild_property_files = fetch_imported_property_files(
400
+ file: fetched_file,
401
+ previously_fetched_files: previously_fetched_files + [file]
402
+ )
403
+
404
+ # Add fetched file and grandchild property files to the set
405
+ fetched_files_set << fetched_file
406
+ fetched_files_set.merge(grandchild_property_files)
407
+ rescue Dependabot::DependencyFileNotFound
408
+ # Don't worry about missing files, just skip them for now
409
+ Dependabot.logger.info("unable to find expected file #{file.name}")
410
+ nil
411
+ end
412
+ end
413
+
414
+ # Convert the set to an array and cache the fetched files
415
+ fetched_files = fetched_files_set.to_a
416
+ @fetched_files[file_id] = fetched_files
417
+
418
+ # Return the fetched files
419
+ fetched_files
383
420
  end
384
421
  end
385
422
  end
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.281.0
4
+ version: 0.283.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dependabot
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-10-17 00:00:00.000000000 Z
11
+ date: 2024-10-31 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.281.0
19
+ version: 0.283.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.281.0
26
+ version: 0.283.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rubyzip
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -134,28 +134,28 @@ dependencies:
134
134
  requirements:
135
135
  - - "~>"
136
136
  - !ruby/object:Gem::Version
137
- version: 1.65.0
137
+ version: 1.67.0
138
138
  type: :development
139
139
  prerelease: false
140
140
  version_requirements: !ruby/object:Gem::Requirement
141
141
  requirements:
142
142
  - - "~>"
143
143
  - !ruby/object:Gem::Version
144
- version: 1.65.0
144
+ version: 1.67.0
145
145
  - !ruby/object:Gem::Dependency
146
146
  name: rubocop-performance
147
147
  requirement: !ruby/object:Gem::Requirement
148
148
  requirements:
149
149
  - - "~>"
150
150
  - !ruby/object:Gem::Version
151
- version: 1.21.0
151
+ version: 1.22.1
152
152
  type: :development
153
153
  prerelease: false
154
154
  version_requirements: !ruby/object:Gem::Requirement
155
155
  requirements:
156
156
  - - "~>"
157
157
  - !ruby/object:Gem::Version
158
- version: 1.21.0
158
+ version: 1.22.1
159
159
  - !ruby/object:Gem::Dependency
160
160
  name: rubocop-rspec
161
161
  requirement: !ruby/object:Gem::Requirement
@@ -310,6 +310,7 @@ files:
310
310
  - helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/RequirementTests.cs
311
311
  - helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/SecurityVulnerabilityExtensionsTests.cs
312
312
  - helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/VersionFinderTests.cs
313
+ - helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/DependencySolverEnvironment.cs
313
314
  - helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTestBase.cs
314
315
  - helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.DotNetToolsJson.cs
315
316
  - helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.GlobalJson.cs
@@ -344,8 +345,8 @@ files:
344
345
  - helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.DotNetTools.cs
345
346
  - helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.GlobalJson.cs
346
347
  - helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.Mixed.cs
348
+ - helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackageReference.cs
347
349
  - helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackagesConfig.cs
348
- - helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.Sdk.cs
349
350
  - helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/AssertEx.cs
350
351
  - helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/DiffUtil.cs
351
352
  - helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/JsonHelperTests.cs
@@ -425,8 +426,8 @@ files:
425
426
  - helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/DotNetToolsJsonUpdater.cs
426
427
  - helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/GlobalJsonUpdater.cs
427
428
  - helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/LockFileUpdater.cs
429
+ - helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackageReferenceUpdater.cs
428
430
  - helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackagesConfigUpdater.cs
429
- - helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/SdkPackageUpdater.cs
430
431
  - helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/UpdateOperationResult.cs
431
432
  - helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/UpdateResult.cs
432
433
  - helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/UpdaterWorker.cs
@@ -496,7 +497,7 @@ licenses:
496
497
  - MIT
497
498
  metadata:
498
499
  bug_tracker_uri: https://github.com/dependabot/dependabot-core/issues
499
- changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.281.0
500
+ changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.283.0
500
501
  post_install_message:
501
502
  rdoc_options: []
502
503
  require_paths: