dependabot-nuget 0.301.1 → 0.303.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.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/helpers/lib/NuGetUpdater/Directory.Packages.props +5 -5
  3. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/DependencyDiscovery.props +4 -1
  4. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/DependencyDiscovery.targets +19 -4
  5. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/DependencyDiscoveryTargetingPacks.props +10 -0
  6. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/DiscoveryWorker.cs +20 -17
  7. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/SdkProjectDiscovery.cs +179 -28
  8. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/ExperimentsManager.cs +3 -0
  9. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/FrameworkChecker/FrameworkCompatibilityService.cs +15 -6
  10. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/FrameworkChecker/SupportedFrameworks.cs +6 -4
  11. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/NuGetUpdater.Core.csproj +1 -0
  12. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/RunWorker.cs +8 -0
  13. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/BindingRedirectManager.cs +7 -4
  14. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/LockFileUpdater.cs +2 -0
  15. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackageReferenceUpdater.cs +257 -37
  16. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackagesConfigUpdater.cs +13 -4
  17. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/{WebApplicationTargetsConditionPatcher.cs → SpecialImportsConditionPatcher.cs} +18 -11
  18. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/UpdateOperationBase.cs +209 -0
  19. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/UpdateOperationResult.cs +3 -0
  20. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/UpdaterWorker.cs +79 -24
  21. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/XmlFilePreAndPostProcessor.cs +26 -11
  22. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +48 -22
  23. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/AnalyzeWorkerTests.cs +54 -0
  24. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.PackagesConfig.cs +68 -0
  25. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.Project.cs +94 -0
  26. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/FrameworkChecker/FrameworkCompatibilityServiceFacts.cs +1 -1
  27. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/RunWorkerTests.cs +24 -6
  28. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/PackageReferenceUpdaterTests.cs +177 -0
  29. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/SpecialFilePatcherTests.cs +99 -0
  30. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateOperationBaseTests.cs +130 -0
  31. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTestBase.cs +5 -0
  32. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.Mixed.cs +71 -5
  33. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackageReference.cs +125 -3
  34. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackagesConfig.cs +23 -0
  35. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/MSBuildHelperTests.cs +145 -147
  36. data/lib/dependabot/nuget/file_parser.rb +22 -19
  37. metadata +13 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 956d84d6354497a9a36a640e0851ae0c97b739f32d8a4fcedc670eb3dc9f2175
4
- data.tar.gz: bdabf60631928f0bdb355d92b8f12466764349c5e482b9da073679b733531be2
3
+ metadata.gz: c0d78d1b91ae626712aaadfdc2f4d03f8f15247b88b869a7285e0b4ad75a36c6
4
+ data.tar.gz: 6bb59e2674de36c8fae8ee8172db65e6c64447a9f951bd9717bc5915b1a491b3
5
5
  SHA512:
6
- metadata.gz: 677106b7e78e73b53bc3238a802e56028f18e33104b3ef7e15107abff684241bc87cfcf44f6c64470bf32ae3e0e3dded3c45f3799c422838239f06f9edd70b3a
7
- data.tar.gz: b906f03ce619ac2d45a907325e7b2a249976ef34056ef8e0d06db38f3c3f8a9a02be72c6e8210749cee1fcc03c4ca9afa9c0b5659838625ecd76bc00c121e19a
6
+ metadata.gz: 0d71151a04b9a0ad457713832a763169027b9e24dc95366ffad4fa0011c7820f458410b288d4b85b3382f28923369cf56a60aa0d1970e66c91096c49c857b8b9
7
+ data.tar.gz: c041d7724ecfbf3dd2cd43064ff566eae5526ee5fe73f5b51df153b093f75b5fbc3a0600bbbe4b250bb1919de3e779f233f9a6100d20bfcc1f0b67cbbb82a503
@@ -19,7 +19,7 @@
19
19
  <PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.12.0" />
20
20
  <PackageVersion Include="Microsoft.CSharp" Version="4.7.0" />
21
21
  <PackageVersion Include="Microsoft.Extensions.FileProviders.Abstractions" Version="9.0.0" />
22
- <PackageVersion Include="Microsoft.Extensions.FileSystemGlobbing" Version="9.0.0" />
22
+ <PackageVersion Include="Microsoft.Extensions.FileSystemGlobbing" Version="9.0.3" />
23
23
  <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
24
24
  <PackageVersion Include="Microsoft.VisualStudio.Setup.Configuration.Interop" Version="3.12.2149" />
25
25
  <PackageVersion Include="Microsoft.Web.Xdt" Version="3.1.0" />
@@ -28,16 +28,16 @@
28
28
  <PackageVersion Include="NuGet.Core" Version="2.14.0" Aliases="CoreV2" />
29
29
  <PackageVersion Include="Semver" Version="3.0.0" />
30
30
  <PackageVersion Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
31
- <PackageVersion Include="System.ComponentModel.Composition" Version="9.0.0" />
31
+ <PackageVersion Include="System.ComponentModel.Composition" Version="9.0.3" />
32
32
  <PackageVersion Include="System.Net.Http" Version="4.3.4" />
33
33
  <PackageVersion Include="System.Formats.Asn1" Version="8.0.1" />
