dependabot-nuget 0.292.0 → 0.293.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 (70) hide show
  1. checksums.yaml +4 -4
  2. data/helpers/lib/NuGetUpdater/.gitignore +1 -0
  3. data/helpers/lib/NuGetUpdater/Directory.Packages.props +1 -0
  4. data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation/Correlator.cs +197 -0
  5. data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation/DotNetPackageCorrelation.csproj +12 -0
  6. data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation/Model/PackageMapper.cs +68 -0
  7. data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation/Model/PackageSet.cs +11 -0
  8. data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation/Model/Release.cs +25 -0
  9. data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation/Model/ReleasesFile.cs +9 -0
  10. data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation/Model/RuntimePackages.cs +11 -0
  11. data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation/Model/Sdk.cs +13 -0
  12. data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation/Model/SemVerComparer.cs +16 -0
  13. data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation/Model/SemVersionConverter.cs +42 -0
  14. data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation.Cli/DotNetPackageCorrelation.Cli.csproj +16 -0
  15. data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation.Cli/Program.cs +32 -0
  16. data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation.Test/CorrelatorTests.cs +99 -0
  17. data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation.Test/DotNetPackageCorrelation.Test.csproj +18 -0
  18. data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation.Test/EndToEndTests.cs +30 -0
  19. data/helpers/lib/NuGetUpdater/DotNetPackageCorrelation.Test/RuntimePackagesTests.cs +206 -0
  20. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/AnalyzeCommand.cs +6 -4
  21. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/DiscoverCommand.cs +8 -7
  22. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/RunCommand.cs +4 -4
  23. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/UpdateCommand.cs +17 -5
  24. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Analyze.cs +7 -1
  25. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Discover.cs +46 -6
  26. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Update.cs +8 -0
  27. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/AnalyzeWorker.cs +8 -17
  28. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/DependencyFinder.cs +4 -4
  29. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/Extensions.cs +1 -1
  30. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Clone/CloneWorker.cs +2 -1
  31. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/DependencyDiscovery.targets +2 -2
  32. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/DiscoveryWorker.cs +7 -20
  33. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/ProjectDiscoveryResult.cs +3 -3
  34. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/SdkProjectDiscovery.cs +99 -2
  35. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/EnsureDotNetPackageCorrelation.targets +25 -0
  36. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/ExperimentsManager.cs +9 -22
  37. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Files/PackagesConfigBuildFile.cs +5 -1
  38. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/MissingFileException.cs +2 -1
  39. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/NativeResult.cs +3 -3
  40. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/NuGetUpdater.Core.csproj +3 -0
  41. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/DependencyFileNotFound.cs +7 -3
  42. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/DependencyFileNotParseable.cs +15 -0
  43. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/JobErrorBase.cs +24 -0
  44. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/RunWorker.cs +6 -21
  45. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/UnparseableFileException.cs +12 -0
  46. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackageReferenceUpdater.cs +21 -0
  47. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/UpdaterWorker.cs +6 -30
  48. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/DotNetPackageCorrelationManager.cs +46 -0
  49. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +51 -27
  50. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/NuGetHelper.cs +1 -1
  51. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/AnalyzeWorkerTestBase.cs +15 -4
  52. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/AnalyzeWorkerTests.cs +15 -9
  53. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTestBase.cs +20 -12
  54. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.Project.cs +108 -0
  55. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.cs +16 -12
  56. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/ExpectedDiscoveryResults.cs +1 -1
  57. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/MockNuGetPackage.cs +15 -28
  58. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/RunWorkerTests.cs +5 -4
  59. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/SerializationTests.cs +9 -1
  60. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/TestBase.cs +24 -0
  61. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/ExpectedUpdateOperationResult.cs +1 -1
  62. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTestBase.cs +11 -15
  63. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.DirsProj.cs +1 -1
  64. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.Mixed.cs +14 -8
  65. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackageReference.cs +148 -3
  66. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackagesConfig.cs +12 -14
  67. data/helpers/lib/NuGetUpdater/NuGetUpdater.sln +18 -1
  68. data/lib/dependabot/nuget/native_helpers.rb +41 -16
  69. metadata +25 -6
  70. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/ErrorType.cs +0 -12
