dependabot-nuget 0.289.0 → 0.291.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/helpers/lib/NuGetUpdater/Directory.Packages.props +1 -1
  3. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/AnalyzeCommand.cs +7 -3
  4. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/RunCommand.cs +1 -1
  5. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Analyze.cs +26 -1
  6. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Discover.cs +2 -1
  7. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Run.cs +0 -6
  8. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Update.cs +1 -1
  9. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/AnalyzeWorker.cs +6 -1
  10. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/CompatabilityChecker.cs +24 -9
  11. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/DependencyFinder.cs +2 -0
  12. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/NuGetContext.cs +0 -13
  13. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/RequirementConverter.cs +17 -0
  14. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/DiscoveryWorker.cs +44 -5
  15. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/PackagesConfigDiscovery.cs +2 -2
  16. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/ProjectDiscoveryResult.cs +2 -0
  17. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/SdkProjectDiscovery.cs +19 -11
  18. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/ErrorType.cs +1 -0
  19. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/ExperimentsManager.cs +3 -0
  20. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/Advisory.cs +13 -0
  21. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/AllowedUpdate.cs +18 -1
  22. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/CommitOptions.cs +8 -0
  23. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/Condition.cs +19 -0
  24. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/DependencyGroup.cs +8 -0
  25. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/GroupPullRequest.cs +9 -0
  26. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/Job.cs +13 -10
  27. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/PullRequest.cs +11 -0
  28. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/RequirementsUpdateStrategy.cs +15 -0
  29. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/RunWorker.cs +24 -4
  30. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/VersionConverter.cs +19 -0
  31. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/BindingRedirectManager.cs +2 -1
  32. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/LockFileUpdater.cs +3 -2
  33. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackageReferenceUpdater.cs +43 -18
  34. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackagesConfigUpdater.cs +13 -12
  35. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/UpdaterWorker.cs +1 -1
  36. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/JsonHelper.cs +2 -0
  37. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +40 -14
  38. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/NuGetHelper.cs +2 -2
  39. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/ProcessExtensions.cs +45 -7
  40. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/ProjectHelper.cs +2 -2
  41. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/AnalyzeWorkerTestBase.cs +5 -2
  42. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.DotNetToolsJson.cs +45 -1
  43. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.GlobalJson.cs +35 -1
  44. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.Project.cs +0 -4
  45. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.cs +41 -0
  46. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/ExpectedDiscoveryResults.cs +1 -0
  47. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/SdkProjectDiscoveryTests.cs +1 -1
  48. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/MockNuGetPackage.cs +2 -1
  49. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/MiscellaneousTests.cs +85 -0
  50. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/RunWorkerTests.cs +7 -31
  51. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/SerializationTests.cs +340 -0
  52. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/TemporaryDirectory.cs +18 -7
  53. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/PackagesConfigUpdaterTests.cs +24 -0
  54. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTestBase.cs +0 -12
  55. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.DotNetTools.cs +84 -0
  56. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.GlobalJson.cs +66 -0
  57. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackageReference.cs +55 -0
  58. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackagesConfig.cs +0 -6
  59. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/MSBuildHelperTests.cs +785 -755
  60. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/PathHelperTests.cs +2 -2
  61. data/lib/dependabot/nuget/analysis/analysis_json_reader.rb +1 -1
  62. data/lib/dependabot/nuget/analysis/dependency_analysis.rb +3 -3
  63. data/lib/dependabot/nuget/discovery/dependency_details.rb +10 -3
  64. data/lib/dependabot/nuget/discovery/dependency_file_discovery.rb +8 -12
  65. data/lib/dependabot/nuget/discovery/discovery_json_reader.rb +214 -29
  66. data/lib/dependabot/nuget/discovery/project_discovery.rb +41 -8
  67. data/lib/dependabot/nuget/discovery/workspace_discovery.rb +14 -19
  68. data/lib/dependabot/nuget/file_fetcher.rb +3 -3
  69. data/lib/dependabot/nuget/file_parser.rb +92 -3
  70. data/lib/dependabot/nuget/file_updater.rb +13 -13
  71. data/lib/dependabot/nuget/language.rb +82 -0
  72. data/lib/dependabot/nuget/native_helpers.rb +37 -5
  73. data/lib/dependabot/nuget/package_manager.rb +51 -0
  74. data/lib/dependabot/nuget/update_checker/requirements_updater.rb +23 -27
  75. data/lib/dependabot/nuget/update_checker.rb +116 -190
  76. metadata +20 -29
  77. data/lib/dependabot/nuget/discovery/directory_packages_props_discovery.rb +0 -43
  78. data/lib/dependabot/nuget/http_response_helpers.rb +0 -19
  79. data/lib/dependabot/nuget/native_discovery/native_dependency_details.rb +0 -102
  80. data/lib/dependabot/nuget/native_discovery/native_dependency_file_discovery.rb +0 -122
  81. data/lib/dependabot/nuget/native_discovery/native_discovery_json_reader.rb +0 -277
  82. data/lib/dependabot/nuget/native_discovery/native_evaluation_details.rb +0 -63
  83. data/lib/dependabot/nuget/native_discovery/native_project_discovery.rb +0 -104
  84. data/lib/dependabot/nuget/native_discovery/native_property_details.rb +0 -43
  85. data/lib/dependabot/nuget/native_discovery/native_workspace_discovery.rb +0 -61
  86. data/lib/dependabot/nuget/native_update_checker/native_requirements_updater.rb +0 -105
  87. data/lib/dependabot/nuget/native_update_checker/native_update_checker.rb +0 -214
  88. data/lib/dependabot/nuget/nuget_client.rb +0 -223
  89. data/lib/dependabot/nuget/update_checker/compatibility_checker.rb +0 -116
  90. data/lib/dependabot/nuget/update_checker/dependency_finder.rb +0 -297
  91. data/lib/dependabot/nuget/update_checker/nupkg_fetcher.rb +0 -221
  92. data/lib/dependabot/nuget/update_checker/nuspec_fetcher.rb +0 -110
  93. data/lib/dependabot/nuget/update_checker/property_updater.rb +0 -196
  94. data/lib/dependabot/nuget/update_checker/repository_finder.rb +0 -466
  95. data/lib/dependabot/nuget/update_checker/tfm_comparer.rb +0 -34
  96. data/lib/dependabot/nuget/update_checker/tfm_finder.rb +0 -30
  97. data/lib/dependabot/nuget/update_checker/version_finder.rb +0 -449