34
34
  <PackageVersion Include="System.Security.Cryptography.Pkcs" Version="9.0.0" />
35
35
  <PackageVersion Include="System.Security.Cryptography.ProtectedData" Version="9.0.0" />
36
- <PackageVersion Include="System.Text.Json" Version="8.0.4" />
36
+ <PackageVersion Include="System.Text.Json" Version="9.0.3" />
37
37
  <PackageVersion Include="System.Text.RegularExpressions" Version="4.3.1" />
38
- <PackageVersion Include="System.Threading.Tasks.Dataflow" Version="9.0.0" />
38
+ <PackageVersion Include="System.Threading.Tasks.Dataflow" Version="9.0.3" />
39
39
  <PackageVersion Include="xunit" Version="2.9.3" />
40
- <PackageVersion Include="xunit.runner.visualstudio" Version="3.0.0" />
40
+ <PackageVersion Include="xunit.runner.visualstudio" Version="3.0.2" />
41
41
  </ItemGroup>
42
42
 
43
43
  </Project>
@@ -10,7 +10,10 @@
10
10
  -->
11
11
  <_DefaultTargetPlatformVersion Condition="'$(_DefaultTargetPlatformVersion)' == ''">0.0</_DefaultTargetPlatformVersion>
12
12
  <DesignTimeBuild>true</DesignTimeBuild>
13
+ <GenerateDependencyFile>true</GenerateDependencyFile>
14
+ <NuGetInteractive>false</NuGetInteractive>
15
+ <RunAnalyzers>false</RunAnalyzers>
13
16
  <EnableWindowsTargeting Condition="$(TargetFramework.Contains('-windows'))">true</EnableWindowsTargeting>
14
- <TargetPlatformVersion Condition="$(TargetPlatformVersion) == '' AND $(TargetFramework.Contains('-'))">$(_DefaultTargetPlatformVersion)</TargetPlatformVersion>
17
+ <TargetPlatformVersion Condition="$(TargetFramework.Contains('-')) AND '$(TargetPlatformVersion)' == ''">$(_DefaultTargetPlatformVersion)</TargetPlatformVersion>
15
18
  </PropertyGroup>
16
19
  </Project>
@@ -1,15 +1,30 @@
1
1
  <Project>
2
2
  <PropertyGroup>
3
- <!-- Dependency discovery requires a non-zero value for $(TargetPlatformVersion) -->
3
+ <!-- Dependency discovery requires a custom value for $(TargetPlatformVersion). Minimum version of unspecified Windows targeting is 7.0. -->
4
4
  <_DefaultTargetPlatformVersion>1.0</_DefaultTargetPlatformVersion>
5
+ <_DefaultTargetPlatformVersion Condition="$(TargetFramework.ToLowerInvariant().EndsWith('-windows'))">7.0</_DefaultTargetPlatformVersion>
6
+ <_DiscoverDependenciesDependsOn>ResolveAssemblyReferences</_DiscoverDependenciesDependsOn>
7
+
8
+ <!-- only SDK-style projects set `$(NETCoreSdkVersion)`; legacy projects don't have it -->
9
+ <_IsLegacyStyleProject Condition="'$(NETCoreSdkVersion)' == ''">true</_IsLegacyStyleProject>
10
+
11
+ <!-- These targets only exists for SDK-style projects -->
12
+ <_DiscoverDependenciesDependsOn Condition="'$(_IsLegacyStyleProject)' != 'true'">$(_DiscoverDependenciesDependsOn);GenerateBuildDependencyFile;ResolvePackageAssets</_DiscoverDependenciesDependsOn>
13
+
14
+ <!-- For non-SDK-style projects, use a different target defined below -->
15
+ <_DiscoverDependenciesDependsOn Condition="'$(_IsLegacyStyleProject)' == 'true'">$(_DiscoverDependenciesDependsOn);_DependencyDiscovery_LegacyProjects</_DiscoverDependenciesDependsOn>
5
16
  </PropertyGroup>
6
17
 
7
18
  <Import Project="DependencyDiscovery.props" />
8
19
 
9
- <Target Name="_DiscoverDependencies" DependsOnTargets="ResolveAssemblyReferences;GenerateBuildDependencyFile;ResolvePackageAssets">
20
+ <Target Name="_DiscoverDependencies" DependsOnTargets="$(_DiscoverDependenciesDependsOn)">
10
21
  <!--
11
- The targets ResolveAssemblyReferences and GenerateBuildDependencyFile are sufficient for projects targeting .NET Standard or .NET Core.
12
- The target ResolvePackageAssets is necessary for projects targeting .NET Framework.
22
+ This is purely a place to collect the `DependsOnTargets` attribute.
13
23
  -->
14
24
  </Target>
25
+
26
+ <Target Name="_DependencyDiscovery_LegacyProjects" Condition="'@(PackageReference)' != ''">
27
+ <!-- pseudo-sdk-style project; legacy, but with PackageReference elements - dependencies need to be resolved differently; output a sentinel value to notify the dependency discovery -->
28
+ <Message Text="_DependencyDiscovery_LegacyProjects::UseTemporaryProject" Importance="High" />
29
+ </Target>
15
30
  </Project>