@@ -0,0 +1,206 @@
1
+ using Semver;
2
+
3
+ using Xunit;
4
+
5
+ namespace DotNetPackageCorrelation;
6
+
7
+ public class RuntimePackagesTests
8
+ {
9
+ [Theory]
10
+ [MemberData(nameof(CorrelatedPackageCanBeFoundData))]
11
+ public void CorrelatedPackageCanBeFound(RuntimePackages runtimePackages, string runtimePackageName, string runtimePackageVersion, string candidatePackageName, string? expectedPackageVersion)
12
+ {
13
+ var packageMapper = PackageMapper.Load(runtimePackages);
14
+ var actualPackageVersion = packageMapper.GetPackageVersionThatShippedWithOtherPackage(runtimePackageName, SemVersion.Parse(runtimePackageVersion), candidatePackageName);
15
+ if (expectedPackageVersion is null)
16
+ {
17
+ Assert.Null(actualPackageVersion);
18
+ }
19
+ else
20
+ {
21
+ Assert.NotNull(actualPackageVersion);
22
+ Assert.Equal(expectedPackageVersion, actualPackageVersion.ToString());
23
+ }
24
+ }
25
+
26
+ public static IEnumerable<object?[]> CorrelatedPackageCanBeFoundData()
27
+ {
28
+ // package not found in specified runtime, but it is in earlier runtime; more recent runtime has that package, but that's not returned
29
+ yield return
30
+ [
31
+ // runtimePackages
32
+ new RuntimePackages()
33
+ {
34
+ Runtimes = new SortedDictionary<SemVersion, PackageSet>(SemVerComparer.Instance)
35
+ {
36
+ {
37
+ SemVersion.Parse("1.0.100"),
38
+ new PackageSet()
39
+ {
40
+ Packages = new SortedDictionary<string, SemVersion>(StringComparer.OrdinalIgnoreCase)
41
+ {
42
+ { "Runtime.Package", SemVersion.Parse("1.0.0") },
43
+ { "Some.Package", SemVersion.Parse("1.0.1") }
44
+ }
45
+ }
46
+ },
47
+ {
48
+ SemVersion.Parse("1.0.101"),
49
+ new PackageSet()
50
+ {
51
+ Packages = new SortedDictionary<string, SemVersion>(StringComparer.OrdinalIgnoreCase)
52
+ {
53
+ // this runtime didn't ship with a new version of "Some.Package", but the earlier release did
54
+ { "Runtime.Package", SemVersion.Parse("1.0.1") }
55
+ }
56
+ }
57
+ },
58
+ {
59
+ SemVersion.Parse("1.0.200"),
60
+ new PackageSet()
61
+ {
62
+ Packages = new SortedDictionary<string, SemVersion>(StringComparer.OrdinalIgnoreCase)
63
+ {
64
+ // the requested package shipped with this runtime, but this runtime isn't the correct version so it's not returned
65
+ { "Runtime.Package", SemVersion.Parse("1.0.2") },
66
+ { "Some.Package", SemVersion.Parse("1.0.2") }
67
+ }
68
+ }
69
+ },
70
+ }
71
+ },
72
+ // runtimePackageName
73
+ "Runtime.Package",
74
+ // runtimePackageVersion
75
+ "1.0.1",
76
+ // candidatePackageName
77
+ "Some.Package",
78
+ // expectedPackageVersion
79
+ "1.0.1"
80
+ ];
81
+
82
+ // package differing in case is found
83
+ yield return
84
+ [
85
+ // runtimePackages
86
+ new RuntimePackages()
87
+ {
88
+ Runtimes = new SortedDictionary<SemVersion, PackageSet>(SemVerComparer.Instance)
89
+ {
90
+ {
91
+ SemVersion.Parse("1.0.100"),
92
+ new PackageSet()
93
+ {
94
+ Packages = new SortedDictionary<string, SemVersion>(StringComparer.OrdinalIgnoreCase)
95
+ {
96
+ { "runtime.package", SemVersion.Parse("1.0.0") },
97
+ { "some.package", SemVersion.Parse("1.0.1") }
98
+ }
99
+ }
100
+ }
101
+ }
102
+ },
103
+ // runtimePackageName
104
+ "Runtime.Package",
105
+ // runtimePackageVersion
106
+ "1.0.0",
107
+ // candidatePackageName
108
+ "Some.Package",
109
+ // expectedPackageVersion
110
+ "1.0.1"
111
+ ];
112
+
113
+ // runtime package not found by name
114
+ yield return
115
+ [
116
+ // runtimePackages
117
+ new RuntimePackages()
118
+ {
119
+ Runtimes = new SortedDictionary<SemVersion, PackageSet>(SemVerComparer.Instance)
120
+ {
121
+ {
122
+ SemVersion.Parse("1.0.100"),
123
+ new PackageSet()
124
+ {
125
+ Packages = new SortedDictionary<string, SemVersion>(StringComparer.OrdinalIgnoreCase)
126
+ {
127
+ { "runtime.package", SemVersion.Parse("1.0.0") },
128
+ { "some.package", SemVersion.Parse("1.0.1") }
129
+ }
130
+ }
131
+ }
132
+ }
133
+ },
134
+ // runtimePackageName
135
+ "Different.Runtime.Package",
136
+ // runtimePackageVersion
137
+ "1.0.0",
138
+ // candidatePackageName
139
+ "Some.Package",
140
+ // expectedPackageVersion
141
+ null
142
+ ];
143
+
144
+ // runtime package not found by version
145
+ yield return
146
+ [
147
+ // runtimePackages
148
+ new RuntimePackages()
149
+ {
150
+ Runtimes = new SortedDictionary<SemVersion, PackageSet>(SemVerComparer.Instance)
151
+ {
152
+ {
153
+ SemVersion.Parse("1.0.100"),
154
+ new PackageSet()
155
+ {
156
+ Packages = new SortedDictionary<string, SemVersion>(StringComparer.OrdinalIgnoreCase)
157
+ {
158
+ { "runtime.package", SemVersion.Parse("1.0.0") },
159
+ { "some.package", SemVersion.Parse("1.0.1") }
160
+ }
161
+ }
162
+ }
163
+ }
164
+ },
165
+ // runtimePackageName
166
+ "Runtime.Package",
167
+ // runtimePackageVersion
168
+ "9.9.9",
169
+ // candidatePackageName
170
+ "Some.Package",
171
+ // expectedPackageVersion
172
+ null
173
+ ];
174
+
175
+ // candidate package not found
176
+ yield return
177
+ [
178
+ // runtimePackages
179
+ new RuntimePackages()
180
+ {
181
+ Runtimes = new SortedDictionary<SemVersion, PackageSet>(SemVerComparer.Instance)
182
+ {
183
+ {
184
+ SemVersion.Parse("1.0.100"),
185
+ new PackageSet()
186
+ {
187
+ Packages = new SortedDictionary<string, SemVersion>(StringComparer.OrdinalIgnoreCase)
188
+ {
189
+ { "runtime.package", SemVersion.Parse("1.0.0") },
190
+ { "some.package", SemVersion.Parse("1.0.1") }
191
+ }
192
+ }
193
+ }
194
+ }
195
+ },
196
+ // runtimePackageName
197
+ "Runtime.Package",
198
+ // runtimePackageVersion
199
+ "1.0.0",
200
+ // candidatePackageName
201
+ "Package.Not.In.This.Runtime",
202
+ // expectedPackageVersion
203
+ null
204
+ ];
205
+ }
206
+ }
@@ -7,6 +7,7 @@ namespace NuGetUpdater.Cli.Commands;
7
7
 
