dependabot-nuget 0.291.0 → 0.293.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/helpers/lib/NuGetUpdater/.editorconfig +1 -0
  3. data/helpers/lib/NuGetUpdater/.gitignore +1 -0
  4. data/helpers/lib/NuGetUpdater/Directory.Build.props +1 -0
  5. data/helpers/lib/NuGetUpdater/Directory.Packages.props +2 -1
  6. data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation/Correlator.cs +197 -0
  7. data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation/DotNetPackageCorrelation.csproj +12 -0
  8. data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation/Model/PackageMapper.cs +68 -0
  9. data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation/Model/PackageSet.cs +11 -0
  10. data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation/Model/Release.cs +25 -0
  11. data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation/Model/ReleasesFile.cs +9 -0
  12. data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation/Model/RuntimePackages.cs +11 -0
  13. data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation/Model/Sdk.cs +13 -0
  14. data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation/Model/SemVerComparer.cs +16 -0
  15. data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation/Model/SemVersionConverter.cs +42 -0
  16. data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation.Cli/DotNetPackageCorrelation.Cli.csproj +16 -0
  17. data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation.Cli/Program.cs +32 -0
  18. data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation.Test/CorrelatorTests.cs +99 -0
  19. data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation.Test/DotNetPackageCorrelation.Test.csproj +18 -0
  20. data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation.Test/EndToEndTests.cs +30 -0
  21. data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation.Test/RuntimePackagesTests.cs +206 -0
  22. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/AnalyzeCommand.cs +6 -4
  23. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/CloneCommand.cs +1 -1
  24. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/DiscoverCommand.cs +19 -4
  25. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/RunCommand.cs +5 -5
  26. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/UpdateCommand.cs +17 -5
  27. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Analyze.cs +8 -1
  28. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Discover.cs +128 -4
  29. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Update.cs +8 -0
  30. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/AnalyzeWorker.cs +9 -7
  31. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/DependencyFinder.cs +4 -4
  32. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/Extensions.cs +1 -1
  33. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/RequirementConverter.cs +19 -1
  34. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/BadRequirementException.cs +9 -0
  35. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Clone/CloneWorker.cs +40 -8
  36. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/DependencyDiscovery.targets +2 -2
  37. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/DiscoveryWorker.cs +65 -23
  38. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/ProjectDiscoveryResult.cs +3 -3
  39. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/SdkProjectDiscovery.cs +99 -2
  40. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/EnsureDotNetPackageCorrelation.targets +25 -0
  41. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/ExperimentsManager.cs +15 -5
  42. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Files/PackagesConfigBuildFile.cs +5 -1
  43. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/MissingFileException.cs +2 -1
  44. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/NativeResult.cs +3 -3
  45. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/NuGetUpdater.Core.csproj +4 -0
  46. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/BadRequirement.cs +10 -0
  47. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/CommitOptions.cs +1 -1
  48. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/DependencyFileNotFound.cs +7 -2
  49. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/DependencyFileNotParseable.cs +15 -0
  50. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/JobErrorBase.cs +25 -2
  51. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/JobRepoNotFound.cs +1 -4
  52. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/PrivateSourceAuthenticationFailure.cs +1 -1
  53. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/UnknownError.cs +6 -2
  54. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/UpdateNotPossible.cs +1 -1
  55. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/RunWorker.cs +9 -18
  56. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/UnparseableFileException.cs +12 -0
  57. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/LockFileUpdater.cs +1 -1
  58. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackageReferenceUpdater.cs +21 -0
  59. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/UpdaterWorker.cs +6 -30
  60. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/WebApplicationTargetsConditionPatcher.cs +12 -1
  61. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/DependencyConflictResolver.cs +0 -7
  62. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/DotNetPackageCorrelationManager.cs +46 -0
  63. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +59 -30
  64. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/NuGetHelper.cs +1 -1
  65. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/ProjectHelper.cs +4 -4
  66. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/AnalyzeWorkerTestBase.cs +15 -4
  67. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/AnalyzeWorkerTests.cs +15 -9
  68. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Clone/CloneWorkerTests.cs +60 -2
  69. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTestBase.cs +20 -3
  70. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.PackagesConfig.cs +56 -0
  71. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.Project.cs +108 -0
  72. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.cs +16 -12
  73. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/ExpectedDiscoveryResults.cs +1 -0
  74. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/MockNuGetPackage.cs +15 -28
  75. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/RunWorkerTests.cs +5 -4
  76. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/SerializationTests.cs +84 -40
  77. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/TestBase.cs +24 -0
  78. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/ExpectedUpdateOperationResult.cs +1 -1
  79. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTestBase.cs +25 -11
  80. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.DirsProj.cs +1 -1
  81. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.GlobalJson.cs +2 -2
  82. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.LockFile.cs +251 -0
  83. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.Mixed.cs +14 -8
  84. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackageReference.cs +154 -9
  85. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackagesConfig.cs +71 -15
  86. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/MSBuildHelperTests.cs +38 -20
  87. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/ProjectHelperTests.cs +65 -0
  88. data/helpers/lib/NuGetUpdater/NuGetUpdater.sln +18 -1
  89. data/helpers/lib/NuGetUpdater/global.json +1 -1
  90. data/lib/dependabot/nuget/language.rb +21 -5
  91. data/lib/dependabot/nuget/native_helpers.rb +41 -14
  92. data/lib/dependabot/nuget/package_manager.rb +4 -4
  93. metadata +30 -7
  94. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/ErrorType.cs +0 -11