@@ -0,0 +1,10 @@
1
+ <Project>
2
+ <PropertyGroup>
3
+ <!--
4
+ Suppress errors like:
5
+ error MSB3644: The reference assemblies for .NETFramework,Version=v4.7.2 were not found.
6
+ because that's irrelevant to dependency discovery.
7
+ -->
8
+ <NoWarn>$(NoWarn);MSB3644</NoWarn>
9
+ </PropertyGroup>
10
+ </Project>
@@ -10,6 +10,7 @@ using Microsoft.Build.Exceptions;
10
10
  using NuGet.Frameworks;
11
11
 
12
12
  using NuGetUpdater.Core.Run.ApiModel;
13
+ using NuGetUpdater.Core.Updater;
13
14
  using NuGetUpdater.Core.Utilities;
14
15
 
15
16
  namespace NuGetUpdater.Core.Discover;
@@ -303,30 +304,24 @@ public partial class DiscoveryWorker : IDiscoveryWorker
303
304
 
304
305
  private async Task<ImmutableArray<ProjectDiscoveryResult>> RunForProjectPathsAsync(string repoRootPath, string workspacePath, IEnumerable<string> projectPaths)
305
306
  {
307
+ var normalizedProjectPaths = projectPaths.SelectMany(p => PathHelper.ResolveCaseInsensitivePathsInsideRepoRoot(p, repoRootPath) ?? []).Distinct().ToImmutableArray();
308
+ var disposables = normalizedProjectPaths.Select(p => new SpecialImportsConditionPatcher(p)).ToImmutableArray();
306
309
  var results = new Dictionary<string, ProjectDiscoveryResult>(StringComparer.Ordinal);
307
- foreach (var projectPath in projectPaths)
308
- {
309
- // If there is some MSBuild logic that needs to run to fully resolve the path skip the project
310
- // Ensure file existence is checked case-insensitively
311
- var actualProjectPaths = PathHelper.ResolveCaseInsensitivePathsInsideRepoRoot(projectPath, repoRootPath);
312
310
 
313
- if (actualProjectPaths == null)
314
- {
315
- continue;
316
- }
317
-
318
- foreach (var actualProjectPath in actualProjectPaths)
311
+ try
312
+ {
313
+ foreach (var projectPath in normalizedProjectPaths)
319
314
  {
320
- if (_processedProjectPaths.Contains(actualProjectPath))
315
+ if (_processedProjectPaths.Contains(projectPath))
321
316
  {
322
317
  continue;
323
318
  }
324
319
 
325
- _processedProjectPaths.Add(actualProjectPath);
320
+ _processedProjectPaths.Add(projectPath);
326
321
 
327
- var relativeProjectPath = Path.GetRelativePath(workspacePath, actualProjectPath).NormalizePathToUnix();
328
- var packagesConfigResult = await PackagesConfigDiscovery.Discover(repoRootPath, workspacePath, actualProjectPath, _experimentsManager, _logger);
329
- var projectResults = await SdkProjectDiscovery.DiscoverAsync(repoRootPath, workspacePath, actualProjectPath, _experimentsManager, _logger);
322
+ var relativeProjectPath = Path.GetRelativePath(workspacePath, projectPath).NormalizePathToUnix();
323
+ var packagesConfigResult = await PackagesConfigDiscovery.Discover(repoRootPath, workspacePath, projectPath, _experimentsManager, _logger);
324
+ var projectResults = await SdkProjectDiscovery.DiscoverAsync(repoRootPath, workspacePath, projectPath, _experimentsManager, _logger);
330
325
 
331
326
  // Determine if there were unrestored MSBuildSdks
332
327
  var msbuildSdks = projectResults.SelectMany(p => p.Dependencies.Where(d => d.Type == DependencyType.MSBuildSdk)).ToImmutableArray();
@@ -335,7 +330,7 @@ public partial class DiscoveryWorker : IDiscoveryWorker
335
330
  // If new SDKs were restored, then we need to rerun SdkProjectDiscovery.
336
331
  if (await TryRestoreMSBuildSdksAsync(repoRootPath, workspacePath, msbuildSdks, _logger))
337
332
  {
338
- projectResults = await SdkProjectDiscovery.DiscoverAsync(repoRootPath, workspacePath, actualProjectPath, _experimentsManager, _logger);
333
+ projectResults = await SdkProjectDiscovery.DiscoverAsync(repoRootPath, workspacePath, projectPath, _experimentsManager, _logger);
339
334
  }
340
335
  }
341
336
 
@@ -423,6 +418,14 @@ public partial class DiscoveryWorker : IDiscoveryWorker
423
418
  }
424
419
  }
425
420
  }
421
+ finally
422
+ {
423
+ foreach (var disposable in disposables)
424
+ {
425
+ // restore the original project file
426
+ disposable.Dispose();
427
+ }
428
+ }
426
429
 
427
430
  return [.. results.Values];
428
431
  }
@@ -1,5 +1,4 @@
1
1
  using System.Collections.Immutable;
2
- using System.Reflection;
3
2
  using System.Xml.Linq;
4
3
  using System.Xml.XPath;
5
4
 
@@ -22,6 +21,11 @@ internal static class SdkProjectDiscovery
22
21
  "PackageReference"
23
22
  };
24
23
 
