dependabot-nuget 0.292.0 → 0.294.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/helpers/lib/NuGetUpdater/.gitignore +1 -0
  3. data/helpers/lib/NuGetUpdater/Directory.Packages.props +2 -1
  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/Analyze/VersionFinder.cs +16 -5
  31. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Clone/CloneWorker.cs +2 -1
  32. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/DependencyDiscovery.targets +2 -2
  33. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/DiscoveryWorker.cs +7 -20
  34. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/ProjectDiscoveryResult.cs +3 -3
  35. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/SdkProjectDiscovery.cs +99 -2
  36. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/EnsureDotNetPackageCorrelation.targets +25 -0
  37. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/ExperimentsManager.cs +9 -22
  38. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Files/PackagesConfigBuildFile.cs +5 -1
  39. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/MissingFileException.cs +2 -1
  40. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/NativeResult.cs +3 -3
  41. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/NuGetUpdater.Core.csproj +3 -0
  42. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/Advisory.cs +2 -0
  43. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/DependencyFileNotFound.cs +7 -3
  44. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/DependencyFileNotParseable.cs +15 -0
  45. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/JobErrorBase.cs +24 -0
  46. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/RunWorker.cs +33 -30
  47. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/UnparseableFileException.cs +12 -0
  48. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackageReferenceUpdater.cs +21 -0
  49. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/UpdaterWorker.cs +6 -30
  50. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/DotNetPackageCorrelationManager.cs +46 -0
  51. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +51 -27
  52. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/NuGetHelper.cs +1 -1
  53. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/AnalyzeWorkerTestBase.cs +15 -4
  54. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/AnalyzeWorkerTests.cs +70 -9
  55. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTestBase.cs +20 -12
  56. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.Project.cs +108 -0
  57. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.cs +16 -12
  58. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/ExpectedDiscoveryResults.cs +1 -1
  59. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/MockNuGetPackage.cs +15 -28
  60. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/MiscellaneousTests.cs +61 -0
  61. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/RunWorkerTests.cs +5 -4
  62. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/SerializationTests.cs +9 -1
  63. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/TestBase.cs +24 -0
  64. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/ExpectedUpdateOperationResult.cs +1 -1
  65. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTestBase.cs +11 -15
  66. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.DirsProj.cs +1 -1
  67. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.Mixed.cs +14 -8
  68. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackageReference.cs +148 -3
  69. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackagesConfig.cs +12 -14
  70. data/helpers/lib/NuGetUpdater/NuGetUpdater.sln +18 -1
  71. data/lib/dependabot/nuget/native_helpers.rb +41 -16
  72. metadata +25 -6
  73. 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)