@@ -1,8 +1,8 @@
1
+ using NuGetUpdater.Core.Run.ApiModel;
2
+
1
3
  namespace NuGetUpdater.Core;
2
4
 
3
5
  public record NativeResult
4
6
  {
5
- // TODO: nullable not required, `ErrorType.None` is the default anyway
6
- public ErrorType? ErrorType { get; init; }
7
- public object? ErrorDetails { get; init; }
7
+ public JobErrorBase? Error { get; init; } = null;
8
8
  }
@@ -14,6 +14,7 @@
14
14
  </ItemGroup>
15
15
 
16
16
  <ItemGroup>
17
+ <ProjectReference Include="..\DotNetPackageCorrelation\DotNetPackageCorrelation.csproj" />
17
18
  <ProjectReference Include="..\NuGetProjects\NuGet.CommandLine\NuGet.CommandLine.csproj" />
18
19
  </ItemGroup>
19
20
 
@@ -26,8 +27,11 @@
26
27
  </ItemGroup>
27
28
 
28
29
  <ItemGroup>
30
+ <InternalsVisibleTo Include="NuGetUpdater.Cli" />
29
31
  <InternalsVisibleTo Include="NuGetUpdater.Cli.Test" />
30
32
  <InternalsVisibleTo Include="NuGetUpdater.Core.Test" />
31
33
  </ItemGroup>
32
34
 
35
+ <Import Project="EnsureDotNetPackageCorrelation.targets" />
36
+
33
37
  </Project>
@@ -0,0 +1,10 @@
1
+ namespace NuGetUpdater.Core.Run.ApiModel;
2
+
3
+ public record BadRequirement : JobErrorBase
4
+ {
5
+ public BadRequirement(string details)
6
+ : base("illformed_requirement")
7
+ {
8
+ Details["message"] = details;
9
+ }
10
+ }
@@ -4,5 +4,5 @@ public record CommitOptions
4
4
  {
5
5
  public string? Prefix { get; init; } = null;
6
6
  public string? PrefixDevelopment { get; init; } = null;
7
- public string? IncludeScope { get; init; } = null;
7
+ public bool? IncludeScope { get; init; } = null;
8
8
  }
@@ -2,9 +2,14 @@ namespace NuGetUpdater.Core.Run.ApiModel;
2
2
 
3
3
  public record DependencyFileNotFound : JobErrorBase
4
4
  {
5
- public DependencyFileNotFound(string filePath)
5
+ public DependencyFileNotFound(string filePath, string? message = null)
6
6
  : base("dependency_file_not_found")
7
7
  {
8
- Details = filePath;
8
+ if (message is not null)
9
+ {
10
+ Details["message"] = message;
11
+ }
12
+
13
+ Details["file-path"] = filePath.NormalizePathToUnix();
9
14
  }
10
15
  }
@@ -0,0 +1,15 @@
1
+ namespace NuGetUpdater.Core.Run.ApiModel;
2
+
3
+ public record DependencyFileNotParseable : JobErrorBase
4
+ {
5
+ public DependencyFileNotParseable(string filePath, string? message = null)
6
+ : base("dependency_file_not_parseable")
7
+ {
8
+ if (message is not null)
9
+ {
10
+ Details["message"] = message;
11
+ }
12
+
13
+ Details["file-path"] = filePath.NormalizePathToUnix();
14
+ }
15
+ }
@@ -1,5 +1,10 @@
1
+ using System.Net;
1
2
  using System.Text.Json.Serialization;
2
3
 
4
+ using Microsoft.Build.Exceptions;
5
+
6
+ using NuGetUpdater.Core.Analyze;
7
+
3
8
  namespace NuGetUpdater.Core.Run.ApiModel;
4
9
 
5
10
  public abstract record JobErrorBase
@@ -13,6 +18,24 @@ public abstract record JobErrorBase
13
18
  public string Type { get; }
14
19
 
15
20
  [JsonPropertyName("error-details")]