24
+ private static readonly HashSet<string> PackageVersionItemNames = new HashSet<string>(StringComparer.Ordinal)
25
+ {
26
+ "PackageVersion"
27
+ };
28
+
25
29
  // the items listed below represent collection names that NuGet will resolve a package into, along with the metadata value names to get the package name and version
26
30
  private static readonly Dictionary<string, (string NameMetadata, string VersionMetadata)> ResolvedPackageItemNames = new Dictionary<string, (string, string)>(StringComparer.OrdinalIgnoreCase)
27
31
  {
@@ -69,26 +73,30 @@ internal static class SdkProjectDiscovery
69
73
  // the following collection feature heavily; the shape is described as follows
70
74
 
71
75
  Dictionary<string, Dictionary<string, Dictionary<string, string>>> packagesPerProject = new(PathComparer.Instance);
72
- // projectPath tfm packageName, packageVersion
76
+ // projectPath tfm packageName packageVersion
73
77
 
74
- Dictionary<string, HashSet<string>> topLevelPackagesPerProject = new(PathComparer.Instance);
75
- // projectPath, packageNames
78
+ Dictionary<string, Dictionary<string, HashSet<string>>> topLevelPackagesPerProject = new(PathComparer.Instance);
79
+ // projectPath tfm, packageNames
80
+
81
+ Dictionary<string, Dictionary<string, Dictionary<string, string>>> explicitPackageVersionsPerProject = new(PathComparer.Instance);
82
+ // projectPath, tfm, packageName, packageVersion
76
83
 
77
84
  Dictionary<string, Dictionary<string, Dictionary<string, string>>> packagesReplacedBySdkPerProject = new(PathComparer.Instance);
78
- // projectPath tfm packageName, packageVersion
85
+ // projectPath tfm packageName packageVersion
79
86
 
80
87
  Dictionary<string, Dictionary<string, string>> resolvedProperties = new(PathComparer.Instance);
81
- // projectPath propertyName, propertyValue
88
+ // projectPath propertyName propertyValue
82
89
 
83
90
  Dictionary<string, HashSet<string>> importedFiles = new(PathComparer.Instance);
84
- // projectPath, importedFiles
91
+ // projectPath importedFiles
85
92
 
86
93
  Dictionary<string, HashSet<string>> referencedProjects = new(PathComparer.Instance);
87
- // projectPath, referencedProjects
94
+ // projectPath referencedProjects
88
95
 
89
96
  Dictionary<string, HashSet<string>> additionalFiles = new(PathComparer.Instance);
90
- // projectPath, additionalFiles
97
+ // projectPath additionalFiles
91
98
 
99
+ var requiresManualPackageResolution = false;
92
100
  var tfms = await MSBuildHelper.GetTargetFrameworkValuesFromProject(repoRootPath, startingProjectPath, experimentsManager, logger);
93
101
  foreach (var tfm in tfms)
94
102
  {
@@ -100,13 +108,15 @@ internal static class SdkProjectDiscovery
100
108
  var (exitCode, stdOut, stdErr) = await MSBuildHelper.HandleGlobalJsonAsync(startingProjectDirectory, repoRootPath, experimentsManager, async () =>
101
109
  {
102
110
  // the built-in target `GenerateBuildDependencyFile` forces resolution of all NuGet packages, but doesn't invoke a full build
103
- var dependencyDiscoveryTargetsPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!, "DependencyDiscovery.targets");
111
+ var dependencyDiscoveryTargetingPacksPropsPath = MSBuildHelper.GetFileFromRuntimeDirectory("DependencyDiscoveryTargetingPacks.props");
112
+ var dependencyDiscoveryTargetsPath = MSBuildHelper.GetFileFromRuntimeDirectory("DependencyDiscovery.targets");
104
113
  var args = new List<string>()
105
114
  {
106
115
  "build",
107
116
  startingProjectPath,
108
117
  "/t:_DiscoverDependencies",
109
118
  $"/p:TargetFramework={tfm}",
119
+ $"/p:CustomBeforeMicrosoftCommonProps={dependencyDiscoveryTargetingPacksPropsPath}",
110
120
  $"/p:CustomAfterMicrosoftCommonCrossTargetingTargets={dependencyDiscoveryTargetsPath}",
111
121
  $"/p:CustomAfterMicrosoftCommonTargets={dependencyDiscoveryTargetsPath}",
112
122
  "/p:TreatWarningsAsErrors=false", // if using CPM and a project also sets TreatWarningsAsErrors to true, this can cause discovery to fail; explicitly don't allow that
@@ -127,10 +137,10 @@ internal static class SdkProjectDiscovery
127
137
  return (exitCode, stdOut, stdErr);
128
138
  }, logger, retainMSBuildSdks: true);
129
139
  MSBuildHelper.ThrowOnError(stdOut);
130
- if (stdOut.Contains("""error MSB4057: The target "GenerateBuildDependencyFile" does not exist in the project."""))
140
+ if (stdOut.Contains("_DependencyDiscovery_LegacyProjects::UseTemporaryProject"))
131
141
  {
132
- // this can happen if it's a non-SDK-style project; totally normal, not worth examining the binlog
133
- return [];
142
+ // special case - legacy project with <PackageReference> elements; this requires extra handling below
143
+ requiresManualPackageResolution = true;
134
144
  }
135
145
  if (exitCode != 0)
136
146
  {
@@ -181,7 +191,7 @@ internal static class SdkProjectDiscovery
181
191
  }
182
192
  break;
183
193
  case NamedNode namedNode when namedNode is AddItem or RemoveItem:
184
- ProcessResolvedPackageReference(namedNode, packagesPerProject, topLevelPackagesPerProject, experimentsManager);
194
+ ProcessResolvedPackageReference(namedNode, packagesPerProject, topLevelPackagesPerProject, explicitPackageVersionsPerProject, experimentsManager);
185
195
 
186
196
  if (namedNode is AddItem addItem)
187
197
  {
@@ -304,8 +314,52 @@ internal static class SdkProjectDiscovery
304
314
  }
305
315
  }
306
316
 
317
+ if (requiresManualPackageResolution)
318
+ {
319
+ // we were able to collect all <PackageReference> elements, but no transitive dependencies were resolved
320
+ // to do this we create a temporary project with all of the top-level project elements, resolve _again_, then rebuild the proper result
321
+ packagesPerProject = await RebuildPackagesPerProject(
322
+ repoRootPath,
323
+ startingProjectPath,
324
+ tfms,
325
+ packagesPerProject,
326
+ explicitPackageVersionsPerProject,
327
+ experimentsManager,
328
+ logger
329
+ );
330
+ }
331
+
307
332
  // and done
308
- var projectDiscoveryResults = packagesPerProject.Keys.OrderBy(p => p).Select(projectPath =>
333
+ var projectDiscoveryResults = BuildResults(
334
+ repoRootPath,
335
+ workspacePath,
336
+ packagesPerProject,
337
+ explicitPackageVersionsPerProject,
338
+ packagesReplacedBySdkPerProject,
339
+ topLevelPackagesPerProject,
340
+ resolvedProperties,
341
+ referencedProjects,
342
+ importedFiles,
343
+ additionalFiles
344
+ );
345
+ return projectDiscoveryResults;
346
+ }
347
+
348
+ private static ImmutableArray<ProjectDiscoveryResult> BuildResults(
349
+ string repoRootPath,
350
+ string workspacePath,
351
+ Dictionary<string, Dictionary<string, Dictionary<string, string>>> packagesPerProject,
352
+ Dictionary<string, Dictionary<string, Dictionary<string, string>>> packageVersionsPerProject,
353
+ Dictionary<string, Dictionary<string, Dictionary<string, string>>> packagesReplacedBySdkPerProject,
354
+ Dictionary<string, Dictionary<string, HashSet<string>>> topLevelPackagesPerProject,
355
+ Dictionary<string, Dictionary<string, string>> resolvedProperties,
356
+ Dictionary<string, HashSet<string>> referencedProjects,
357
+ Dictionary<string, HashSet<string>> importedFiles,
358
+ Dictionary<string, HashSet<string>> additionalFiles
359
+ )
360
+ {
361
+ var projectDiscoveryResults = new List<ProjectDiscoveryResult>();
362
+ foreach (var projectPath in packagesPerProject.Keys.OrderBy(p => p)) //packagesPerProject.Keys.OrderBy(p => p).Select(projectPath =>
309
363
  {
310
364
  // gather some project-level information
311
365
  var packagesByTfm = packagesPerProject[projectPath];
@@ -341,7 +395,10 @@ internal static class SdkProjectDiscovery
341
395
  var localPropertyDefinitionElements = doc.Root!.XPathSelectElements("/Project/PropertyGroup/*");
342
396
  var projectPropertyNames = localPropertyDefinitionElements.Select(e => e.Name.LocalName).ToHashSet(StringComparer.OrdinalIgnoreCase);
343
397
  var projectRelativePath = Path.GetRelativePath(workspacePath, projectPath);
344
- var topLevelPackageNames = topLevelPackagesPerProject.GetOrAdd(projectPath, () => new(StringComparer.OrdinalIgnoreCase));
398
+ var topLevelPackageNames = topLevelPackagesPerProject
399
+ .GetOrAdd(projectPath, () => new(StringComparer.OrdinalIgnoreCase))
400
+ .SelectMany(kvp => kvp.Value)
401
+ .ToHashSet(StringComparer.OrdinalIgnoreCase);
345
402
 
346
403
  // create dependencies
347
404
  var tfms = packagesByTfm.Keys.OrderBy(tfm => tfm).ToImmutableArray();
@@ -379,7 +436,7 @@ internal static class SdkProjectDiscovery
379
436
  .OrderBy(p => p)
380
437
  .ToImmutableArray();
381
438
 
382
- return new ProjectDiscoveryResult()
439
+ var projectDiscoveryResult = new ProjectDiscoveryResult()
383
440
  {
384
441
  FilePath = projectRelativePath,
385
442
  Dependencies = dependencies,
@@ -389,14 +446,65 @@ internal static class SdkProjectDiscovery
389
446
  ImportedFiles = imported,
390
447
  AdditionalFiles = additional,
391
448
  };
392
- }).ToImmutableArray();
393
- return projectDiscoveryResults;
449
+ projectDiscoveryResults.Add(projectDiscoveryResult);
450
+ }
451
+ return projectDiscoveryResults.ToImmutableArray();
452
+ }
453
+
454
+ private static async Task<Dictionary<string, Dictionary<string, Dictionary<string, string>>>> RebuildPackagesPerProject(
455
+ string repoRootPath,
456
+ string projectPath,
457
+ ImmutableArray<string> targetFrameworks,
458
+ Dictionary<string, Dictionary<string, Dictionary<string, string>>> packagesPerProject,
459
+ Dictionary<string, Dictionary<string, Dictionary<string, string>>> explicitPackageVersionsPerProject,
460
+ ExperimentsManager experimentsManager,
461
+ ILogger logger
462
+ )
463
+ {
464
+ var tempDirectory = Directory.CreateTempSubdirectory("legacy-package-reference-resolution_");
465
+ try
466
+ {
467
+ // gather top level dependencies from topLevelPackagesPerProject
468
+ // TODO: we don't currently partition dependencies by TFM; this will have to be redone when that's supported
469
+ var topLevelDependencies = explicitPackageVersionsPerProject
470
+ .GetOrAdd(projectPath, () => new(StringComparer.OrdinalIgnoreCase))
471
+ .SelectMany(kvp => kvp.Value)
472
+ .Select(kvp => new Dependency(kvp.Key, kvp.Value, DependencyType.PackageReference, TargetFrameworks: targetFrameworks))
473
+ .ToImmutableArray();
474
+
475
+ var tempProjectPath = await MSBuildHelper.CreateTempProjectAsync(tempDirectory, repoRootPath, projectPath, targetFrameworks, topLevelDependencies, experimentsManager, logger);
476
+ var tempProjectDirectory = Path.GetDirectoryName(tempProjectPath)!;
477
+ var rediscoveredDependencies = await DiscoverWithBinLogAsync(tempProjectDirectory, tempProjectDirectory, tempProjectPath, experimentsManager, logger);
478
+ var rediscoveredDependenciesForThisProject = rediscoveredDependencies.Single(); // we started with a single temp project, this will be the only result
479
+
480
+ // re-build packagesPerProject
481
+ var rebuiltPackagesPerProject = packagesPerProject.ToDictionary(PathComparer.Instance); // shallow copy
482
+ rebuiltPackagesPerProject[projectPath] = new(StringComparer.OrdinalIgnoreCase); // rebuild for this project
483
+ var rebuiltPackagesForThisProject = rebuiltPackagesPerProject[projectPath];
484
+ foreach (var tfm in targetFrameworks)
485
+ {
486
+ rebuiltPackagesForThisProject[tfm] = rediscoveredDependenciesForThisProject.Dependencies.ToDictionary(d => d.Name, d => d.Version!, StringComparer.OrdinalIgnoreCase);
487
+ }
488
+
489
+ return rebuiltPackagesPerProject;
490
+ }
491
+ finally
492
+ {
493
+ try
494
+ {
495
+ tempDirectory.Delete(recursive: true);
496
+ }
497
+ catch
498
+ {
499
+ }
500
+ }
394
501
  }