@@ -0,0 +1,8 @@
1
+ namespace NuGetUpdater.Core.Run.ApiModel;
2
+
3
+ public record DependencyGroup
4
+ {
5
+ public required string Name { get; init; }
6
+ public string? AppliesTo { get; init; }
7
+ public Dictionary<string, object> Rules { get; init; } = new();
8
+ }
@@ -0,0 +1,9 @@
1
+ using System.Collections.Immutable;
2
+
3
+ namespace NuGetUpdater.Core.Run.ApiModel;
4
+
5
+ public record GroupPullRequest
6
+ {
7
+ public required string DependencyGroupName { get; init; }
8
+ public required ImmutableArray<PullRequest> Dependencies { get; init; }
9
+ }
@@ -1,25 +1,28 @@
1
+ using System.Collections.Immutable;
1
2
  using System.Text.Json;
2
3
  using System.Text.Json.Serialization;
3
4
 
5
+ using NuGet.Credentials;
6
+
4
7
  namespace NuGetUpdater.Core.Run.ApiModel;
5
8
 
6
9
  public sealed record Job
7
10
  {
8
11
  public string PackageManager { get; init; } = "nuget";
9
- public AllowedUpdate[]? AllowedUpdates { get; init; } = null;
12
+ public ImmutableArray<AllowedUpdate> AllowedUpdates { get; init; } = [new AllowedUpdate()];
10
13
 
11
14
  [JsonConverter(typeof(NullAsBoolConverter))]
12
15
  public bool Debug { get; init; } = false;
13
- public object[]? DependencyGroups { get; init; } = null;
14
- public object[]? Dependencies { get; init; } = null;
16
+ public ImmutableArray<DependencyGroup> DependencyGroups { get; init; } = [];
17
+ public ImmutableArray<string>? Dependencies { get; init; } = null;
15
18
  public string? DependencyGroupToRefresh { get; init; } = null;
16
- public object[]? ExistingPullRequests { get; init; } = null;
17
- public object[]? ExistingGroupPullRequests { get; init; } = null;
19
+ public ImmutableArray<ImmutableArray<PullRequest>> ExistingPullRequests { get; init; } = [];
20
+ public ImmutableArray<GroupPullRequest> ExistingGroupPullRequests { get; init; } = [];
18
21
  public Dictionary<string, object>? Experiments { get; init; } = null;
19
- public object[]? IgnoreConditions { get; init; } = null;
22
+ public Condition[] IgnoreConditions { get; init; } = [];
20
23
  public bool LockfileOnly { get; init; } = false;
21
- public string? RequirementsUpdateStrategy { get; init; } = null;
22
- public object[]? SecurityAdvisories { get; init; } = null;
24
+ public RequirementsUpdateStrategy? RequirementsUpdateStrategy { get; init; } = null;
25
+ public ImmutableArray<Advisory> SecurityAdvisories { get; init; } = [];
23
26
  public bool SecurityUpdatesOnly { get; init; } = false;
24
27
  public required JobSource Source { get; init; }
25
28
  public bool UpdateSubdependencies { get; init; } = false;
@@ -27,8 +30,8 @@ public sealed record Job
27
30
  public bool VendorDependencies { get; init; } = false;
28
31
  public bool RejectExternalCode { get; init; } = false;
29
32
  public bool RepoPrivate { get; init; } = false;
30
- public object? CommitMessageOptions { get; init; } = null;
31
- public object[]? CredentialsMetadata { get; init; } = null;
33
+ public CommitOptions? CommitMessageOptions { get; init; } = null;
34
+ public ImmutableArray<Dictionary<string, string>>? CredentialsMetadata { get; init; } = null;
32
35
  public int MaxUpdaterRunTime { get; init; } = 0;
33
36
 
34
37
  public IEnumerable<string> GetAllDirectories()
@@ -0,0 +1,11 @@
1
+ using NuGet.Versioning;
2
+
3
+ namespace NuGetUpdater.Core.Run.ApiModel;
4
+
5
+ public record PullRequest
6
+ {
7
+ public required string DependencyName { get; init; }
8
+ public required NuGetVersion DependencyVersion { get; init; }
9
+ public bool DependencyRemoved { get; init; } = false;
10
+ public string? Directory { get; init; } = null;
11
+ }
@@ -0,0 +1,15 @@
1
+ using System.Text.Json.Serialization;
2
+
3
+ namespace NuGetUpdater.Core.Run.ApiModel;
4
+
5
+ public enum RequirementsUpdateStrategy
6
+ {
7
+ [JsonStringEnumMemberName("bump_versions")]
8
+ BumpVersions,
9
+ [JsonStringEnumMemberName("bump_versions_if_necessary")]
10
+ BumpVersionsIfNecessary,
11
+ [JsonStringEnumMemberName("lockfile_only")]
12
+ LockfileOnly,
13
+ [JsonStringEnumMemberName("widen_ranges")]
14
+ WidenRanges,
15
+ }
@@ -1,3 +1,4 @@
1
+ using System.Collections.Immutable;
1
2
  using System.Net;
2
3
  using System.Text;
3
4
  using System.Text.Json;
@@ -21,7 +22,7 @@ public class RunWorker
21
22
  {
22
23
  PropertyNamingPolicy = JsonNamingPolicy.KebabCaseLower,
23
24
  WriteIndented = true,
24
- Converters = { new JsonStringEnumConverter() },
25
+ Converters = { new JsonStringEnumConverter(), new RequirementConverter(), new VersionConverter() },
25
26
  };
26
27
 
27
28
  public RunWorker(IApiHandler apiHandler, IDiscoveryWorker discoverWorker, IAnalyzeWorker analyzeWorker, IUpdaterWorker updateWorker, ILogger logger)
@@ -123,9 +124,8 @@ public class RunWorker
123
124
  // TODO: pull out relevant dependencies, then check each for updates and track the changes
124
125
  // TODO: for each top-level dependency, _or_ specific dependency (if security, use transitive)
125
126
  var originalDependencyFileContents = new Dictionary<string, string>();
126
- var allowedUpdates = job.AllowedUpdates ?? [];
127
127
  var actualUpdatedDependencies = new List<ReportedDependency>();
128
- if (allowedUpdates.Any(a => a.UpdateType == "all"))
128
+ if (job.AllowedUpdates.Any(a => a.UpdateType == UpdateType.All))
129
129
  {
130
130
  await _apiHandler.IncrementMetric(new()
131
131
  {
@@ -173,12 +173,13 @@ public class RunWorker
173
173
  continue;
174
174
  }
175
175
 
176
+ var ignoredVersions = GetIgnoredRequirementsForDependency(job, dependency.Name);
176
177
  var dependencyInfo = new DependencyInfo()
177
178
  {
178
179
  Name = dependency.Name,
179
180
  Version = dependency.Version!,
180
181
  IsVulnerable = false,
181
- IgnoredVersions = [],
182
+ IgnoredVersions = ignoredVersions,
182
183
  Vulnerabilities = [],
183
184
  };
184
185
  var analysisResult = await _analyzeWorker.RunAsync(repoContentsPath.FullName, discoveryResult, dependencyInfo);
@@ -303,6 +304,25 @@ public class RunWorker
303
304
  return result;
304
305
  }
305
306
 
307
+ internal static ImmutableArray<Requirement> GetIgnoredRequirementsForDependency(Job job, string dependencyName)
308
+ {
309
+ var ignoreConditions = job.IgnoreConditions
310
+ .Where(c => c.DependencyName.Equals(dependencyName, StringComparison.OrdinalIgnoreCase))
311
+ .ToArray();
312
+ if (ignoreConditions.Length == 1 && ignoreConditions[0].VersionRequirement is null)
313
+ {
314
+ // if only one match with no version requirement, ignore all versions
315
+ return [Requirement.Parse("> 0.0.0")];
316
+ }
317
+
318
+ var ignoredVersions = ignoreConditions
319
+ .Select(c => c.VersionRequirement)
320
+ .Where(r => r is not null)
321
+ .Cast<Requirement>()
322
+ .ToImmutableArray();
323
+ return ignoredVersions;
324
+ }
325
+
306
326
  internal static UpdatedDependencyList GetUpdatedDependencyListFromDiscovery(WorkspaceDiscoveryResult discoveryResult, string pathToContents)
307
327
  {
308
328
  string GetFullRepoPath(string path)
@@ -0,0 +1,19 @@
1
+ using System.Text.Json;
2
+ using System.Text.Json.Serialization;
3
+
4
+ using NuGet.Versioning;
5
+
6
+ namespace NuGetUpdater.Core.Analyze;
7
+
8
+ public class VersionConverter : JsonConverter<NuGetVersion>
9
+ {
10
+ public override NuGetVersion? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
11
+ {
12
+ return NuGetVersion.Parse(reader.GetString()!);
13
+ }
14
+
15
+ public override void Write(Utf8JsonWriter writer, NuGetVersion value, JsonSerializerOptions options)
16
+ {
17
+ writer.WriteStringValue(value.ToString());
18
+ }
19
+ }
@@ -60,7 +60,8 @@ internal static class BindingRedirectManager
60
60
  // finally we pull out the assembly `HintPath` values for _all_ references relative to the project file in a unix-style value
61
61
  // e.g., ../packages/Some.Other.Package/4.5.6/lib/net45/Some.Other.Package.dll
62
62
  // all of that is passed to `AddBindingRedirects()` so we can ensure binding redirects for the relevant assemblies
63
- var packagesDirectory = PackagesConfigUpdater.GetPathToPackagesDirectory(projectBuildFile, updatedPackageName, updatedPackageVersion, packagesConfigPath: null)!;
63
+ var packagesConfigPath = ProjectHelper.GetPackagesConfigPathFromProject(projectBuildFile.Path, ProjectHelper.PathFormat.Full);
64
+ var packagesDirectory = PackagesConfigUpdater.GetPathToPackagesDirectory(projectBuildFile, updatedPackageName, updatedPackageVersion, packagesConfigPath)!;
64
65
  var assemblyPathPrefix = Path.Combine(packagesDirectory, $"{updatedPackageName}.{updatedPackageVersion}").NormalizePathToUnix().EnsureSuffix("/");
65
66
  var assemblyPaths = references.Select(static x => x.HintPath).Select(x => Path.GetRelativePath(Path.GetDirectoryName(projectBuildFile.Path)!, x).NormalizePathToUnix()).ToList();
66
67
  var bindingsAndAssemblyPaths = bindings.Zip(assemblyPaths);
@@ -5,12 +5,13 @@ internal static class LockFileUpdater
5
5
  public static async Task UpdateLockFileAsync(
6
6
  string repoRootPath,
7
7
  string projectPath,
8
+ ExperimentsManager experimentsManager,
8
9
  ILogger logger)
9
10
  {
10
11
  var projectDirectory = Path.GetDirectoryName(projectPath)!;
11
- await MSBuildHelper.SidelineGlobalJsonAsync(projectDirectory, repoRootPath, async () =>
12
+ await MSBuildHelper.HandleGlobalJsonAsync(projectDirectory, repoRootPath, experimentsManager, async () =>
12
13
  {
13
- var (exitCode, stdout, stderr) = await ProcessEx.RunAsync("dotnet", ["restore", "--force-evaluate", projectPath], workingDirectory: projectDirectory);
14
+ var (exitCode, stdout, stderr) = await ProcessEx.RunDotnetWithoutMSBuildEnvironmentVariablesAsync(["restore", "--force-evaluate", projectPath], projectDirectory, experimentsManager);
14
15
  if (exitCode != 0)
15
16
  {
16
17
  logger.Error($" Lock file update failed.\nSTDOUT:\n{stdout}\nSTDERR:\n{stderr}");
@@ -37,17 +37,17 @@ internal static class PackageReferenceUpdater
37
37
  // Get the set of all top-level dependencies in the current project
38
38
  var topLevelDependencies = MSBuildHelper.GetTopLevelPackageDependencyInfos(buildFiles).ToArray();
39
39
 
40
- if (!await DoesDependencyRequireUpdateAsync(repoRootPath, projectPath, tfms, topLevelDependencies, dependencyName, newDependencyVersion, logger))
40
+ if (!await DoesDependencyRequireUpdateAsync(repoRootPath, projectPath, tfms, topLevelDependencies, dependencyName, newDependencyVersion, experimentsManager, logger))
41
41
  {
42
42
  return;
43
43
  }
44
44
 
45
- var peerDependencies = await GetUpdatedPeerDependenciesAsync(repoRootPath, projectPath, tfms, dependencyName, newDependencyVersion, logger);
45
+ var peerDependencies = await GetUpdatedPeerDependenciesAsync(repoRootPath, projectPath, tfms, dependencyName, newDependencyVersion, experimentsManager, logger);
46
46
  if (experimentsManager.UseLegacyDependencySolver)
47
47
  {
48
48
  if (isTransitive)
49
49
  {
50
- await UpdateTransitiveDependencyAsync(repoRootPath, projectPath, dependencyName, newDependencyVersion, buildFiles, logger);
50
+ await UpdateTransitiveDependencyAsync(repoRootPath, projectPath, dependencyName, newDependencyVersion, buildFiles, experimentsManager, logger);
51
51
  }
52
52
  else
53
53
  {
@@ -56,7 +56,7 @@ internal static class PackageReferenceUpdater
56
56
  return;
57
57
  }
58
58
 
59
- await UpdateTopLevelDepdendency(repoRootPath, buildFiles, tfms, dependencyName, previousDependencyVersion, newDependencyVersion, peerDependencies, logger);
59
+ await UpdateTopLevelDepdendency(repoRootPath, buildFiles, tfms, dependencyName, previousDependencyVersion, newDependencyVersion, peerDependencies, experimentsManager, logger);
60
60
  }
61
61
  }
62
62
  else
@@ -66,10 +66,10 @@ internal static class PackageReferenceUpdater
66
66
  return;
67
67
  }
68
68
 
69
- await UpdateDependencyWithConflictResolution(repoRootPath, buildFiles, tfms, projectPath, dependencyName, previousDependencyVersion, newDependencyVersion, isTransitive, peerDependencies, logger);
69
+ await UpdateDependencyWithConflictResolution(repoRootPath, buildFiles, tfms, projectPath, dependencyName, previousDependencyVersion, newDependencyVersion, isTransitive, peerDependencies, experimentsManager, logger);
70
70
  }
71
71
 
72
- if (!await AreDependenciesCoherentAsync(repoRootPath, projectPath, dependencyName, logger, buildFiles, tfms))
72
+ if (!await AreDependenciesCoherentAsync(repoRootPath, projectPath, dependencyName, buildFiles, tfms, experimentsManager, logger))
73
73
  {
74
74
  return;
75
75
  }
@@ -87,6 +87,7 @@ internal static class PackageReferenceUpdater
87
87
  string newDependencyVersion,
88
88
  bool isTransitive,
89
89
  IDictionary<string, string> peerDependencies,
90
+ ExperimentsManager experimentsManager,
90
91
  ILogger logger)
91
92
  {
92
93
  var topLevelDependencies = MSBuildHelper.GetTopLevelPackageDependencyInfos(buildFiles).ToArray();
@@ -107,7 +108,7 @@ internal static class PackageReferenceUpdater
107
108
  {
108
109
  foreach (var tfm in targetFrameworks)
109
110
  {
110
- var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflicts(repoRootPath, projectFile.Path, tfm, topLevelDependencies, dependenciesToUpdate, logger);
111
+ var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflicts(repoRootPath, projectFile.Path, tfm, topLevelDependencies, dependenciesToUpdate, experimentsManager, logger);
111
112
  if (resolvedDependencies is null)
112
113
  {
113
114
  logger.Warn($" Unable to resolve dependency conflicts for {projectFile.Path}.");
@@ -118,7 +119,7 @@ internal static class PackageReferenceUpdater
118
119
  if (isTransitive && !isDependencyTopLevel && isDependencyInResolutionSet)
119
120
  {
120
121
  // a transitive dependency had to be pinned; add it here
121
- await UpdateTransitiveDependencyAsync(repoRootPath, projectPath, dependencyName, newDependencyVersion, buildFiles, logger);
122
+ await UpdateTransitiveDependencyAsync(repoRootPath, projectPath, dependencyName, newDependencyVersion, buildFiles, experimentsManager, logger);
122
123
  }
123
124
 
124
125
  // update all resolved dependencies that aren't the initial dependency
@@ -143,6 +144,7 @@ internal static class PackageReferenceUpdater
143
144
  Dependency[] topLevelDependencies,
144
145
  string dependencyName,
145
146
  string newDependencyVersion,
147
+ ExperimentsManager experimentsManager,
146
148
  ILogger logger)
147
149
  {
148
150
  var newDependencyNuGetVersion = NuGetVersion.Parse(newDependencyVersion);
@@ -157,6 +159,7 @@ internal static class PackageReferenceUpdater
157
159
  projectPath,
158
160
  tfm,
159
161
  topLevelDependencies,
162
+ experimentsManager,
160
163
  logger);
161
164
  foreach (var dependency in dependencies)
162
165
  {
@@ -203,7 +206,15 @@ internal static class PackageReferenceUpdater
203
206
  return true;
204
207
  }
205
208
 
206
- private static async Task UpdateTransitiveDependencyAsync(string repoRootPath, string projectPath, string dependencyName, string newDependencyVersion, ImmutableArray<ProjectBuildFile> buildFiles, ILogger logger)
209
+ private static async Task UpdateTransitiveDependencyAsync(
210
+ string repoRootPath,
211
+ string projectPath,
212
+ string dependencyName,
213
+ string newDependencyVersion,
214
+ ImmutableArray<ProjectBuildFile> buildFiles,
215
+ ExperimentsManager experimentsManager,
216
+ ILogger logger
217
+ )
207
218
  {
208
219
  var directoryPackagesWithPinning = buildFiles.OfType<ProjectBuildFile>()
209
220
  .FirstOrDefault(bf => IsCpmTransitivePinningEnabled(bf));
@@ -213,7 +224,7 @@ internal static class PackageReferenceUpdater
213
224
  }
214
225
  else
215
226
  {
216
- await AddTransitiveDependencyAsync(repoRootPath, projectPath, dependencyName, newDependencyVersion, logger);
227
+ await AddTransitiveDependencyAsync(repoRootPath, projectPath, dependencyName, newDependencyVersion, experimentsManager, logger);
217
228
  }
218
229
  }
219
230
 
@@ -302,15 +313,19 @@ internal static class PackageReferenceUpdater
302
313
  directoryPackages.Update(updatedXml);
303
314
  }
304
315
 
305
- private static async Task AddTransitiveDependencyAsync(string repoRootPath, string projectPath, string dependencyName, string newDependencyVersion, ILogger logger)
316
+ private static async Task AddTransitiveDependencyAsync(string repoRootPath, string projectPath, string dependencyName, string newDependencyVersion, ExperimentsManager experimentsManager, ILogger logger)
306
317
  {
307
318
  var projectDirectory = Path.GetDirectoryName(projectPath)!;
308
- await MSBuildHelper.SidelineGlobalJsonAsync(projectDirectory, repoRootPath, async () =>
319
+ await MSBuildHelper.HandleGlobalJsonAsync(projectDirectory, repoRootPath, experimentsManager, async () =>
309
320
  {
310
321
  logger.Info($" Adding [{dependencyName}/{newDependencyVersion}] as a top-level package reference.");
311
322
 
312
323
  // see https://learn.microsoft.com/nuget/consume-packages/install-use-packages-dotnet-cli
313
- var (exitCode, stdout, stderr) = await ProcessEx.RunAsync("dotnet", ["add", projectPath, "package", dependencyName, "--version", newDependencyVersion], workingDirectory: projectDirectory);
324
+ var (exitCode, stdout, stderr) = await ProcessEx.RunDotnetWithoutMSBuildEnvironmentVariablesAsync(
325
+ ["add", projectPath, "package", dependencyName, "--version", newDependencyVersion],
326
+ projectDirectory,
327
+ experimentsManager
328
+ );
314
329
  MSBuildHelper.ThrowOnUnauthenticatedFeed(stdout);
315
330
  if (exitCode != 0)
316
331
  {
@@ -331,13 +346,14 @@ internal static class PackageReferenceUpdater
331
346
  string[] tfms,
332
347
  string dependencyName,
333
348
  string newDependencyVersion,
349
+ ExperimentsManager experimentsManager,
334
350
  ILogger logger)
335
351
  {
336
352
  var newDependency = new[] { new Dependency(dependencyName, newDependencyVersion, DependencyType.Unknown) };
337
353
  var tfmsAndDependencies = new Dictionary<string, Dependency[]>();
338
354
  foreach (var tfm in tfms)
339
355
  {
340
- var dependencies = await MSBuildHelper.GetAllPackageDependenciesAsync(repoRootPath, projectPath, tfm, newDependency, logger);
356
+ var dependencies = await MSBuildHelper.GetAllPackageDependenciesAsync(repoRootPath, projectPath, tfm, newDependency, experimentsManager, logger);
341
357
  tfmsAndDependencies[tfm] = dependencies;
342
358
  }
343
359
 
@@ -386,6 +402,7 @@ internal static class PackageReferenceUpdater
386
402
  string previousDependencyVersion,
387
403
  string newDependencyVersion,
388
404
  IDictionary<string, string> peerDependencies,
405
+ ExperimentsManager experimentsManager,
389
406
  ILogger logger)
390
407
  {
391
408
  // update dependencies...
@@ -407,7 +424,7 @@ internal static class PackageReferenceUpdater
407
424
  {
408
425
  foreach (string tfm in targetFrameworks)
409
426
  {
410
- var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflictsWithBruteForce(repoRootPath, projectFile.Path, tfm, updatedTopLevelDependencies, logger);
427
+ var resolvedDependencies = await MSBuildHelper.ResolveDependencyConflictsWithBruteForce(repoRootPath, projectFile.Path, tfm, updatedTopLevelDependencies, experimentsManager, logger);
411
428
  if (resolvedDependencies is null)
412
429
  {
413
430
  logger.Info($" Unable to resolve dependency conflicts for {projectFile.Path}.");
@@ -697,13 +714,21 @@ internal static class PackageReferenceUpdater
697
714
  ?? e.GetAttributeOrSubElementValue("VersionOverride", StringComparison.OrdinalIgnoreCase)) is not null;
698
715
  });
699
716
 
700
- private static async Task<bool> AreDependenciesCoherentAsync(string repoRootPath, string projectPath, string dependencyName, ILogger logger, ImmutableArray<ProjectBuildFile> buildFiles, string[] tfms)
717
+ private static async Task<bool> AreDependenciesCoherentAsync(
718
+ string repoRootPath,
719
+ string projectPath,
720
+ string dependencyName,
721
+ ImmutableArray<ProjectBuildFile> buildFiles,
722
+ string[] tfms,
723
+ ExperimentsManager experimentsManager,
724
+ ILogger logger
725
+ )
701
726
  {
702
727
  var updatedTopLevelDependencies = MSBuildHelper.GetTopLevelPackageDependencyInfos(buildFiles).ToArray();
703
728
  foreach (var tfm in tfms)
704
729
  {
705
- var updatedPackages = await MSBuildHelper.GetAllPackageDependenciesAsync(repoRootPath, projectPath, tfm, updatedTopLevelDependencies, logger);
706
- var dependenciesAreCoherent = await MSBuildHelper.DependenciesAreCoherentAsync(repoRootPath, projectPath, tfm, updatedPackages, logger);
730
+ var updatedPackages = await MSBuildHelper.GetAllPackageDependenciesAsync(repoRootPath, projectPath, tfm, updatedTopLevelDependencies, experimentsManager, logger);
731
+ var dependenciesAreCoherent = await MSBuildHelper.DependenciesAreCoherentAsync(repoRootPath, projectPath, tfm, updatedPackages, experimentsManager, logger);
707
732
  if (!dependenciesAreCoherent)
708
733
  {
709
734
  logger.Warn($" Package [{dependencyName}] could not be updated in [{projectPath}] because it would cause a dependency conflict.");
@@ -23,7 +23,7 @@ namespace NuGetUpdater.Core;
23
23
  /// See: https://learn.microsoft.com/en-us/nuget/reference/packages-config
24
24
  /// https://learn.microsoft.com/en-us/nuget/resources/check-project-format
25
25
  /// <remarks>
26
- internal static class PackagesConfigUpdater
26
+ internal static partial class PackagesConfigUpdater
27
27
  {
28
28
  public static async Task UpdateDependencyAsync(
29
29
  string repoRootPath,
@@ -215,7 +215,7 @@ internal static class PackagesConfigUpdater
215
215
  var hintPathSubString = $"{dependencyName}.{dependencyVersion}";
216
216
 
217
217
  string? partialPathMatch = null;
218
- var specificHintPathNodes = projectBuildFile.Contents.Descendants().Where(e => e.IsHintPathNodeForDependency(dependencyName)).ToArray();
218
+ var specificHintPathNodes = projectBuildFile.Contents.Descendants().Where(e => e.IsHintPathNodeForDependency(dependencyName, dependencyVersion)).ToArray();
219
219
  foreach (var hintPathNode in specificHintPathNodes)
220
220
  {
221
221
  var hintPath = hintPathNode.GetContentValue();
@@ -266,8 +266,7 @@ internal static class PackagesConfigUpdater
266
266
  foreach (var hintPathNode in genericHintPathNodes)
267
267
  {
268
268
  var hintPath = hintPathNode.GetContentValue();
269
- var match = Regex.Match(hintPath, @"^(?<PackagesPath>.*)[/\\](?<PackageNameAndVersion>[^/\\]+)[/\\]lib[/\\](?<Tfm>[^/\\]+)[/\\](?<AssemblyName>[^/\\]+)$");
270
- // e.g., ..\..\packages \ Some.Package.1.2.3 \ lib\ net45 \ Some.Package.dll
269
+ var match = PackageAssemblyHintPathPattern().Match(hintPath);
271
270
  if (match.Success)
272
271
  {
273
272
  partialPathMatch = match.Groups["PackagesPath"].Value;
@@ -297,17 +296,15 @@ internal static class PackagesConfigUpdater
297
296
  return false;
298
297
  }
299
298
 
300
- private static bool IsHintPathNodeForDependency(this IXmlElementSyntax element, string dependencyName)
299
+ private static bool IsHintPathNodeForDependency(this IXmlElementSyntax element, string dependencyName, string dependencyVersion)
301
300
  {
302
301
  if (element.IsHintPathNode())
303
302
  {
304
- // the include attribute will look like one of the following:
305
- // <Reference Include="Some.Dependency, Version=1.0.0.0, Culture=neutral, PublicKeyToken=abcd">
306
- // or
307
- // <Reference Include="Some.Dependency">
308
- string includeAttributeValue = element.Parent.GetAttributeValue("Include", StringComparison.OrdinalIgnoreCase);
309
- if (includeAttributeValue.Equals(dependencyName, StringComparison.OrdinalIgnoreCase) ||
310
- includeAttributeValue.StartsWith($"{dependencyName},", StringComparison.OrdinalIgnoreCase))
303
+ // the hint path will look similar to this:
304
+ // ..\packages\Some.Package.1.2.3\lib\net45\Some.Package.dll
305
+ var assemblyPath = element.GetContentValue();
306
+ var match = PackageAssemblyHintPathPattern().Match(assemblyPath);
307
+ if (match.Success)
311
308
  {
312
309
  return true;
313
310
  }
@@ -326,4 +323,8 @@ internal static class PackagesConfigUpdater
326
323
 
327
324
  return subpath;
328
325
  }
326
+
327
+ [GeneratedRegex(@"^(?<PackagesPath>.*)[/\\](?<PackageNameAndVersion>[^/\\]+)[/\\]lib[/\\](?<Tfm>[^/\\]+)[/\\](?<AssemblyName>[^/\\]+)$", RegexOptions.IgnoreCase)]
328
+ // e.g., ..\..\packages \ Some.Package.1.2.3 \ lib \ net45 \ Some.Package.dll
329
+ private static partial Regex PackageAssemblyHintPathPattern();
329
330
  }
@@ -232,7 +232,7 @@ public class UpdaterWorker : IUpdaterWorker
232
232
  var packagesLockFullPath = additionalFiles.Where(p => Path.GetFileName(p).Equals(ProjectHelper.PackagesLockJsonFileName, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
233
233
  if (packagesLockFullPath is not null)
234
234
  {
235
- await LockFileUpdater.UpdateLockFileAsync(repoRootPath, projectPath, _logger);
235
+ await LockFileUpdater.UpdateLockFileAsync(repoRootPath, projectPath, _experimentsManager, _logger);
236
236
  }
237
237
  }
238
238
  }
@@ -11,6 +11,7 @@ namespace NuGetUpdater.Core.Utilities
11
11
  public static JsonDocumentOptions DocumentOptions { get; } = new JsonDocumentOptions
12
12
  {
13
13
  CommentHandling = JsonCommentHandling.Skip,
14
+ AllowTrailingCommas = true,
14
15
  };
15
16
 
16
17
  public static JsonNode? ParseNode(string content)
@@ -24,6 +25,7 @@ namespace NuGetUpdater.Core.Utilities
24
25
  var readerOptions = new JsonReaderOptions
25
26
  {
26
27
  CommentHandling = JsonCommentHandling.Allow,
28
+ AllowTrailingCommas = true,
27
29
  };
28
30
  var bytes = Encoding.UTF8.GetBytes(json);
29
31
  var reader = new Utf8JsonReader(bytes, readerOptions);
@@ -34,7 +34,8 @@ internal static partial class MSBuildHelper
34
34
  // Ensure MSBuild types are registered before calling a method that loads the types
35
35
  if (!IsMSBuildRegistered)
36
36
  {
37
- SidelineGlobalJsonAsync(currentDirectory, rootDirectory, () =>
37
+ var experimentsManager = new ExperimentsManager() { InstallDotnetSdks = false }; // `global.json` definitely needs to be moved for this operation
38
+ HandleGlobalJsonAsync(currentDirectory, rootDirectory, experimentsManager, () =>
38
39
  {
39
40
  var defaultInstance = MSBuildLocator.QueryVisualStudioInstances().First();
40
41
  MSBuildPath = defaultInstance.MSBuildPath;
@@ -44,9 +45,23 @@ internal static partial class MSBuildHelper
44
45
  }
45
46
  }
46
47
 
47
- public static async Task<T> SidelineGlobalJsonAsync<T>(string currentDirectory, string rootDirectory, Func<Task<T>> action, ILogger? logger = null, bool retainMSBuildSdks = false)
48
+ public static async Task<T> HandleGlobalJsonAsync<T>(
49
+ string currentDirectory,
50
+ string rootDirectory,
51
+ ExperimentsManager experimentsManager,
52
+ Func<Task<T>> action,
53
+ ILogger? logger = null,
54
+ bool retainMSBuildSdks = false
55
+ )
48
56
  {
49
57
  logger ??= new ConsoleLogger();
58
+ if (experimentsManager.InstallDotnetSdks)
59
+ {
60
+ logger.Info($"{nameof(ExperimentsManager.InstallDotnetSdks)} == true; retaining `global.json` contents.");
61
+ var result = await action();
62
+ return result;
63
+ }
64
+
50
65
  var candidateDirectories = PathHelper.GetAllDirectoriesToRoot(currentDirectory, rootDirectory);
51
66
  var globalJsonPaths = candidateDirectories.Select(d => Path.Combine(d, "global.json")).Where(File.Exists).Select(p => (p, p + Guid.NewGuid().ToString())).ToArray();
52
67
  foreach (var (globalJsonPath, tempGlobalJsonPath) in globalJsonPaths)
@@ -322,13 +337,13 @@ internal static partial class MSBuildHelper
322
337
  return false;
323
338
  }
324
339
 
325
- internal static async Task<bool> DependenciesAreCoherentAsync(string repoRoot, string projectPath, string targetFramework, Dependency[] packages, ILogger logger)
340
+ internal static async Task<bool> DependenciesAreCoherentAsync(string repoRoot, string projectPath, string targetFramework, Dependency[] packages, ExperimentsManager experimentsManager, ILogger logger)
326
341
  {
327
342
  var tempDirectory = Directory.CreateTempSubdirectory("package-dependency-coherence_");
328
343
  try
329
344
  {
330
345
  var tempProjectPath = await CreateTempProjectAsync(tempDirectory, repoRoot, projectPath, targetFramework, packages, logger);
331
- var (exitCode, stdOut, stdErr) = await ProcessEx.RunAsync("dotnet", ["restore", tempProjectPath], workingDirectory: tempDirectory.FullName);
346
+ var (exitCode, stdOut, stdErr) = await ProcessEx.RunDotnetWithoutMSBuildEnvironmentVariablesAsync(["restore", tempProjectPath], tempDirectory.FullName, experimentsManager);
332
347
 
333
348
  // NU1608: Detected package version outside of dependency constraint
334
349
 
@@ -340,7 +355,7 @@ internal static partial class MSBuildHelper
340
355
  }
341
356
  }
342
357
 
343
- internal static async Task<Dependency[]?> ResolveDependencyConflicts(string repoRoot, string projectPath, string targetFramework, Dependency[] packages, Dependency[] update, ILogger logger)
358
+ internal static async Task<Dependency[]?> ResolveDependencyConflicts(string repoRoot, string projectPath, string targetFramework, Dependency[] packages, Dependency[] update, ExperimentsManager experimentsManager, ILogger logger)
344
359
  {
345
360
  var tempDirectory = Directory.CreateTempSubdirectory("package-dependency-coherence_");
346
361
  PackageManager packageManager = new PackageManager(repoRoot, projectPath);
@@ -348,7 +363,7 @@ internal static partial class MSBuildHelper
348
363
  try
349
364
  {
350
365
  string tempProjectPath = await CreateTempProjectAsync(tempDirectory, repoRoot, projectPath, targetFramework, packages, logger);
351
- var (exitCode, stdOut, stdErr) = await ProcessEx.RunAsync("dotnet", ["restore", tempProjectPath], workingDirectory: tempDirectory.FullName);
366
+ var (exitCode, stdOut, stdErr) = await ProcessEx.RunDotnetWithoutMSBuildEnvironmentVariablesAsync(["restore", tempProjectPath], tempDirectory.FullName, experimentsManager);
352
367
 
353
368
  // Add Dependency[] packages to List<PackageToUpdate> existingPackages
354
369
  List<PackageToUpdate> existingPackages = packages
@@ -498,13 +513,13 @@ internal static partial class MSBuildHelper
498
513
  }
499
514
  }
500
515
 
501
- internal static async Task<Dependency[]?> ResolveDependencyConflictsWithBruteForce(string repoRoot, string projectPath, string targetFramework, Dependency[] packages, ILogger logger)
516
+ internal static async Task<Dependency[]?> ResolveDependencyConflictsWithBruteForce(string repoRoot, string projectPath, string targetFramework, Dependency[] packages, ExperimentsManager experimentsManager, ILogger logger)
502
517
  {
503
518
  var tempDirectory = Directory.CreateTempSubdirectory("package-dependency-coherence_");
504
519
  try
505
520
  {
506
521
  var tempProjectPath = await CreateTempProjectAsync(tempDirectory, repoRoot, projectPath, targetFramework, packages, logger);
507
- var (exitCode, stdOut, stdErr) = await ProcessEx.RunAsync("dotnet", ["restore", tempProjectPath], workingDirectory: tempDirectory.FullName);
522
+ var (exitCode, stdOut, stdErr) = await ProcessEx.RunDotnetWithoutMSBuildEnvironmentVariablesAsync(["restore", tempProjectPath], tempDirectory.FullName, experimentsManager);
508
523
  ThrowOnUnauthenticatedFeed(stdOut);
509
524
 
510
525
  // simple cases first
@@ -529,6 +544,7 @@ internal static partial class MSBuildHelper
529
544
  foreach ((string PackageName, NuGetVersion packageVersion) in badPackagesAndVersions)
530
545
  {
531
546
  // this command dumps a JSON object with all versions of the specified package from all package sources
547
+ // not using the `dotnet` execution method because we want to force the latest MSBuild and SDK to be used
532
548
  (exitCode, stdOut, stdErr) = await ProcessEx.RunAsync("dotnet", ["package", "search", PackageName, "--exact-match", "--format", "json"], workingDirectory: tempDirectory.FullName);
533
549
  if (exitCode != 0)
534
550
  {
@@ -591,7 +607,7 @@ internal static partial class MSBuildHelper
591
607
  return p;
592
608
  }).ToArray();
593
609
 
594
- if (await DependenciesAreCoherentAsync(repoRoot, projectPath, targetFramework, candidatePackages, logger))
610
+ if (await DependenciesAreCoherentAsync(repoRoot, projectPath, targetFramework, candidatePackages, experimentsManager, logger))
595
611
  {
596
612
  // return as soon as we find a coherent set
597
613
  return candidatePackages;
@@ -749,14 +765,23 @@ internal static partial class MSBuildHelper
749
765
  return tempProjectPath;
750
766
  }
751
767
 
752
- internal static async Task<ImmutableArray<string>> GetTargetFrameworkValuesFromProject(string repoRoot, string projectPath, ILogger logger)
768
+ internal static async Task<ImmutableArray<string>> GetTargetFrameworkValuesFromProject(string repoRoot, string projectPath, ExperimentsManager experimentsManager, ILogger logger)
753
769
  {
754
- // TODO: once the updater image has all relevant SDKs installed, we won't have to sideline global.json anymore
755
770
  var projectDirectory = Path.GetDirectoryName(projectPath)!;
756
- var (exitCode, stdOut, stdErr) = await SidelineGlobalJsonAsync(projectDirectory, repoRoot, async () =>
771
+ var (exitCode, stdOut, stdErr) = await HandleGlobalJsonAsync(projectDirectory, repoRoot, experimentsManager, async () =>
757
772
  {
758
773
  var targetsHelperPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!, "TargetFrameworkReporter.targets");
759
- var (exitCode, stdOut, stdErr) = await ProcessEx.RunAsync("dotnet", ["build", projectPath, "/t:ReportTargetFramework", $"/p:CustomAfterMicrosoftCommonCrossTargetingTargets={targetsHelperPath};CustomAfterMicrosoftCommonTargets={targetsHelperPath}"], workingDirectory: Path.GetDirectoryName(projectPath));
774
+ var (exitCode, stdOut, stdErr) = await ProcessEx.RunDotnetWithoutMSBuildEnvironmentVariablesAsync(
775
+ [
776
+ "build",
777
+ projectPath,
778
+ "/t:ReportTargetFramework",
779
+ $"/p:CustomAfterMicrosoftCommonCrossTargetingTargets={targetsHelperPath}",
780
+ $"/p:CustomAfterMicrosoftCommonTargets={targetsHelperPath}"
781
+ ],
782
+ projectDirectory,
783
+ experimentsManager
784
+ );
760
785
  return (exitCode, stdOut, stdErr);
761
786
  });
762
787
  ThrowOnUnauthenticatedFeed(stdOut);
@@ -806,6 +831,7 @@ internal static partial class MSBuildHelper
806
831
  string projectPath,
807
832
  string targetFramework,
808
833
  IReadOnlyCollection<Dependency> packages,
834
+ ExperimentsManager experimentsManager,
809
835
  ILogger logger)
810
836
  {
811
837
  var tempDirectory = Directory.CreateTempSubdirectory("package-dependency-resolution_");
@@ -814,7 +840,7 @@ internal static partial class MSBuildHelper
814
840
  var topLevelPackagesNames = packages.Select(p => p.Name).ToHashSet(StringComparer.OrdinalIgnoreCase);
815
841
  var tempProjectPath = await CreateTempProjectAsync(tempDirectory, repoRoot, projectPath, targetFramework, packages, logger);
816
842
 
817
- var (exitCode, stdout, stderr) = await ProcessEx.RunAsync("dotnet", ["build", tempProjectPath, "/t:_ReportDependencies"], workingDirectory: tempDirectory.FullName);
843
+ var (exitCode, stdout, stderr) = await ProcessEx.RunDotnetWithoutMSBuildEnvironmentVariablesAsync(["build", tempProjectPath, "/t:_ReportDependencies"], tempDirectory.FullName, experimentsManager);
818
844
  ThrowOnUnauthenticatedFeed(stdout);
819
845
 
820
846
  if (exitCode == 0)