16
- [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
17
- public object? Details { get; init; } = null;
21
+ public Dictionary<string, object> Details { get; init; } = new();
22
+
23
+ public static JobErrorBase ErrorFromException(Exception ex, string jobId, string currentDirectory)
24
+ {
25
+ return ex switch
26
+ {
27
+ BadRequirementException badRequirement => new BadRequirement(badRequirement.Message),
28
+ HttpRequestException httpRequest => httpRequest.StatusCode switch
29
+ {
30
+ HttpStatusCode.Unauthorized or
31
+ HttpStatusCode.Forbidden => new PrivateSourceAuthenticationFailure(NuGetContext.GetPackageSourceUrls(currentDirectory)),
32
+ _ => new UnknownError(ex, jobId),
33
+ },
34
+ InvalidProjectFileException invalidProjectFile => new DependencyFileNotParseable(invalidProjectFile.ProjectFile),
35
+ MissingFileException missingFile => new DependencyFileNotFound(missingFile.FilePath, missingFile.Message),
36
+ UnparseableFileException unparseableFile => new DependencyFileNotParseable(unparseableFile.FilePath, unparseableFile.Message),
37
+ UpdateNotPossibleException updateNotPossible => new UpdateNotPossible(updateNotPossible.Dependencies),
38
+ _ => new UnknownError(ex, jobId),
39
+ };
40
+ }
18
41
  }
@@ -5,9 +5,6 @@ public record JobRepoNotFound : JobErrorBase
5
5
  public JobRepoNotFound(string message)
6
6
  : base("job_repo_not_found")
7
7
  {
8
- Details = new Dictionary<string, string>()
9
- {
10
- ["message"] = message
11
- };
8
+ Details["message"] = message;
12
9
  }
13
10
  }
@@ -5,6 +5,6 @@ public record PrivateSourceAuthenticationFailure : JobErrorBase
5
5
  public PrivateSourceAuthenticationFailure(string[] urls)
6
6
  : base("private_source_authentication_failure")
7
7
  {
8
- Details = $"({string.Join("|", urls)})";
8
+ Details["source"] = $"({string.Join("|", urls)})";
9
9
  }
10
10
  }
@@ -2,9 +2,13 @@ namespace NuGetUpdater.Core.Run.ApiModel;
2
2
 
3
3
  public record UnknownError : JobErrorBase
4
4
  {
5
- public UnknownError(string details)
5
+ public UnknownError(Exception ex, string jobId)
6
6
  : base("unknown_error")
7
7
  {
8
- Details = details;
8
+ Details["error-class"] = ex.GetType().Name;
9
+ Details["error-message"] = ex.Message;
10
+ Details["error-backtrace"] = ex.StackTrace ?? "<unknown>";
11
+ Details["package-manager"] = "nuget";
12
+ Details["job-id"] = jobId;
9
13
  }
10
14
  }
@@ -5,6 +5,6 @@ public record UpdateNotPossible : JobErrorBase
5
5
  public UpdateNotPossible(string[] dependencies)
6
6
  : base("update_not_possible")
7
7
  {
8
- Details = dependencies;
8
+ Details["dependencies"] = dependencies;
9
9
  }
10
10
  }
@@ -12,6 +12,7 @@ namespace NuGetUpdater.Core.Run;
12
12
 
13
13
  public class RunWorker
14
14
  {
15
+ private readonly string _jobId;
15
16
  private readonly IApiHandler _apiHandler;
16
17
  private readonly ILogger _logger;
17
18
  private readonly IDiscoveryWorker _discoveryWorker;
@@ -25,8 +26,9 @@ public class RunWorker
25
26
  Converters = { new JsonStringEnumConverter(), new RequirementConverter(), new VersionConverter() },
26
27
  };
27
28
 