395
502
 
396
503
  private static void ProcessResolvedPackageReference(
397
504
  NamedNode node,
398
505
  Dictionary<string, Dictionary<string, Dictionary<string, string>>> packagesPerProject, // projectPath -> tfm -> (packageName, packageVersion)
399
- Dictionary<string, HashSet<string>> topLevelPackagesPerProject,
506
+ Dictionary<string, Dictionary<string, HashSet<string>>> topLevelPackagesPerProject, // projectPath -> tfm -> packageName
507
+ Dictionary<string, Dictionary<string, Dictionary<string, string>>> packageVersionsPerProject, // projectPath -> tfm -> (packageName, packageVersion)
400
508
  ExperimentsManager experimentsManager
401
509
  )
402
510
  {
@@ -416,16 +524,28 @@ internal static class SdkProjectDiscovery
416
524
  continue;
417
525
  }
418
526
 
419
- var topLevelPackages = topLevelPackagesPerProject.GetOrAdd(projectEvaluation.ProjectFile, () => new(StringComparer.OrdinalIgnoreCase));
420
-
421
- if (doRemoveOperation)
527
+ var tfm = GetPropertyValueFromProjectEvaluation(projectEvaluation, "TargetFramework");
528
+ if (tfm is not null)
422
529
  {
423
- topLevelPackages.Remove(packageName);
424
- }
530
+ var topLevelPackages = topLevelPackagesPerProject.GetOrAdd(projectEvaluation.ProjectFile, () => new(StringComparer.OrdinalIgnoreCase));
531
+ var topLevelPackagesPerTfm = topLevelPackages.GetOrAdd(tfm, () => new(StringComparer.OrdinalIgnoreCase));
425
532
 
