dependabot-nuget 0.289.0 → 0.291.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 (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)