8
8
  internal static class AnalyzeCommand
9
9
  {
10
+ internal static readonly Option<string> JobIdOption = new("--job-id") { IsRequired = true };
10
11
  internal static readonly Option<FileInfo> JobPathOption = new("--job-path") { IsRequired = true };
11
12
  internal static readonly Option<DirectoryInfo> RepoRootOption = new("--repo-root") { IsRequired = true };
12
13
  internal static readonly Option<FileInfo> DependencyFilePathOption = new("--dependency-file-path") { IsRequired = true };
@@ -17,6 +18,7 @@ internal static class AnalyzeCommand
17
18
  {
18
19
  Command command = new("analyze", "Determines how to update a dependency based on the workspace discovery information.")
19
20
  {
21
+ JobIdOption,
20
22
  JobPathOption,
21
23
  RepoRootOption,
22
24
  DependencyFilePathOption,
@@ -26,13 +28,13 @@ internal static class AnalyzeCommand
26
28
 
27
29
  command.TreatUnmatchedTokensAsErrors = true;
28
30
 
29
- command.SetHandler(async (jobPath, repoRoot, discoveryPath, dependencyPath, analysisDirectory) =>
31
+ command.SetHandler(async (jobId, jobPath, repoRoot, discoveryPath, dependencyPath, analysisDirectory) =>
30
32
  {
31
33
  var logger = new ConsoleLogger();
32
- var (experimentsManager, _errorResult) = await ExperimentsManager.FromJobFileAsync(jobPath.FullName);
33
- var worker = new AnalyzeWorker(experimentsManager, logger);
34
+ var (experimentsManager, _errorResult) = await ExperimentsManager.FromJobFileAsync(jobId, jobPath.FullName);
35
+ var worker = new AnalyzeWorker(jobId, experimentsManager, logger);
34
36
  await worker.RunAsync(repoRoot.FullName, discoveryPath.FullName, dependencyPath.FullName, analysisDirectory.FullName);
35
- }, JobPathOption, RepoRootOption, DiscoveryFilePathOption, DependencyFilePathOption, AnalysisFolderOption);
37
+ }, JobIdOption, JobPathOption, RepoRootOption, DiscoveryFilePathOption, DependencyFilePathOption, AnalysisFolderOption);
36
38
 
37
39
  return command;
38
40
  }
@@ -7,6 +7,7 @@ namespace NuGetUpdater.Cli.Commands;
7
7
 
8
8
  internal static class DiscoverCommand
9
9
  {
10
+ internal static readonly Option<string> JobIdOption = new("--job-id") { IsRequired = true };
10
11
  internal static readonly Option<FileInfo> JobPathOption = new("--job-path") { IsRequired = true };
11
12
  internal static readonly Option<DirectoryInfo> RepoRootOption = new("--repo-root") { IsRequired = true };
12
13
  internal static readonly Option<string> WorkspaceOption = new("--workspace") { IsRequired = true };
@@ -16,6 +17,7 @@ internal static class DiscoverCommand
16
17
  {
17
18
  Command command = new("discover", "Generates a report of the workspace dependencies and where they are located.")
18
19
  {
20
+ JobIdOption,
19
21
  JobPathOption,
20
22
  RepoRootOption,
21
23
  WorkspaceOption,
@@ -24,27 +26,26 @@ internal static class DiscoverCommand
24
26
 
25
27
  command.TreatUnmatchedTokensAsErrors = true;
26
28
 
27
- command.SetHandler(async (jobPath, repoRoot, workspace, outputPath) =>
29
+ command.SetHandler(async (jobId, jobPath, repoRoot, workspace, outputPath) =>
28
30
  {
29
- var (experimentsManager, errorResult) = await ExperimentsManager.FromJobFileAsync(jobPath.FullName);
30
- if (errorResult is not null)
31
+ var (experimentsManager, error) = await ExperimentsManager.FromJobFileAsync(jobId, jobPath.FullName);
32
+ if (error is not null)
31
33
  {
32
34
  // to make testing easier, this should be a `WorkspaceDiscoveryResult` object
33
35
  var discoveryErrorResult = new WorkspaceDiscoveryResult
34
36
  {
37
+ Error = error,
35
38
  Path = workspace,
36
39
  Projects = [],
37
- ErrorType = errorResult.ErrorType,
38
- ErrorDetails = errorResult.ErrorDetails,
39
40
  };
40
41
  await DiscoveryWorker.WriteResultsAsync(repoRoot.FullName, outputPath.FullName, discoveryErrorResult);
41
42
  return;
42
43
  }
43
44
 
44
45
  var logger = new ConsoleLogger();
45
- var worker = new DiscoveryWorker(experimentsManager, logger);
46
+ var worker = new DiscoveryWorker(jobId, experimentsManager, logger);
46
47
  await worker.RunAsync(repoRoot.FullName, workspace, outputPath.FullName);
47
- }, JobPathOption, RepoRootOption, WorkspaceOption, OutputOption);
48
+ }, JobIdOption, JobPathOption, RepoRootOption, WorkspaceOption, OutputOption);
48
49
 
49
50
  return command;
50
51
  }
@@ -33,11 +33,11 @@ internal static class RunCommand
33
33
  command.SetHandler(async (jobPath, repoContentsPath, apiUrl, jobId, outputPath, baseCommitSha) =>
34
34
  {
35
35
  var apiHandler = new HttpApiHandler(apiUrl.ToString(), jobId);
36
- var (experimentsManager, _errorResult) = await ExperimentsManager.FromJobFileAsync(jobPath.FullName);
36
+ var (experimentsManager, _errorResult) = await ExperimentsManager.FromJobFileAsync(jobId, jobPath.FullName);
37
37
  var logger = new ConsoleLogger();
38
- var discoverWorker = new DiscoveryWorker(experimentsManager, logger);
39
- var analyzeWorker = new AnalyzeWorker(experimentsManager, logger);
40
- var updateWorker = new UpdaterWorker(experimentsManager, logger);
38
+ var discoverWorker = new DiscoveryWorker(jobId, experimentsManager, logger);
39
+ var analyzeWorker = new AnalyzeWorker(jobId, experimentsManager, logger);
40
+ var updateWorker = new UpdaterWorker(jobId, experimentsManager, logger);
41
41
  var worker = new RunWorker(jobId, apiHandler, discoverWorker, analyzeWorker, updateWorker, logger);
42
42
  await worker.RunAsync(jobPath, repoContentsPath, baseCommitSha, outputPath);
43
43
  }, JobPathOption, RepoContentsPathOption, ApiUrlOption, JobIdOption, OutputPathOption, BaseCommitShaOption);
@@ -6,6 +6,7 @@ namespace NuGetUpdater.Cli.Commands;
6
6
 
7
7
  internal static class UpdateCommand
8
8
  {
9
+ internal static readonly Option<string> JobIdOption = new("--job-id") { IsRequired = true };
9
10
  internal static readonly Option<FileInfo> JobPathOption = new("--job-path") { IsRequired = true };
10
11
  internal static readonly Option<DirectoryInfo> RepoRootOption = new("--repo-root", () => new DirectoryInfo(Environment.CurrentDirectory)) { IsRequired = false };
11
12
  internal static readonly Option<FileInfo> SolutionOrProjectFileOption = new("--solution-or-project") { IsRequired = true };
@@ -19,6 +20,7 @@ internal static class UpdateCommand
19
20
  {
20
21
  Command command = new("update", "Applies the changes from an analysis report to update a dependency.")
21
22
  {
23
+ JobIdOption,
22
24
  JobPathOption,
23
25
  RepoRootOption,
24
26
  SolutionOrProjectFileOption,
@@ -30,15 +32,25 @@ internal static class UpdateCommand
30
32
  };
31
33
 
32
34
  command.TreatUnmatchedTokensAsErrors = true;
33
-
34
- command.SetHandler(async (jobPath, repoRoot, solutionOrProjectFile, dependencyName, newVersion, previousVersion, isTransitive, resultOutputPath) =>
35
+ command.SetHandler(async (context) =>
35
36
  {
36
- var (experimentsManager, _errorResult) = await ExperimentsManager.FromJobFileAsync(jobPath.FullName);
37
+ // since we have more than 8 arguments, we have to pull them out manually
38
+ var jobId = context.ParseResult.GetValueForOption(JobIdOption)!;
39
+ var jobPath = context.ParseResult.GetValueForOption(JobPathOption)!;
40
+ var repoRoot = context.ParseResult.GetValueForOption(RepoRootOption)!;
41
+ var solutionOrProjectFile = context.ParseResult.GetValueForOption(SolutionOrProjectFileOption)!;
42
+ var dependencyName = context.ParseResult.GetValueForOption(DependencyNameOption)!;
43
+ var newVersion = context.ParseResult.GetValueForOption(NewVersionOption)!;
44
+ var previousVersion = context.ParseResult.GetValueForOption(PreviousVersionOption)!;
45
+ var isTransitive = context.ParseResult.GetValueForOption(IsTransitiveOption);
46
+ var resultOutputPath = context.ParseResult.GetValueForOption(ResultOutputPathOption);
47
+
48
+ var (experimentsManager, _error) = await ExperimentsManager.FromJobFileAsync(jobId, jobPath.FullName);
37
49
  var logger = new ConsoleLogger();
38
- var worker = new UpdaterWorker(experimentsManager, logger);
50
+ var worker = new UpdaterWorker(jobId, experimentsManager, logger);
39
51
  await worker.RunAsync(repoRoot.FullName, solutionOrProjectFile.FullName, dependencyName, previousVersion, newVersion, isTransitive, resultOutputPath);
40
52
  setExitCode(0);
41
- }, JobPathOption, RepoRootOption, SolutionOrProjectFileOption, DependencyNameOption, NewVersionOption, PreviousVersionOption, IsTransitiveOption, ResultOutputPathOption);
53
+ });
42
54
 
43
55
  return command;
44
56
  }
@@ -26,6 +26,8 @@ public partial class EntryPointTests
26
26
  await RunAsync(path =>
27
27
  [
28
28
  "analyze",
29
+ "--job-id",
30
+ "TEST-JOB-ID",
29
31
  "--job-path",
30
32
  Path.Combine(path, "job.json"),
31
33
  "--repo-root",
@@ -146,6 +148,8 @@ public partial class EntryPointTests
146
148
  await RunAsync(path =>
147
149
  [
148
150
  "analyze",
151
+ "--job-id",
152
+ "TEST-JOB-ID",
149
153
  "--job-path",
150
154
  Path.Combine(path, "job.json"),
151
155
  "--repo-root",
@@ -235,6 +239,8 @@ public partial class EntryPointTests
235
239
  await RunAsync(path =>
236
240
  [
237
241
  "analyze",
242
+ "--job-id",
243
+ "TEST-JOB-ID",
238
244
  "--job-path",
239
245
  Path.Combine(path, "job.json"),
240
246
  "--repo-root",
@@ -345,7 +351,7 @@ public partial class EntryPointTests
345
351
  {
346
352
  if (args[i] == "--job-path")
347
353
  {
348
- var experimentsResult = await ExperimentsManager.FromJobFileAsync(args[i + 1]);
354
+ var experimentsResult = await ExperimentsManager.FromJobFileAsync("TEST-JOB-ID", args[i + 1]);
349
355
  experimentsManager = experimentsResult.ExperimentsManager;
350
356
  }
351
357
  }
@@ -1,5 +1,6 @@
1
1
  using System.Text;
2
2
  using System.Text.Json;
3
+ using System.Text.Json.Serialization;
3
4
 
4
5
  using NuGetUpdater.Core;
5
6
  using NuGetUpdater.Core.Discover;
@@ -25,6 +26,8 @@ public partial class EntryPointTests
25
26
  await RunAsync(path =>
26
27
  [
27
28
  "discover",
29
+ "--job-id",
30
+ "TEST-JOB-ID",
28
31
  "--job-path",
29
32
  Path.Combine(path, "job.json"),
30
33
  "--repo-root",
@@ -83,6 +86,8 @@ public partial class EntryPointTests
83
86
  await RunAsync(path =>
84
87
  [
85
88
  "discover",
89
+ "--job-id",
90
+ "TEST-JOB-ID",
86
91
  "--job-path",
87
92
  Path.Combine(path, "job.json"),
88
93
  "--repo-root",
@@ -178,6 +183,8 @@ public partial class EntryPointTests
178
183
  await RunAsync(path =>
179
184
  [
180
185
  "discover",
186
+ "--job-id",
187
+ "TEST-JOB-ID",
181
188
  "--job-path",
182
189
  Path.Combine(path, "job.json"),
183
190
  "--repo-root",
@@ -251,6 +258,8 @@ public partial class EntryPointTests
251
258
  await RunAsync(path =>
252
259
  [
253
260
  "discover",
261
+ "--job-id",
262
+ "TEST-JOB-ID",
254
263
  "--job-path",
255
264
  Path.Combine(path, "job.json"),
256
265
  "--repo-root",
@@ -321,6 +330,8 @@ public partial class EntryPointTests
321
330
  await RunAsync(path =>
322
331
  [
323
332
  "discover",
333
+ "--job-id",
334
+ "TEST-JOB-ID",
324
335
  "--job-path",
325
336
  Path.Combine(path, "job.json"),
326
337
  "--repo-root",
@@ -398,6 +409,8 @@ public partial class EntryPointTests
398
409
  await RunAsync(path =>
399
410
  [
400
411
  "discover",
412
+ "--job-id",
413
+ "TEST-JOB-ID",
401
414
  "--job-path",
402
415
  jobFilePath,
403
416
  "--repo-root",
@@ -412,8 +425,7 @@ public partial class EntryPointTests
412
425
  {
413
426
  Path = "/",
414
427
  Projects = [],
415
- ErrorType = ErrorType.Unknown,
416
- ErrorDetailsPattern = "JsonException",
428
+ ErrorRegex = "Error deserializing job file contents",
417
429
  }
418
430
  );
419
431
  }
@@ -445,6 +457,8 @@ public partial class EntryPointTests
445
457
  await RunAsync(path =>
446
458
  [
447
459
  "discover",
460
+ "--job-id",
461
+ "TEST-JOB-ID",
448
462
  "--job-path",
449
463
  jobFilePath,
450
464
  "--repo-root",
@@ -459,8 +473,7 @@ public partial class EntryPointTests
459
473
  {
460
474
  Path = "/",
461
475
  Projects = [],
462
- ErrorType = ErrorType.BadRequirement,
463
- ErrorDetailsPattern = "not a valid requirement",
476
+ Error = new Core.Run.ApiModel.BadRequirement("not a valid requirement"),
464
477
  }
465
478
  );
466
479
  }
@@ -497,7 +510,7 @@ public partial class EntryPointTests
497
510
  switch (args[i])
498
511
  {
499
512
  case "--job-path":
500
- var experimentsResult = await ExperimentsManager.FromJobFileAsync(args[i + 1]);
513
+ var experimentsResult = await ExperimentsManager.FromJobFileAsync("TEST-JOB-ID", args[i + 1]);
501
514
  experimentsManager = experimentsResult.ExperimentsManager;
502
515
  break;
503
516
  case "--output":
@@ -520,11 +533,38 @@ public partial class EntryPointTests
520
533
 
521
534
  resultPath ??= Path.Join(path, DiscoveryWorker.DiscoveryResultFileName);
522
535
  var resultJson = await File.ReadAllTextAsync(resultPath);
523
- var resultObject = JsonSerializer.Deserialize<WorkspaceDiscoveryResult>(resultJson, DiscoveryWorker.SerializerOptions);
536
+ var serializerOptions = new JsonSerializerOptions()
537
+ {
538
+ Converters = { new TestJobErrorBaseConverter() }
539
+ };
540
+ foreach (var converter in DiscoveryWorker.SerializerOptions.Converters)
541
+ {
542
+ serializerOptions.Converters.Add(converter);
543
+ }
544
+ var resultObject = JsonSerializer.Deserialize<WorkspaceDiscoveryResult>(resultJson, serializerOptions);
524
545
  return resultObject!;
525
546
  });
526
547
 
527
548
  ValidateWorkspaceResult(expectedResult, actualResult, experimentsManager);
528
549
  }
550
+
551
+ private class TestJobErrorBaseConverter : JsonConverter<Core.Run.ApiModel.JobErrorBase>
552
+ {
553
+ public override Core.Run.ApiModel.JobErrorBase? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
554
+ {
555
+ var dict = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(ref reader, options)!;
556
+ return dict["error-type"].GetString() switch
557
+ {
558
+ "illformed_requirement" => new Core.Run.ApiModel.BadRequirement(dict["error-details"].GetProperty("message").GetString()!),
559
+ "unknown_error" => new Core.Run.ApiModel.UnknownError(new Exception("Error deserializing job file contents"), "TEST-JOB-ID"),
560
+ _ => throw new NotImplementedException($"Unknown error type: {dict["error-type"]}"),
561
+ };
562
+ }
563
+
564
+ public override void Write(Utf8JsonWriter writer, Core.Run.ApiModel.JobErrorBase value, JsonSerializerOptions options)
565
+ {
566
+ throw new NotImplementedException();
567
+ }
568
+ }
529
569
  }
530
570
  }
@@ -19,6 +19,8 @@ public partial class EntryPointTests
19
19
  await Run(path =>
20
20
  [
21
21
  "update",
22
+ "--job-id",
23
+ "TEST-JOB-ID",
22
24
  "--job-path",
23
25
  Path.Combine(path, "job.json"),
24
26
  "--repo-root",
@@ -122,6 +124,8 @@ public partial class EntryPointTests
122
124
  await Run(path =>
123
125
  [
124
126
  "update",
127
+ "--job-id",
128
+ "TEST-JOB-ID",
125
129
  "--job-path",
126
130
  Path.Combine(path, "job.json"),
127
131
  "--repo-root",
@@ -202,6 +206,8 @@ public partial class EntryPointTests
202
206
  await Run(path =>
203
207
  [
204
208
  "update",
209
+ "--job-id",
210
+ "TEST-JOB-ID",
205
211
  "--job-path",
206
212
  Path.Combine(path, "job.json"),
207
213
  "--repo-root",
@@ -361,6 +367,8 @@ public partial class EntryPointTests
361
367
  IEnumerable<string> executableArgs = [
362
368
  executableName,
363
369
  "update",
370
+ "--job-id",
371
+ "TEST-JOB-ID",
364
372
  "--job-path",
365
373
  Path.Combine(tempDir.DirectoryPath, "job.json"),
366
374
  "--repo-root",
@@ -7,6 +7,7 @@ using NuGet.Frameworks;
7
7
  using NuGet.Versioning;
8
8
 
9
9
  using NuGetUpdater.Core.Discover;
10
+ using NuGetUpdater.Core.Run.ApiModel;
10
11
 
11
12
  namespace NuGetUpdater.Core.Analyze;
12
13
 
@@ -16,6 +17,7 @@ public partial class AnalyzeWorker : IAnalyzeWorker
16
17
  {
17
18
  public const string AnalysisDirectoryName = "./.dependabot/analysis";
18
19
 
20
+ private readonly string _jobId;
19
21
  private readonly ExperimentsManager _experimentsManager;
20
22
  private readonly ILogger _logger;
21
23
 
@@ -25,8 +27,9 @@ public partial class AnalyzeWorker : IAnalyzeWorker
25
27
  Converters = { new JsonStringEnumConverter(), new RequirementArrayConverter() },
26
28
  };
27
29
 
28
- public AnalyzeWorker(ExperimentsManager experimentsManager, ILogger logger)
30
+ public AnalyzeWorker(string jobId, ExperimentsManager experimentsManager, ILogger logger)
29
31
  {
32
+ _jobId = jobId;
30
33
  _experimentsManager = experimentsManager;
31
34
  _logger = logger;
32
35
  }
@@ -48,26 +51,11 @@ public partial class AnalyzeWorker : IAnalyzeWorker
48
51
  {
49
52
  analysisResult = await RunAsync(repoRoot, discovery, dependencyInfo);
50
53
  }
51
- catch (HttpRequestException ex)
52
- when (ex.StatusCode == HttpStatusCode.Unauthorized || ex.StatusCode == HttpStatusCode.Forbidden)
53
- {
54
- var localPath = PathHelper.JoinPath(repoRoot, discovery.Path);
55
- using var nugetContext = new NuGetContext(localPath);
56
- analysisResult = new AnalysisResult
57
- {
58
- ErrorType = ErrorType.AuthenticationFailure,
59
- ErrorDetails = "(" + string.Join("|", nugetContext.PackageSources.Select(s => s.Source)) + ")",
60
- UpdatedVersion = string.Empty,
61
- CanUpdate = false,
62
- UpdatedDependencies = [],
63
- };
64
- }
65
54
  catch (Exception ex)
66
55
  {
67
56
  analysisResult = new AnalysisResult
68
57
  {
69
- ErrorType = ErrorType.Unknown,
70
- ErrorDetails = ex.ToString(),
58
+ Error = JobErrorBase.ErrorFromException(ex, _jobId, PathHelper.JoinPath(repoRoot, discovery.Path)),
71
59
  UpdatedVersion = string.Empty,
72
60
  CanUpdate = false,
73
61
  UpdatedDependencies = [],
@@ -79,6 +67,8 @@ public partial class AnalyzeWorker : IAnalyzeWorker
79
67
 
80
68
  public async Task<AnalysisResult> RunAsync(string repoRoot, WorkspaceDiscoveryResult discovery, DependencyInfo dependencyInfo)
81
69
  {
70
+ MSBuildHelper.RegisterMSBuild(repoRoot, repoRoot);
71
+
82
72
  var startingDirectory = PathHelper.JoinPath(repoRoot, discovery.Path);
83
73
 
84
74
  _logger.Info($"Starting analysis of {dependencyInfo.Name}...");
@@ -423,6 +413,7 @@ public partial class AnalyzeWorker : IAnalyzeWorker
423
413
  .SelectMany(p => p.TargetFrameworks)
424
414
  .Select(NuGetFramework.Parse)
425
415
  .Distinct()
416
+ .Select(f => f.GetShortFolderName())
426
417
  .ToImmutableArray();
427
418
 
428
419
  // When updating peer dependencies, we only need to consider top-level dependencies.
@@ -7,10 +7,10 @@ namespace NuGetUpdater.Core.Analyze;
7
7
 
8
8
  internal static class DependencyFinder
9
9
  {
10
- public static async Task<ImmutableDictionary<NuGetFramework, ImmutableArray<Dependency>>> GetDependenciesAsync(
10
+ public static async Task<ImmutableDictionary<string, ImmutableArray<Dependency>>> GetDependenciesAsync(
11
11
  string repoRoot,
12
12
  string projectPath,
13
- IEnumerable<NuGetFramework> frameworks,
13
+ IEnumerable<string> frameworks,
14
14
  ImmutableHashSet<string> packageIds,
15
15
  NuGetVersion version,
16
16
  NuGetContext nugetContext,
@@ -23,13 +23,13 @@ internal static class DependencyFinder
23
23
  .Select(id => new Dependency(id, versionString, DependencyType.Unknown))
24
24
  .ToImmutableArray();
25
25
 
26
- var result = ImmutableDictionary.CreateBuilder<NuGetFramework, ImmutableArray<Dependency>>();
26
+ var result = ImmutableDictionary.CreateBuilder<string, ImmutableArray<Dependency>>();
27
27
  foreach (var framework in frameworks)
28
28
  {
29
29
  var dependencies = await MSBuildHelper.GetAllPackageDependenciesAsync(
30
30
  repoRoot,
31
31
  projectPath,
32
- framework.ToString(),
32
+ framework,
33
33
  packages,
34
34
  experimentsManager,
35
35
  logger);
@@ -7,7 +7,7 @@ using NuGetUpdater.Core;
7
7
 
8
8
  internal static class Extensions
9
9
  {
10
- public static ImmutableArray<Dependency> GetDependencies(this ImmutableDictionary<NuGetFramework, ImmutableArray<Dependency>> dependenciesByTfm)
10
+ public static ImmutableArray<Dependency> GetDependencies(this ImmutableDictionary<string, ImmutableArray<Dependency>> dependenciesByTfm)
11
11
  {
12
12
  Dictionary<string, Dependency> dependencies = [];
13
13
  foreach (var (_framework, dependenciesForTfm) in dependenciesByTfm)