426
- if (doAddOperation)
427
- {
428
- topLevelPackages.Add(packageName);
533
+ if (doRemoveOperation)
534
+ {
535
+ topLevelPackagesPerTfm.Remove(packageName);
536
+ }
537
+
538
+ if (doAddOperation)
539
+ {
540
+ topLevelPackagesPerTfm.Add(packageName);
541
+ var packageVersion = GetChildMetadataValue(child, "Version");
542
+ if (packageVersion is not null)
543
+ {
544
+ var packagesPerTfm = packageVersionsPerProject.GetOrAdd(projectEvaluation.ProjectFile, () => new(StringComparer.OrdinalIgnoreCase));
545
+ var packageVersions = packagesPerTfm.GetOrAdd(tfm, () => new(StringComparer.OrdinalIgnoreCase));
546
+ packageVersions[packageName] = packageVersion;
547
+ }
548
+ }
429
549
  }
430
550
  }
431
551
  }
@@ -469,6 +589,37 @@ internal static class SdkProjectDiscovery
469
589
  }
470
590
  }
471
591
  }
592
+ else if (PackageVersionItemNames.Contains(node.Name))
593
+ {
594
+ foreach (var child in node.Children.OfType<Item>())
595
+ {
596
+ var projectEvaluation = GetNearestProjectEvaluation(node);
597
+ if (projectEvaluation is not null)
598
+ {
599
+ var tfm = GetPropertyValueFromProjectEvaluation(projectEvaluation, "TargetFramework");
600
+ if (tfm is not null)
601
+ {
602
+ var packageName = child.Name;
603
+ var packageVersions = packageVersionsPerProject.GetOrAdd(projectEvaluation.ProjectFile, () => new(StringComparer.OrdinalIgnoreCase));
604
+ var packageVersionsPerTfm = packageVersions.GetOrAdd(tfm, () => new(StringComparer.OrdinalIgnoreCase));
605
+
606
+ if (doRemoveOperation)
607
+ {
608
+ packageVersionsPerTfm.Remove(packageName);
609
+ }
610
+
611
+ if (doAddOperation)
612
+ {
613
+ var packageVersion = GetChildMetadataValue(child, "Version");
614
+ if (packageVersion is not null)
615
+ {
616
+ packageVersionsPerTfm[packageName] = packageVersion;
617
+ }
618
+ }
619
+ }
620
+ }
621
+ }
622
+ }
472
623
  }