28
- public RunWorker(IApiHandler apiHandler, IDiscoveryWorker discoverWorker, IAnalyzeWorker analyzeWorker, IUpdaterWorker updateWorker, ILogger logger)
29
+ public RunWorker(string jobId, IApiHandler apiHandler, IDiscoveryWorker discoverWorker, IAnalyzeWorker analyzeWorker, IUpdaterWorker updateWorker, ILogger logger)
29
30
  {
31
+ _jobId = jobId;
30
32
  _apiHandler = apiHandler;
31
33
  _logger = logger;
32
34
  _discoveryWorker = discoverWorker;
@@ -51,7 +53,7 @@ public class RunWorker
51
53
  private async Task<RunResult> RunWithErrorHandlingAsync(Job job, DirectoryInfo repoContentsPath, string baseCommitSha)
52
54
  {
53
55
  JobErrorBase? error = null;
54
- string[] lastUsedPackageSourceUrls = []; // used for error reporting below
56
+ var currentDirectory = repoContentsPath.FullName; // used for error reporting below
55
57
  var runResult = new RunResult()
56
58
  {
57
59
  Base64DependencyFiles = [],
@@ -67,7 +69,7 @@ public class RunWorker
67
69
  foreach (var directory in job.GetAllDirectories())
68
70
  {
69
71
  var localPath = PathHelper.JoinPath(repoContentsPath.FullName, directory);
70
- lastUsedPackageSourceUrls = NuGetContext.GetPackageSourceUrls(localPath);
72
+ currentDirectory = localPath;
71
73
  var result = await RunForDirectory(job, repoContentsPath, directory, baseCommitSha, experimentsManager);
72
74
  foreach (var dependencyFile in result.Base64DependencyFiles)
73
75
  {
@@ -82,22 +84,9 @@ public class RunWorker
82
84
  BaseCommitSha = baseCommitSha,
83
85
  };
84
86
  }
85
- catch (HttpRequestException ex)
86
- when (ex.StatusCode == HttpStatusCode.Unauthorized || ex.StatusCode == HttpStatusCode.Forbidden)
87
- {
88
- error = new PrivateSourceAuthenticationFailure(lastUsedPackageSourceUrls);
89
- }
90
- catch (MissingFileException ex)
91
- {
92
- error = new DependencyFileNotFound(ex.FilePath);
93
- }
94
- catch (UpdateNotPossibleException ex)
95
- {
96
- error = new UpdateNotPossible(ex.Dependencies);
97
- }
98
87
  catch (Exception ex)
99
88
  {
100
- error = new UnknownError(ex.ToString());
89
+ error = JobErrorBase.ErrorFromException(ex, _jobId, currentDirectory);
101
90
  }
102
91
 
103
92
  if (error is not null)
@@ -117,6 +106,8 @@ public class RunWorker
117
106
  _logger.Info("Discovery JSON content:");
118
107
  _logger.Info(JsonSerializer.Serialize(discoveryResult, DiscoveryWorker.SerializerOptions));
119
108
 
109
+ // TODO: report errors
110
+
120
111
  // report dependencies
121
112
  var discoveredUpdatedDependencies = GetUpdatedDependencyListFromDiscovery(discoveryResult, repoContentsPath.FullName);
122
113
  await _apiHandler.UpdateDependencyList(discoveredUpdatedDependencies);
@@ -215,7 +206,7 @@ public class RunWorker
215
206
  var dependencyFilePath = Path.Join(discoveryResult.Path, project.FilePath).FullyNormalizedRootedPath();
216
207
  var updateResult = await _updaterWorker.RunAsync(repoContentsPath.FullName, dependencyFilePath, dependency.Name, dependency.Version!, analysisResult.UpdatedVersion, isTransitive: false);
217
208
  // TODO: need to report if anything was actually updated
218
- if (updateResult.ErrorType is null || updateResult.ErrorType == ErrorType.None)
209
+ if (updateResult.Error is null)
219
210
  {
220
211
  if (dependencyLocation != dependencyFilePath)
221
212
  {
@@ -0,0 +1,12 @@
1
+ namespace NuGetUpdater.Core;
2
+
3
+ internal class UnparseableFileException : Exception
4
+ {
5
+ public string FilePath { get; }
6
+
7
+ public UnparseableFileException(string message, string filePath)
8
+ : base(message)
9
+ {
10
+ FilePath = filePath;
11
+ }
12
+ }
@@ -11,7 +11,7 @@ internal static class LockFileUpdater
11
11
  var projectDirectory = Path.GetDirectoryName(projectPath)!;
12
12
  await MSBuildHelper.HandleGlobalJsonAsync(projectDirectory, repoRootPath, experimentsManager, async () =>
13
13
  {
14
- var (exitCode, stdout, stderr) = await ProcessEx.RunDotnetWithoutMSBuildEnvironmentVariablesAsync(["restore", "--force-evaluate", projectPath], projectDirectory, experimentsManager);
14
+ var (exitCode, stdout, stderr) = await ProcessEx.RunDotnetWithoutMSBuildEnvironmentVariablesAsync(["restore", "--force-evaluate", "-p:EnableWindowsTargeting=true", projectPath], projectDirectory, experimentsManager);
15
15
  if (exitCode != 0)
16
16
  {
17
17
  logger.Error($" Lock file update failed.\nSTDOUT:\n{stdout}\nSTDERR:\n{stderr}");
@@ -4,6 +4,8 @@ using Microsoft.Language.Xml;
4
4
 
5
5
  using NuGet.Versioning;
6
6
 
7
+ using NuGetUpdater.Core.Utilities;
8
+
7
9
  namespace NuGetUpdater.Core;
8
10
 
9
11
  /// <summary>
@@ -36,6 +38,25 @@ internal static class PackageReferenceUpdater
36
38
 
37
39
  // Get the set of all top-level dependencies in the current project
38
40
  var topLevelDependencies = MSBuildHelper.GetTopLevelPackageDependencyInfos(buildFiles).ToArray();
41
+ var isDependencyTopLevel = topLevelDependencies.Any(d => d.Name.Equals(dependencyName, StringComparison.OrdinalIgnoreCase));
42
+ if (isDependencyTopLevel)
43
+ {
44
+ var packageMapper = DotNetPackageCorrelationManager.GetPackageMapper();
45
+ // TODO: this is slow
46
+ var isSdkReplacementPackage = packageMapper.RuntimePackages.Runtimes.Any(r =>
47
+ {
48
+ return r.Value.Packages.Any(p => dependencyName.Equals(p.Key, StringComparison.Ordinal));
49
+ });
50
+ if (isSdkReplacementPackage)
51
+ {
52
+ // If we're updating a top level SDK replacement package, the version listed in the project file won't
53
+ // necessarily match the resolved version that caused the update because the SDK might have replaced
54
+ // the package. To handle this scenario, we pretend the version we're searching for is the actual
55
+ // version in the file, not the resolved version. This allows us to keep a strict equality check when
56
+ // finding the file to update.
57
+ previousDependencyVersion = topLevelDependencies.First(d => d.Name.Equals(dependencyName, StringComparison.OrdinalIgnoreCase)).Version!;
58
+ }
59
+ }
39
60
 
40
61
  if (!await DoesDependencyRequireUpdateAsync(repoRootPath, projectPath, tfms, topLevelDependencies, dependencyName, newDependencyVersion, experimentsManager, logger))
41
62
  {
@@ -3,6 +3,7 @@ using System.Text.Json;
3
3
  using System.Text.Json.Serialization;
4
4
 
5
5
  using NuGetUpdater.Core.Analyze;
6
+ using NuGetUpdater.Core.Run.ApiModel;
6
7
  using NuGetUpdater.Core.Updater;
7
8
  using NuGetUpdater.Core.Utilities;
8
9
 
@@ -10,6 +11,7 @@ namespace NuGetUpdater.Core;
10
11
 
11
12
  public class UpdaterWorker : IUpdaterWorker
12
13
  {
14
+ private readonly string _jobId;
13
15
  private readonly ExperimentsManager _experimentsManager;
14
16
  private readonly ILogger _logger;
15
17
  private readonly HashSet<string> _processedProjectPaths = new(StringComparer.OrdinalIgnoreCase);
@@ -20,8 +22,9 @@ public class UpdaterWorker : IUpdaterWorker
20
22
  Converters = { new JsonStringEnumConverter() },
21
23
  };
22
24
 
23
- public UpdaterWorker(ExperimentsManager experimentsManager, ILogger logger)
25
+ public UpdaterWorker(string jobId, ExperimentsManager experimentsManager, ILogger logger)
24
26
  {
27
+ _jobId = jobId;
25
28
  _experimentsManager = experimentsManager;
26
29
  _logger = logger;
27
30
  }
@@ -43,42 +46,15 @@ public class UpdaterWorker : IUpdaterWorker
43
46
  {
44
47
  result = await RunAsync(repoRootPath, workspacePath, dependencyName, previousDependencyVersion, newDependencyVersion, isTransitive);
45
48
  }
46
- catch (HttpRequestException ex)
47
- when (ex.StatusCode == HttpStatusCode.Unauthorized || ex.StatusCode == HttpStatusCode.Forbidden)
49
+ catch (Exception ex)
48
50
  {
49
51
  if (!Path.IsPathRooted(workspacePath) || !File.Exists(workspacePath))
50
52
  {
51
53
  workspacePath = Path.GetFullPath(Path.Join(repoRootPath, workspacePath));
52
54
  }
53
-
54
- result = new()
55
- {
56
- ErrorType = ErrorType.AuthenticationFailure,
57
- ErrorDetails = "(" + string.Join("|", NuGetContext.GetPackageSourceUrls(workspacePath)) + ")",
58
- };
59
- }
60
- catch (MissingFileException ex)
61
- {
62
- result = new()
63
- {
64
- ErrorType = ErrorType.MissingFile,
65
- ErrorDetails = ex.FilePath,
66
- };
67
- }
68
- catch (UpdateNotPossibleException ex)
69
- {
70
- result = new()
71
- {
72
- ErrorType = ErrorType.UpdateNotPossible,
73
- ErrorDetails = ex.Dependencies,
74
- };
75
- }
76
- catch (Exception ex)
77
- {
78
55
  result = new()
79
56
  {
80
- ErrorType = ErrorType.Unknown,
81
- ErrorDetails = ex.ToString(),
57
+ Error = JobErrorBase.ErrorFromException(ex, _jobId, workspacePath),
82
58
  };
83
59
  }
84
60
 
@@ -13,7 +13,18 @@ namespace NuGetUpdater.Core.Updater
13
13
  getContent: () => File.ReadAllText(projectFilePath),
14
14
  setContent: s => File.WriteAllText(projectFilePath, s),
15
15
  nodeFinder: doc => doc.Descendants()
16
- .FirstOrDefault(e => e.Name == "Import" && e.GetAttributeValue("Project") == @"$(VSToolsPath)\WebApplications\Microsoft.WebApplication.targets")
16
+ .Where(e => e.Name == "Import")
17
+ .FirstOrDefault(e =>
18
+ {
19
+ var projectPath = e.GetAttributeValue("Project");
20
+ if (projectPath is not null)
21
+ {
22
+ var projectFileName = Path.GetFileName(projectPath.NormalizePathToUnix());
23
+ return projectFileName.Equals("Microsoft.WebApplication.targets", StringComparison.OrdinalIgnoreCase);
24
+ }
25
+
26
+ return false;
27
+ })
17
28
  as XmlNodeSyntax,
18
29
  preProcessor: n =>
19
30
  {
@@ -395,7 +395,6 @@ public class PackageManager
395
395
  if (await AreAllParentsCompatibleAsync(existingPackages, existingPackage, targetFramework, projectDirectory, logger) == true)
396
396
  {
397
397
  existingPackage.CurrentVersion = dependencyOldVersion;
398
- string NewVersion = dependency.CurrentVersion;
399
398
  existingPackage.NewVersion = dependency.CurrentVersion;
400
399
  await UpdateVersion(existingPackages, existingPackage, targetFramework, projectDirectory, logger);
401
400
  }
@@ -593,12 +592,6 @@ public class PackageManager
593
592
  return null;
594
593
  }
595
594
 
596
- // If the current version of the parent is less than the current version of the dependency
597
- else if (CurrentVersion < currentVersionDependency)
598
- {
599
- return currentVersionDependency;
600
- }
601
-
602
595
  // Loop from the current version to the latest version, use next patch as a limit (unless there's a limit) so it doesn't look for versions that don't exist
603
596
  for (NuGetVersion version = CurrentVersion; version <= latestVersion; version = NextPatch(version, versions))
604
597
  {
@@ -0,0 +1,46 @@
1
+ using System.Text.Json;
2
+
3
+ using DotNetPackageCorrelation;
4
+
5
+ using NuGetUpdater.Core.Discover;
6
+
7
+ namespace NuGetUpdater.Core.Utilities;
8
+
9
+ internal static class DotNetPackageCorrelationManager
10
+ {
11
+ private static readonly PackageMapper _packageMapper;
12
+ private static readonly Dictionary<string, PackageMapper> _packageMapperByOverrideFile = new();
13
+
14
+ static DotNetPackageCorrelationManager()
15
+ {
16
+ var packageCorrelationPath = Path.Combine(Path.GetDirectoryName(typeof(SdkProjectDiscovery).Assembly.Location)!, "dotnet-package-correlation.json");
17
+ var runtimePackages = LoadRuntimePackagesFromFile(packageCorrelationPath);
18
+ _packageMapper = PackageMapper.Load(runtimePackages);
19
+ }
20
+
21
+ public static PackageMapper GetPackageMapper()
22
+ {
23
+ var packageCorrelationFileOverride = Environment.GetEnvironmentVariable("DOTNET_PACKAGE_CORRELATION_FILE_PATH");
24
+ if (packageCorrelationFileOverride is not null)
25
+ {
26
+ // this is used as a test hook to allow unit tests to be SDK agnostic
27
+ if (_packageMapperByOverrideFile.TryGetValue(packageCorrelationFileOverride, out var packageMapper))
28
+ {
29
+ return packageMapper;
30
+ }
31
+
32
+ var runtimePackages = LoadRuntimePackagesFromFile(packageCorrelationFileOverride);
33
+ packageMapper = PackageMapper.Load(runtimePackages);
34
+ _packageMapperByOverrideFile[packageCorrelationFileOverride] = packageMapper;
35
+ return packageMapper;
36
+ }
37
+
38
+ return _packageMapper;
39
+ }
40
+
41
+ private static RuntimePackages LoadRuntimePackagesFromFile(string filePath)
42
+ {
43
+ var packageCorrelationJson = File.ReadAllText(filePath);
44
+ return JsonSerializer.Deserialize<RuntimePackages>(packageCorrelationJson, Correlator.SerializerOptions)!;
45
+ }
46
+ }
@@ -19,6 +19,7 @@ using NuGet.Frameworks;
19
19
  using NuGet.Versioning;
20
20
 
21
21
  using NuGetUpdater.Core.Analyze;
22
+ using NuGetUpdater.Core.Discover;
22
23
  using NuGetUpdater.Core.Utilities;
23
24
 
24
25
  namespace NuGetUpdater.Core;
@@ -342,7 +343,7 @@ internal static partial class MSBuildHelper
342
343
  var tempDirectory = Directory.CreateTempSubdirectory("package-dependency-coherence_");
343
344
  try
344
345
  {
345
- var tempProjectPath = await CreateTempProjectAsync(tempDirectory, repoRoot, projectPath, targetFramework, packages, logger);
346
+ var tempProjectPath = await CreateTempProjectAsync(tempDirectory, repoRoot, projectPath, targetFramework, packages, experimentsManager, logger);
346
347
  var (exitCode, stdOut, stdErr) = await ProcessEx.RunDotnetWithoutMSBuildEnvironmentVariablesAsync(["restore", tempProjectPath], tempDirectory.FullName, experimentsManager);
347
348
 
348
349
  // NU1608: Detected package version outside of dependency constraint
@@ -362,7 +363,7 @@ internal static partial class MSBuildHelper
362
363
 
363
364
  try
364
365
  {
365
- string tempProjectPath = await CreateTempProjectAsync(tempDirectory, repoRoot, projectPath, targetFramework, packages, logger);
366
+ string tempProjectPath = await CreateTempProjectAsync(tempDirectory, repoRoot, projectPath, targetFramework, packages, experimentsManager, logger);
366
367
  var (exitCode, stdOut, stdErr) = await ProcessEx.RunDotnetWithoutMSBuildEnvironmentVariablesAsync(["restore", tempProjectPath], tempDirectory.FullName, experimentsManager);
367
368
 
368
369
  // Add Dependency[] packages to List<PackageToUpdate> existingPackages
@@ -518,7 +519,7 @@ internal static partial class MSBuildHelper
518
519
  var tempDirectory = Directory.CreateTempSubdirectory("package-dependency-coherence_");
519
520
  try
520
521
  {
521
- var tempProjectPath = await CreateTempProjectAsync(tempDirectory, repoRoot, projectPath, targetFramework, packages, logger);
522
+ var tempProjectPath = await CreateTempProjectAsync(tempDirectory, repoRoot, projectPath, targetFramework, packages, experimentsManager, logger);
522
523
  var (exitCode, stdOut, stdErr) = await ProcessEx.RunDotnetWithoutMSBuildEnvironmentVariablesAsync(["restore", tempProjectPath], tempDirectory.FullName, experimentsManager);
523
524
  ThrowOnUnauthenticatedFeed(stdOut);
524
525
 
@@ -668,12 +669,22 @@ internal static partial class MSBuildHelper
668
669
  string projectPath,
669
670
  string targetFramework,
670
671
  IReadOnlyCollection<Dependency> packages,
672
+ ExperimentsManager experimentsManager,
671
673
  ILogger logger,
672
674
  bool usePackageDownload = false)
673
675
  {
674
676
  var projectDirectory = Path.GetDirectoryName(projectPath);
675
677
  projectDirectory ??= repoRoot;
676
- var topLevelFiles = Directory.GetFiles(repoRoot);
678
+
679
+ if (experimentsManager.InstallDotnetSdks)
680
+ {
681
+ var globalJsonPath = PathHelper.GetFileInDirectoryOrParent(projectPath, repoRoot, "global.json", caseSensitive: true);
682
+ if (globalJsonPath is not null)
683
+ {
684
+ File.Copy(globalJsonPath, Path.Combine(tempDir.FullName, "global.json"));
685
+ }
686
+ }
687
+
677
688
  var nugetConfigPath = PathHelper.GetFileInDirectoryOrParent(projectPath, repoRoot, "NuGet.Config", caseSensitive: false);
678
689
  if (nugetConfigPath is not null)
679
690
  {
@@ -832,40 +843,53 @@ internal static partial class MSBuildHelper
832
843
  string targetFramework,
833
844
  IReadOnlyCollection<Dependency> packages,
834
845
  ExperimentsManager experimentsManager,
835
- ILogger logger)
846
+ ILogger logger
847
+ )
836
848
  {
837
849
  var tempDirectory = Directory.CreateTempSubdirectory("package-dependency-resolution_");
838
850
  try
839
851
  {
840
852
  var topLevelPackagesNames = packages.Select(p => p.Name).ToHashSet(StringComparer.OrdinalIgnoreCase);
841
- var tempProjectPath = await CreateTempProjectAsync(tempDirectory, repoRoot, projectPath, targetFramework, packages, logger);
842
-
843
- var (exitCode, stdout, stderr) = await ProcessEx.RunDotnetWithoutMSBuildEnvironmentVariablesAsync(["build", tempProjectPath, "/t:_ReportDependencies"], tempDirectory.FullName, experimentsManager);
844
- ThrowOnUnauthenticatedFeed(stdout);
853
+ var tempProjectPath = await CreateTempProjectAsync(tempDirectory, repoRoot, projectPath, targetFramework, packages, experimentsManager, logger);
845
854
 
846
- if (exitCode == 0)
855
+ Dependency[] allDependencies;
856
+ if (experimentsManager.UseDirectDiscovery)
847
857
  {
848
- ImmutableArray<string> tfms = [targetFramework];
849
- var lines = stdout.Split('\n').Select(line => line.Trim());
850
- var pattern = PackagePattern();
851
- var allDependencies = lines
852
- .Select(line => pattern.Match(line))
853
- .Where(match => match.Success)
854
- .Select(match =>
855
- {
856
- var PackageName = match.Groups["PackageName"].Value;
857
- var isTransitive = !topLevelPackagesNames.Contains(PackageName);
858
- return new Dependency(PackageName, match.Groups["PackageVersion"].Value, DependencyType.Unknown, TargetFrameworks: tfms, IsTransitive: isTransitive);
859
- })
860
- .ToArray();
861
-
862
- return allDependencies;
858
+ var projectDiscovery = await SdkProjectDiscovery.DiscoverAsync(repoRoot, tempDirectory.FullName, tempProjectPath, experimentsManager, logger);
859
+ allDependencies = projectDiscovery
860
+ .Where(p => p.FilePath == Path.GetFileName(tempProjectPath))
861
+ .FirstOrDefault()
862
+ ?.Dependencies.ToArray() ?? [];
863
863
  }
864
864
  else
865
865
  {
866
- logger?.Warn($"dotnet build in {nameof(GetAllPackageDependenciesAsync)} failed. STDOUT: {stdout} STDERR: {stderr}");
867
- return [];
866
+ var (exitCode, stdout, stderr) = await ProcessEx.RunDotnetWithoutMSBuildEnvironmentVariablesAsync(["build", tempProjectPath, "/t:_ReportDependencies"], tempDirectory.FullName, experimentsManager);
867
+ ThrowOnUnauthenticatedFeed(stdout);
868
+
869
+ if (exitCode == 0)
870
+ {
871
+ ImmutableArray<string> tfms = [targetFramework];
872
+ var lines = stdout.Split('\n').Select(line => line.Trim());
873
+ var pattern = PackagePattern();
874
+ allDependencies = lines
875
+ .Select(line => pattern.Match(line))
876
+ .Where(match => match.Success)
877
+ .Select(match =>
878
+ {
879
+ var PackageName = match.Groups["PackageName"].Value;
880
+ var isTransitive = !topLevelPackagesNames.Contains(PackageName);
881
+ return new Dependency(PackageName, match.Groups["PackageVersion"].Value, DependencyType.Unknown, TargetFrameworks: tfms, IsTransitive: isTransitive);
882
+ })
883
+ .ToArray();
884
+ }
885
+ else
886
+ {
887
+ logger?.Warn($"dotnet build in {nameof(GetAllPackageDependenciesAsync)} failed. STDOUT: {stdout} STDERR: {stderr}");
888
+ allDependencies = [];
889
+ }
868
890
  }
891
+
892
+ return allDependencies;
869
893
  }
870
894
  finally
871
895
  {
@@ -886,6 +910,7 @@ internal static partial class MSBuildHelper
886
910
  "The plugin credential provider could not acquire credentials",
887
911
  "401 (Unauthorized)",
888
912
  "error NU1301: Unable to load the service index for source",
913
+ "Response status code does not indicate success: 403",
889
914
  };
890
915
  if (unauthorizedMessageSnippets.Any(stdout.Contains))
891
916
  {
@@ -895,9 +920,13 @@ internal static partial class MSBuildHelper
895
920
 
896
921
  internal static string? GetMissingFile(string output)
897
922
  {
898
- var missingFilePattern = new Regex(@"The imported project \""(?<FilePath>.*)\"" was not found");
899
- var match = missingFilePattern.Match(output);
900
- if (match.Success)
923
+ var missingFilePatterns = new[]
924
+ {
925
+ new Regex(@"The imported project \""(?<FilePath>.*)\"" was not found"),
926
+ new Regex(@"The imported file \""(?<FilePath>.*)\"" does not exist"),
927
+ };
928
+ var match = missingFilePatterns.Select(p => p.Match(output)).Where(m => m.Success).FirstOrDefault();
929
+ if (match is not null)
901
930
  {
902
931
  return match.Groups["FilePath"].Value;
903
932
  }
@@ -9,7 +9,7 @@ internal static class NuGetHelper
9
9
  var tempDirectory = Directory.CreateTempSubdirectory("msbuild_sdk_restore_");
10
10
  try
11
11
  {
12
- var tempProjectPath = await MSBuildHelper.CreateTempProjectAsync(tempDirectory, repoRoot, projectPath, "netstandard2.0", packages, logger, usePackageDownload: true);
12
+ var tempProjectPath = await MSBuildHelper.CreateTempProjectAsync(tempDirectory, repoRoot, projectPath, "netstandard2.0", packages, experimentsManager, logger, usePackageDownload: true);
13
13
  var (exitCode, stdOut, stdErr) = await ProcessEx.RunDotnetWithoutMSBuildEnvironmentVariablesAsync(["restore", tempProjectPath], tempDirectory.FullName, experimentsManager);
14
14
 
15
15
  return exitCode == 0;