473
624
 
474
625
  private static string? GetChildMetadataValue(TreeNode node, string metadataItemName)
@@ -8,6 +8,7 @@ namespace NuGetUpdater.Core;
8
8
  public record ExperimentsManager
9
9
  {
10
10
  public bool InstallDotnetSdks { get; init; } = false;
11
+ public bool NativeUpdater { get; init; } = false;
11
12
  public bool UseLegacyDependencySolver { get; init; } = false;
12
13
  public bool UseDirectDiscovery { get; init; } = false;
13
14
 
@@ -16,6 +17,7 @@ public record ExperimentsManager
16
17
  return new()
17
18
  {
18
19
  ["nuget_install_dotnet_sdks"] = InstallDotnetSdks,
20
+ ["nuget_native_updater"] = NativeUpdater,
19
21
  ["nuget_legacy_dependency_solver"] = UseLegacyDependencySolver,
20
22
  ["nuget_use_direct_discovery"] = UseDirectDiscovery,
21
23
  };
@@ -26,6 +28,7 @@ public record ExperimentsManager
26
28
  return new ExperimentsManager()
27
29
  {
28
30
  InstallDotnetSdks = IsEnabled(experiments, "nuget_install_dotnet_sdks"),
31
+ NativeUpdater = IsEnabled(experiments, "nuget_native_updater"),
29
32
  UseLegacyDependencySolver = IsEnabled(experiments, "nuget_legacy_dependency_solver"),
30
33
  UseDirectDiscovery = IsEnabled(experiments, "nuget_use_direct_discovery"),
31
34
  };
@@ -61,14 +61,23 @@ public class FrameworkCompatibilityService
61
61
  }
62
62
  }
63
63
 
64
- matrix.Add(
65
- SupportedFrameworks.Net60Windows7,
66
- new HashSet<NuGetFramework>
64
+ // e.g., explicitly allow a project targeting `net9.0-windows` to consume packages targeting `net9.0-windows7.0`
65
+ foreach (var packageFramework in SupportedFrameworks.TfmFilters.NetTfms)
66
+ {
67
+ if (packageFramework.Version.Major <= 5)
68
+ {
69
+ // the TFM `net5.0-windows7.0` isn't valid
70
+ continue;
71
+ }
72
+
73
+ var packageFrameworkWithWindowsVersion = new NuGetFramework(packageFramework.Framework, packageFramework.Version, "windows", FrameworkConstants.Version7);
74
+ var compatibleVersions = SupportedFrameworks.TfmFilters.NetTfms.Where(t => t.Version.Major >= packageFrameworkWithWindowsVersion.Version.Major).ToArray();
75
+ foreach (var compatibleVersion in compatibleVersions)
67
76
  {
68
- SupportedFrameworks.Net60Windows, SupportedFrameworks.Net60Windows7,
69
- SupportedFrameworks.Net70Windows, SupportedFrameworks.Net70Windows7
77
+ var compatibleWindowsTargetWithoutVersion = new NuGetFramework(compatibleVersion.Framework, compatibleVersion.Version, "windows", FrameworkConstants.EmptyVersion);
78
+ matrix[packageFrameworkWithWindowsVersion].Add(compatibleWindowsTargetWithoutVersion);
70
79
  }
71
- );
80
+ }
72
81
 
73
82
  return matrix;
74
83
  }
@@ -72,6 +72,8 @@ namespace NuGetGallery.Frameworks
72
72
 
73
73
  public static readonly NuGetFramework Net60Windows7 = new NuGetFramework(FrameworkIdentifiers.NetCoreApp, Version6, "windows", Version7);
74
74
  public static readonly NuGetFramework Net70Windows7 = new NuGetFramework(FrameworkIdentifiers.NetCoreApp, Version7, "windows", Version7);
75
+ public static readonly NuGetFramework Net80Windows7 = new NuGetFramework(FrameworkIdentifiers.NetCoreApp, Version8, "windows", Version7);
76
+ public static readonly NuGetFramework Net90Windows7 = new NuGetFramework(FrameworkIdentifiers.NetCoreApp, Version9, "windows", Version7);
75
77
 
76
78
  public static readonly IReadOnlyList<NuGetFramework> AllSupportedNuGetFrameworks;
77
79
 
@@ -83,10 +85,10 @@ namespace NuGetGallery.Frameworks
83
85
  Native,
84
86
  Net11, Net2, Net35, Net4, Net403, Net45, Net451, Net452, Net46, Net461, Net462, Net463, Net47, Net471, Net472, Net48, Net481,
85
87
  Net50, Net50Windows,
86
- Net60, Net60Android, Net60Ios, Net60MacCatalyst, Net60MacOs, Net60TvOs, Net60Windows,
87
- Net70, Net70Android, Net70Ios, Net70MacCatalyst, Net70MacOs, Net70TvOs, Net70Windows,
88
- Net80, Net80Android, Net80Ios, Net80MacCatalyst, Net80MacOs, Net80TvOs, Net80Windows,
89
- Net90, Net90Android, Net90Ios, Net90MacCatalyst, Net90MacOs, Net90TvOs, Net90Windows,
88
+ Net60, Net60Android, Net60Ios, Net60MacCatalyst, Net60MacOs, Net60TvOs, Net60Windows, Net60Windows7,
89
+ Net70, Net70Android, Net70Ios, Net70MacCatalyst, Net70MacOs, Net70TvOs, Net70Windows, Net70Windows7,
90
+ Net80, Net80Android, Net80Ios, Net80MacCatalyst, Net80MacOs, Net80TvOs, Net80Windows, Net80Windows7,
91
+ Net90, Net90Android, Net90Ios, Net90MacCatalyst, Net90MacOs, Net90TvOs, Net90Windows, Net90Windows7,
90
92
  NetCore, NetCore45, NetCore451,
91
93
  NetCoreApp10, NetCoreApp11, NetCoreApp20, NetCoreApp21, NetCoreApp22, NetCoreApp30, NetCoreApp31,
92
94
  NetMf,
@@ -9,6 +9,7 @@
9
9
 
10
10
  <ItemGroup>
11
11
  <None Include="DependencyDiscovery.props" CopyToOutputDirectory="PreserveNewest" />
12
+ <None Include="DependencyDiscoveryTargetingPacks.props" CopyToOutputDirectory="PreserveNewest" />
12
13
  <None Include="DependencyDiscovery.targets" CopyToOutputDirectory="PreserveNewest" />
13
14
  <None Include="TargetFrameworkReporter.targets" CopyToOutputDirectory="PreserveNewest" />
14
15
  </ItemGroup>
@@ -10,6 +10,7 @@ using NuGet.Versioning;
10
10
  using NuGetUpdater.Core.Analyze;
11
11
  using NuGetUpdater.Core.Discover;
12
12
  using NuGetUpdater.Core.Run.ApiModel;
13
+ using NuGetUpdater.Core.Updater;
13
14
  using NuGetUpdater.Core.Utilities;
14
15
 
15
16
  using static NuGetUpdater.Core.Utilities.EOLHandling;
@@ -158,6 +159,7 @@ public class RunWorker
158
159
  }
159
160
 
160
161
  // do update
162
+ var updateOperationsPerformed = new List<UpdateOperationBase>();
161
163
  var existingPullRequests = job.GetAllExistingPullRequests();
162
164
  var unhandledPullRequestDependenciesSet = existingPullRequests
163
165
  .Select(pr => pr.Item2.Select(d => d.DependencyName).ToHashSet(StringComparer.OrdinalIgnoreCase))
@@ -240,6 +242,8 @@ public class RunWorker
240
242
  {
241
243
  actualUpdatedDependencies.Add(updatedDependency);
242
244
  }
245
+
246
+ updateOperationsPerformed.AddRange(updateResult.UpdateOperations);
243
247
  }
244
248
  }
245
249
 
@@ -317,6 +321,10 @@ public class RunWorker
317
321
  await SendApiMessage(new SecurityUpdateNotNeeded(depName));
318
322
  }
319
323
 
324
+ var normalizedUpdateOperationsPerformed = UpdateOperationBase.NormalizeUpdateOperationCollection(repoContentsPath.FullName, updateOperationsPerformed);
325
+ var report = UpdateOperationBase.GenerateUpdateOperationReport(normalizedUpdateOperationsPerformed);
326
+ _logger.Info(report);
327
+
320
328
  var result = new RunResult()
321
329
  {
322
330
  Base64DependencyFiles = originalDependencyFileContents.OrderBy(kvp => kvp.Key).Select(kvp =>