dependabot-nuget 0.290.0 → 0.292.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 (65) hide show
  1. checksums.yaml +4 -4
  2. data/helpers/lib/NuGetUpdater/.editorconfig +1 -0
  3. data/helpers/lib/NuGetUpdater/Directory.Build.props +1 -0
  4. data/helpers/lib/NuGetUpdater/Directory.Packages.props +1 -1
  5. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/AnalyzeCommand.cs +1 -1
  6. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/CloneCommand.cs +1 -1
  7. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/DiscoverCommand.cs +15 -1
  8. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/RunCommand.cs +2 -2
  9. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/UpdateCommand.cs +1 -1
  10. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Analyze.cs +2 -1
  11. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Discover.cs +87 -3
  12. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Update.cs +1 -1
  13. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/AnalyzeWorker.cs +14 -0
  14. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/DependencyFinder.cs +2 -0
  15. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/RequirementConverter.cs +19 -1
  16. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/BadRequirementException.cs +9 -0
  17. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Clone/CloneWorker.cs +39 -8
  18. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/DiscoveryWorker.cs +111 -17
  19. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/PackagesConfigDiscovery.cs +2 -2
  20. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/ProjectDiscoveryResult.cs +2 -0
  21. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/SdkProjectDiscovery.cs +19 -11
  22. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/ErrorType.cs +2 -0
  23. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/ExperimentsManager.cs +31 -5
  24. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/NuGetUpdater.Core.csproj +1 -0
  25. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/BadRequirement.cs +10 -0
  26. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/CommitOptions.cs +1 -1
  27. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/DependencyFileNotFound.cs +3 -2
  28. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/JobErrorBase.cs +1 -2
  29. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/JobRepoNotFound.cs +1 -4
  30. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/PrivateSourceAuthenticationFailure.cs +1 -1
  31. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/UnknownError.cs +6 -2
  32. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/UpdateNotPossible.cs +1 -1
  33. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/RunWorker.cs +9 -3
  34. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/LockFileUpdater.cs +3 -2
  35. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackageReferenceUpdater.cs +43 -18
  36. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/UpdaterWorker.cs +1 -1
  37. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/WebApplicationTargetsConditionPatcher.cs +12 -1
  38. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/DependencyConflictResolver.cs +0 -7
  39. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +48 -17
  40. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/NuGetHelper.cs +2 -2
  41. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/ProcessExtensions.cs +45 -7
  42. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/ProjectHelper.cs +4 -4
  43. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Clone/CloneWorkerTests.cs +60 -2
  44. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTestBase.cs +10 -1
  45. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.PackagesConfig.cs +56 -0
  46. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.cs +41 -0
  47. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/ExpectedDiscoveryResults.cs +2 -0
  48. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/SdkProjectDiscoveryTests.cs +1 -1
  49. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/MockNuGetPackage.cs +2 -1
  50. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/RunWorkerTests.cs +1 -1
  51. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/SerializationTests.cs +76 -40
  52. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTestBase.cs +20 -2
  53. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.GlobalJson.cs +2 -2
  54. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.LockFile.cs +251 -0
  55. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackageReference.cs +6 -6
  56. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackagesConfig.cs +63 -5
  57. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/MSBuildHelperTests.cs +277 -73
  58. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/ProjectHelperTests.cs +65 -0
  59. data/helpers/lib/NuGetUpdater/global.json +1 -1
  60. data/lib/dependabot/nuget/file_fetcher.rb +1 -0
  61. data/lib/dependabot/nuget/file_parser.rb +90 -0
  62. data/lib/dependabot/nuget/language.rb +98 -0
  63. data/lib/dependabot/nuget/native_helpers.rb +25 -0
  64. data/lib/dependabot/nuget/package_manager.rb +51 -0
  65. metadata +12 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b3ca35ae6f3d02ce8507984f89e7c929eb75cec515b39af94db530d6600f0caa
4
- data.tar.gz: cbd548418a3e25163af5d8543109b520b6c9bd9e2bf5a5869421f8fe838128d1
3
+ metadata.gz: fc0cf2e8a7f769337787f983af710cd8a4c30c464b1118f22589c522b3ed107a
4
+ data.tar.gz: b1e2fb7957b3cd3daf6331e199ebce5b0f94d84f42033de6e6f0e39bef4295b8
5
5
  SHA512:
6
- metadata.gz: 9395fb4eea720fbcba3b0cef278344b41a9ace7315ce62f5cb193bb05af2aa23553130dab3112cb900d39ad12cadd321e64291d72e4e21e4bbee2f2b7610626a
7
- data.tar.gz: d74843dc18cab2fd6a58a31f1df2a4172cb308a3a2e452db7056727666e9f153b611046c25bd78f7c0679abe8be4eb57462886d87c8010ba21ac0a2ee316120e
6
+ metadata.gz: 2c66ad9bb9ce3d932d0965f18fc4a6dde14ae022413343689a7c92f1e9439cca523447c20719f579bbc6c5fe6caeadab1dde0056f0a99e4a1afd09c7a5a79266
7
+ data.tar.gz: eb4601daef366ea15b011fb20a3efce1b976da73a4c4ba14c991a1aa9910a01d220b2cc46a086e47e7336432597b01f431d65ad4d40b431b1545996e1af613dd
@@ -26,6 +26,7 @@ end_of_line = lf
26
26
  [*.{cs,vb}]
27
27
 
28
28
  max_line_length = 0
29
+ dotnet_diagnostic.IDE0055.severity = error
29
30
 
30
31
  # Organize usings
31
32
  dotnet_separate_import_directive_groups = true
@@ -4,6 +4,7 @@
4
4
  <CentralPackageTransitivePinningEnabled>true</CentralPackageTransitivePinningEnabled>
5
5
  <NoWarn>$(NoWarn);NU1701</NoWarn>
6
6
  <Nullable>enable</Nullable>
7
+ <EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
7
8
  </PropertyGroup>
8
9
 
9
10
  <Import Project="Directory.Common.props" />
@@ -36,7 +36,7 @@
36
36
  <PackageVersion Include="System.Text.RegularExpressions" Version="4.3.1" />
37
37
  <PackageVersion Include="System.Threading.Tasks.Dataflow" Version="9.0.0" />
38
38
  <PackageVersion Include="xunit" Version="2.9.2" />
39
- <PackageVersion Include="xunit.runner.visualstudio" Version="2.8.2" />
39
+ <PackageVersion Include="xunit.runner.visualstudio" Version="3.0.0" />
40
40
  </ItemGroup>
41
41
 
42
42
  </Project>
@@ -29,7 +29,7 @@ internal static class AnalyzeCommand
29
29
  command.SetHandler(async (jobPath, repoRoot, discoveryPath, dependencyPath, analysisDirectory) =>
30
30
  {
31
31
  var logger = new ConsoleLogger();
32
- var experimentsManager = await ExperimentsManager.FromJobFileAsync(jobPath.FullName, logger);
32
+ var (experimentsManager, _errorResult) = await ExperimentsManager.FromJobFileAsync(jobPath.FullName);
33
33
  var worker = new AnalyzeWorker(experimentsManager, logger);
34
34
  await worker.RunAsync(repoRoot.FullName, discoveryPath.FullName, dependencyPath.FullName, analysisDirectory.FullName);
35
35
  }, JobPathOption, RepoRootOption, DiscoveryFilePathOption, DependencyFilePathOption, AnalysisFolderOption);
@@ -30,7 +30,7 @@ internal static class CloneCommand
30
30
  var apiHandler = new HttpApiHandler(apiUrl.ToString(), jobId);
31
31
  var logger = new ConsoleLogger();
32
32
  var gitCommandHandler = new ShellGitCommandHandler(logger);
33
- var worker = new CloneWorker(apiHandler, gitCommandHandler, logger);
33
+ var worker = new CloneWorker(jobId, apiHandler, gitCommandHandler);
34
34
  var exitCode = await worker.RunAsync(jobPath, repoContentsPath);
35
35
  setExitCode(exitCode);
36
36
  }, JobPathOption, RepoContentsPathOption, ApiUrlOption, JobIdOption);
@@ -26,8 +26,22 @@ internal static class DiscoverCommand
26
26
 
27
27
  command.SetHandler(async (jobPath, repoRoot, workspace, outputPath) =>
28
28
  {
29
+ var (experimentsManager, errorResult) = await ExperimentsManager.FromJobFileAsync(jobPath.FullName);
30
+ if (errorResult is not null)
31
+ {
32
+ // to make testing easier, this should be a `WorkspaceDiscoveryResult` object
33
+ var discoveryErrorResult = new WorkspaceDiscoveryResult
34
+ {
35
+ Path = workspace,
36
+ Projects = [],
37
+ ErrorType = errorResult.ErrorType,
38
+ ErrorDetails = errorResult.ErrorDetails,
39
+ };
40
+ await DiscoveryWorker.WriteResultsAsync(repoRoot.FullName, outputPath.FullName, discoveryErrorResult);
41
+ return;
42
+ }
43
+
29
44
  var logger = new ConsoleLogger();
30
- var experimentsManager = await ExperimentsManager.FromJobFileAsync(jobPath.FullName, logger);
31
45
  var worker = new DiscoveryWorker(experimentsManager, logger);
32
46
  await worker.RunAsync(repoRoot.FullName, workspace, outputPath.FullName);
33
47
  }, JobPathOption, RepoRootOption, WorkspaceOption, OutputOption);
@@ -33,12 +33,12 @@ 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
37
  var logger = new ConsoleLogger();
37
- var experimentsManager = await ExperimentsManager.FromJobFileAsync(jobPath.FullName, logger);
38
38
  var discoverWorker = new DiscoveryWorker(experimentsManager, logger);
39
39
  var analyzeWorker = new AnalyzeWorker(experimentsManager, logger);
40
40
  var updateWorker = new UpdaterWorker(experimentsManager, logger);
41
- var worker = new RunWorker(apiHandler, discoverWorker, analyzeWorker, updateWorker, logger);
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);
44
44
 
@@ -33,8 +33,8 @@ internal static class UpdateCommand
33
33
 
34
34
  command.SetHandler(async (jobPath, repoRoot, solutionOrProjectFile, dependencyName, newVersion, previousVersion, isTransitive, resultOutputPath) =>
35
35
  {
36
+ var (experimentsManager, _errorResult) = await ExperimentsManager.FromJobFileAsync(jobPath.FullName);
36
37
  var logger = new ConsoleLogger();
37
- var experimentsManager = await ExperimentsManager.FromJobFileAsync(jobPath.FullName, logger);
38
38
  var worker = new UpdaterWorker(experimentsManager, logger);
39
39
  await worker.RunAsync(repoRoot.FullName, solutionOrProjectFile.FullName, dependencyName, previousVersion, newVersion, isTransitive, resultOutputPath);
40
40
  setExitCode(0);
@@ -345,7 +345,8 @@ public partial class EntryPointTests
345
345
  {
346
346
  if (args[i] == "--job-path")
347
347
  {
348
- experimentsManager = await ExperimentsManager.FromJobFileAsync(args[i + 1], new TestLogger());
348
+ var experimentsResult = await ExperimentsManager.FromJobFileAsync(args[i + 1]);
349
+ experimentsManager = experimentsResult.ExperimentsManager;
349
350
  }
350
351
  }
351
352
 
@@ -388,6 +388,83 @@ public partial class EntryPointTests
388
388
  );
389
389
  }
390
390
 
391
+ [Fact]
392
+ public async Task JobFileParseErrorIsReported_InvalidJson()
393
+ {
394
+ using var testDirectory = new TemporaryDirectory();
395
+ var jobFilePath = Path.Combine(testDirectory.DirectoryPath, "job.json");
396
+ var resultFilePath = Path.Combine(testDirectory.DirectoryPath, DiscoveryWorker.DiscoveryResultFileName);
397
+ await File.WriteAllTextAsync(jobFilePath, "not json");
398
+ await RunAsync(path =>
399
+ [
400
+ "discover",
401
+ "--job-path",
402
+ jobFilePath,
403
+ "--repo-root",
404
+ path,
405
+ "--workspace",
406
+ "/",
407
+ "--output",
408
+ resultFilePath
409
+ ],
410
+ initialFiles: [],
411
+ expectedResult: new()
412
+ {
413
+ Path = "/",
414
+ Projects = [],
415
+ ErrorType = ErrorType.Unknown,
416
+ ErrorDetailsPattern = "JsonException",
417
+ }
418
+ );
419
+ }
420
+
421
+ [Fact]
422
+ public async Task JobFileParseErrorIsReported_BadRequirement()
423
+ {
424
+ using var testDirectory = new TemporaryDirectory();
425
+ var jobFilePath = Path.Combine(testDirectory.DirectoryPath, "job.json");
426
+ var resultFilePath = Path.Combine(testDirectory.DirectoryPath, DiscoveryWorker.DiscoveryResultFileName);
427
+
428
+ // write a job file with a valid shape, but invalid requirement
429
+ await File.WriteAllTextAsync(jobFilePath, """
430
+ {
431
+ "job": {
432
+ "source": {
433
+ "provider": "github",
434
+ "repo": "test/repo"
435
+ },
436
+ "security-advisories": [
437
+ {
438
+ "dependency-name": "Some.Dependency",
439
+ "affected-versions": ["not a valid requirement"]
440
+ }
441
+ ]
442
+ }
443
+ }
444
+ """);
445
+ await RunAsync(path =>
446
+ [
447
+ "discover",
448
+ "--job-path",
449
+ jobFilePath,
450
+ "--repo-root",
451
+ path,
452
+ "--workspace",
453
+ "/",
454
+ "--output",
455
+ resultFilePath
456
+ ],
457
+ initialFiles: [],
458
+ expectedResult: new()
459
+ {
460
+ Path = "/",
461
+ Projects = [],
462
+ ErrorType = ErrorType.BadRequirement,
463
+ ErrorDetailsPattern = "not a valid requirement",
464
+ }
465
+ );
466
+ }
467
+
391
468
  private static async Task RunAsync(
392
469
  Func<string, string[]> getArgs,
393
470
  TestFile[] initialFiles,
@@ -406,6 +483,7 @@ public partial class EntryPointTests
406
483
  var originalErr = Console.Error;
407
484
  Console.SetOut(writer);
408
485
  Console.SetError(writer);
486
+ string? resultPath = null;
409
487
 
410
488
  try
411
489
  {
@@ -416,9 +494,15 @@ public partial class EntryPointTests
416
494
  // manually pull out the experiments manager for the validate step below
417
495
  for (int i = 0; i < args.Length - 1; i++)
418
496
  {
419
- if (args[i] == "--job-path")
497
+ switch (args[i])
420
498
  {
421
- experimentsManager = await ExperimentsManager.FromJobFileAsync(args[i + 1], new TestLogger());
499
+ case "--job-path":
500
+ var experimentsResult = await ExperimentsManager.FromJobFileAsync(args[i + 1]);
501
+ experimentsManager = experimentsResult.ExperimentsManager;
502
+ break;
503
+ case "--output":
504
+ resultPath = args[i + 1];
505
+ break;
422
506
  }
423
507
  }
424
508
 
@@ -434,7 +518,7 @@ public partial class EntryPointTests
434
518
  Console.SetError(originalErr);
435
519
  }
436
520
 
437
- var resultPath = Path.Join(path, DiscoveryWorker.DiscoveryResultFileName);
521
+ resultPath ??= Path.Join(path, DiscoveryWorker.DiscoveryResultFileName);
438
522
  var resultJson = await File.ReadAllTextAsync(resultPath);
439
523
  var resultObject = JsonSerializer.Deserialize<WorkspaceDiscoveryResult>(resultJson, DiscoveryWorker.SerializerOptions);
440
524
  return resultObject!;
@@ -382,7 +382,7 @@ public partial class EntryPointTests
382
382
  workingDirectory = Path.Join(workingDirectory, workingDirectoryPath);
383
383
  }
384
384
 
385
- var (exitCode, output, error) = await ProcessEx.RunAsync("dotnet", executableArgs, workingDirectory: workingDirectory);
385
+ var (exitCode, output, error) = await ProcessEx.RunDotnetWithoutMSBuildEnvironmentVariablesAsync(executableArgs, workingDirectory, new ExperimentsManager() { InstallDotnetSdks = false });
386
386
  Assert.True(exitCode == 0, $"Error running update on unsupported SDK.\nSTDOUT:\n{output}\nSTDERR:\n{error}");
387
387
 
388
388
  // verify project update
@@ -62,6 +62,17 @@ public partial class AnalyzeWorker : IAnalyzeWorker
62
62
  UpdatedDependencies = [],
63
63
  };
64
64
  }
65
+ catch (Exception ex)
66
+ {
67
+ analysisResult = new AnalysisResult
68
+ {
69
+ ErrorType = ErrorType.Unknown,
70
+ ErrorDetails = ex.ToString(),
71
+ UpdatedVersion = string.Empty,
72
+ CanUpdate = false,
73
+ UpdatedDependencies = [],
74
+ };
75
+ }
65
76
 
66
77
  return analysisResult;
67
78
  }
@@ -142,6 +153,7 @@ public partial class AnalyzeWorker : IAnalyzeWorker
142
153
  dependenciesToUpdate,
143
154
  updatedVersion,
144
155
  nugetContext,
156
+ _experimentsManager,
145
157
  _logger,
146
158
  CancellationToken.None);
147
159
  }
@@ -393,6 +405,7 @@ public partial class AnalyzeWorker : IAnalyzeWorker
393
405
  ImmutableHashSet<string> packageIds,
394
406
  NuGetVersion updatedVersion,
395
407
  NuGetContext nugetContext,
408
+ ExperimentsManager experimentsManager,
396
409
  ILogger logger,
397
410
  CancellationToken cancellationToken)
398
411
  {
@@ -432,6 +445,7 @@ public partial class AnalyzeWorker : IAnalyzeWorker
432
445
  packageIds,
433
446
  updatedVersion,
434
447
  nugetContext,
448
+ experimentsManager,
435
449
  logger,
436
450
  cancellationToken);
437
451
 
@@ -14,6 +14,7 @@ internal static class DependencyFinder
14
14
  ImmutableHashSet<string> packageIds,
15
15
  NuGetVersion version,
16
16
  NuGetContext nugetContext,
17
+ ExperimentsManager experimentsManager,
17
18
  ILogger logger,
18
19
  CancellationToken cancellationToken)
19
20
  {
@@ -30,6 +31,7 @@ internal static class DependencyFinder
30
31
  projectPath,
31
32
  framework.ToString(),
32
33
  packages,
34
+ experimentsManager,
33
35
  logger);
34
36
  var updatedDependencies = new List<Dependency>();
35
37
  foreach (var dependency in dependencies)
@@ -7,7 +7,25 @@ public class RequirementConverter : JsonConverter<Requirement>
7
7
  {
8
8
  public override Requirement? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
9
9
  {
10
- return Requirement.Parse(reader.GetString()!);
10
+ if (reader.TokenType != JsonTokenType.String)
11
+ {
12
+ throw new BadRequirementException($"Expected token type {nameof(JsonTokenType.String)}, but found {reader.TokenType}.");
13
+ }
14
+
15
+ var text = reader.GetString();
16
+ if (text is null)
17
+ {
18
+ throw new BadRequirementException("Unexpected null token.");
19
+ }
20
+
21
+ try
22
+ {
23
+ return Requirement.Parse(text);
24
+ }
25
+ catch
26
+ {
27
+ throw new BadRequirementException(text);
28
+ }
11
29
  }
12
30
 
13
31
  public override void Write(Utf8JsonWriter writer, Requirement value, JsonSerializerOptions options)
@@ -0,0 +1,9 @@
1
+ namespace NuGetUpdater.Core;
2
+
3
+ internal class BadRequirementException : Exception
4
+ {
5
+ public BadRequirementException(string details)
6
+ : base(details)
7
+ {
8
+ }
9
+ }
@@ -9,23 +9,49 @@ namespace NuGetUpdater.Core.Clone;
9
9
 
10
10
  public class CloneWorker
11
11
  {
12
+ private readonly string _jobId;
12
13
  private readonly IApiHandler _apiHandler;
13
14
  private readonly IGitCommandHandler _gitCommandHandler;
14
- private readonly ILogger _logger;
15
15
 
16
- public CloneWorker(IApiHandler apiHandler, IGitCommandHandler gitCommandHandler, ILogger logger)
16
+ public CloneWorker(string jobId, IApiHandler apiHandler, IGitCommandHandler gitCommandHandler)
17
17
  {
18
+ _jobId = jobId;
18
19
  _apiHandler = apiHandler;
19
20
  _gitCommandHandler = gitCommandHandler;
20
- _logger = logger;
21
21
  }
22
22
 
23
23
  // entrypoint for cli
24
24
  public async Task<int> RunAsync(FileInfo jobFilePath, DirectoryInfo repoContentsPath)
25
25
  {
26
26
  var jobFileContent = await File.ReadAllTextAsync(jobFilePath.FullName);
27
- var jobWrapper = RunWorker.Deserialize(jobFileContent);
28
- var result = await RunAsync(jobWrapper.Job, repoContentsPath.FullName);
27
+
28
+ // only a limited set of errors can occur here
29
+ JobFile? jobFile = null;
30
+ JobErrorBase? parseError = null;
31
+ try
32
+ {
33
+ jobFile = RunWorker.Deserialize(jobFileContent);
34
+ if (jobFile is null)
35
+ {
36
+ parseError = new UnknownError(new Exception("Job file could not be deserialized"), _jobId);
37
+ }
38
+ }
39
+ catch (BadRequirementException ex)
40
+ {
41
+ parseError = new BadRequirement(ex.Message);
42
+ }
43
+ catch (Exception ex)
44
+ {
45
+ parseError = new UnknownError(ex, _jobId);
46
+ }
47
+
48
+ if (parseError is not null)
49
+ {
50
+ await ReportError(parseError);
51
+ return 1;
52
+ }
53
+
54
+ var result = await RunAsync(jobFile!.Job, repoContentsPath.FullName);
29
55
  return result;
30
56
  }
31
57
 
@@ -48,19 +74,24 @@ public class CloneWorker
48
74
  }
49
75
  catch (Exception ex)
50
76
  {
51
- error = new UnknownError(ex.ToString());
77
+ error = new UnknownError(ex, _jobId);
52
78
  }
53
79
 
54
80
  if (error is not null)
55
81
  {
56
- await _apiHandler.RecordUpdateJobError(error);
57
- await _apiHandler.MarkAsProcessed(new("unknown"));
82
+ await ReportError(error);
58
83
  return 1;
59
84
  }
60
85
 
61
86
  return 0;
62
87
  }
63
88
 
89
+ private async Task ReportError(JobErrorBase error)
90
+ {
91
+ await _apiHandler.RecordUpdateJobError(error);
92
+ await _apiHandler.MarkAsProcessed(new("unknown"));
93
+ }
94
+
64
95
  internal static CommandArguments[] GetAllCommandArgs(Job job, string repoContentsPath)
65
96
  {
66
97
  var commandArgs = new List<CommandArguments>()
@@ -6,6 +6,9 @@ using System.Text.Json.Serialization;
6
6
  using Microsoft.Build.Construction;
7
7
  using Microsoft.Build.Definition;
8
8
  using Microsoft.Build.Evaluation;
9
+ using Microsoft.Build.Exceptions;
10
+
11
+ using NuGet.Frameworks;
9
12
 
10
13
  using NuGetUpdater.Core.Analyze;
11
14
  using NuGetUpdater.Core.Utilities;
@@ -56,6 +59,16 @@ public partial class DiscoveryWorker : IDiscoveryWorker
56
59
  Projects = [],
57
60
  };
58
61
  }
62
+ catch (Exception ex)
63
+ {
64
+ result = new WorkspaceDiscoveryResult
65
+ {
66
+ ErrorType = ErrorType.Unknown,
67
+ ErrorDetails = ex.ToString(),
68
+ Path = workspacePath,
69
+ Projects = [],
70
+ };
71
+ }
59
72
 
60
73
  return result;
61
74
  }
@@ -93,13 +106,31 @@ public partial class DiscoveryWorker : IDiscoveryWorker
93
106
  }
94
107
 
95
108
  // this next line should throw or something
96
- projectResults = await RunForDirectoryAsnyc(repoRootPath, workspacePath);
109
+ projectResults = await RunForDirectoryAsync(repoRootPath, workspacePath);
97
110
  }
98
111
  else
99
112
  {
100
113
  _logger.Info($"Workspace path [{workspacePath}] does not exist.");
101
114
  }
102
115
 
116
+ //if any projectResults are not successful, return a failed result
117
+ if (projectResults.Any(p => p.IsSuccess == false))
118
+ {
119
+ var failedProjectResult = projectResults.Where(p => p.IsSuccess == false).First();
120
+ var failedDiscoveryResult = new WorkspaceDiscoveryResult
121
+ {
122
+ Path = initialWorkspacePath,
123
+ DotNetToolsJson = null,
124
+ GlobalJson = null,
125
+ Projects = projectResults.Where(p => p.IsSuccess).OrderBy(p => p.FilePath).ToImmutableArray(),
126
+ ErrorType = failedProjectResult.ErrorType,
127
+ ErrorDetails = failedProjectResult.FilePath,
128
+ IsSuccess = false,
129
+ };
130
+
131
+ return failedDiscoveryResult;
132
+ }
133
+
103
134
  result = new WorkspaceDiscoveryResult
104
135
  {
105
136
  Path = initialWorkspacePath,
@@ -137,14 +168,34 @@ public partial class DiscoveryWorker : IDiscoveryWorker
137
168
 
138
169
  _logger.Info($" Restoring MSBuild SDKs: {string.Join(", ", keys)}");
139
170
 
140
- return await NuGetHelper.DownloadNuGetPackagesAsync(repoRootPath, workspacePath, msbuildSdks, logger);
171
+ return await NuGetHelper.DownloadNuGetPackagesAsync(repoRootPath, workspacePath, msbuildSdks, _experimentsManager, logger);
141
172
  }
142
173
 
143
- private async Task<ImmutableArray<ProjectDiscoveryResult>> RunForDirectoryAsnyc(string repoRootPath, string workspacePath)
174
+ private async Task<ImmutableArray<ProjectDiscoveryResult>> RunForDirectoryAsync(string repoRootPath, string workspacePath)
144
175
  {
145
176
  _logger.Info($" Discovering projects beneath [{Path.GetRelativePath(repoRootPath, workspacePath)}].");
146
177
  var entryPoints = FindEntryPoints(workspacePath);
147
- var projects = ExpandEntryPointsIntoProjects(entryPoints);
178
+ ImmutableArray<string> projects;
179
+ try
180
+ {
181
+ projects = ExpandEntryPointsIntoProjects(entryPoints);
182
+ }
183
+ catch (InvalidProjectFileException e)
184
+ {
185
+ var invalidProjectFile = Path.GetRelativePath(workspacePath, e.ProjectFile).NormalizePathToUnix();
186
+
187
+ _logger.Info("Error encountered during discovery: " + e.Message);
188
+ return [new ProjectDiscoveryResult
189
+ {
190
+ FilePath = invalidProjectFile,
191
+ Dependencies = ImmutableArray<Dependency>.Empty,
192
+ ImportedFiles = ImmutableArray<string>.Empty,
193
+ AdditionalFiles = ImmutableArray<string>.Empty,
194
+ IsSuccess = false,
195
+ ErrorType = ErrorType.DependencyFileNotParseable,
196
+ ErrorDetails = "Failed to parse project file found at " + invalidProjectFile,
197
+ }];
198
+ }
148
199
  if (projects.IsEmpty)
149
200
  {
150
201
  _logger.Info(" No project files found.");
@@ -286,7 +337,7 @@ public partial class DiscoveryWorker : IDiscoveryWorker
286
337
  _processedProjectPaths.Add(actualProjectPath);
287
338
 
288
339
  var relativeProjectPath = Path.GetRelativePath(workspacePath, actualProjectPath).NormalizePathToUnix();
289
- var packagesConfigResult = await PackagesConfigDiscovery.Discover(repoRootPath, workspacePath, actualProjectPath, _logger);
340
+ var packagesConfigResult = await PackagesConfigDiscovery.Discover(repoRootPath, workspacePath, actualProjectPath, _experimentsManager, _logger);
290
341
  var projectResults = await SdkProjectDiscovery.DiscoverAsync(repoRootPath, workspacePath, actualProjectPath, _experimentsManager, _logger);
291
342
 
292
343
  // Determine if there were unrestored MSBuildSdks
@@ -325,19 +376,62 @@ public partial class DiscoveryWorker : IDiscoveryWorker
325
376
  }
326
377
  }
327
378
 
328
- if (!results.ContainsKey(relativeProjectPath) &&
329
- packagesConfigResult is not null &&
330
- packagesConfigResult.Dependencies.Length > 0)
379
+ if (packagesConfigResult is not null)
331
380
  {
332
- // project contained only packages.config dependencies
333
- results[relativeProjectPath] = new ProjectDiscoveryResult()
381
+ // we might have to merge this dependency with some others
382
+ if (results.TryGetValue(relativeProjectPath, out var existingProjectDiscovery))
383
+ {
384
+ // merge SDK and packages.config results
385
+ var mergedDependencies = existingProjectDiscovery.Dependencies.Concat(packagesConfigResult.Dependencies)
386
+ .DistinctBy(d => d.Name, StringComparer.OrdinalIgnoreCase)
387
+ .OrderBy(d => d.Name)
388
+ .ToImmutableArray();
389
+ var mergedTargetFrameworks = existingProjectDiscovery.TargetFrameworks.Concat(packagesConfigResult.TargetFrameworks)
390
+ .Select(t =>
391
+ {
392
+ try
393
+ {
394
+ var tfm = NuGetFramework.Parse(t);
395
+ return tfm.GetShortFolderName();
396
+ }
397
+ catch
398
+ {
399
+ return string.Empty;
400
+ }
401
+ })
402
+ .Where(tfm => !string.IsNullOrEmpty(tfm))
403
+ .Distinct()
404
+ .OrderBy(tfm => tfm)
405
+ .ToImmutableArray();
406
+ var mergedProperties = existingProjectDiscovery.Properties; // packages.config discovery doesn't produce properties
407
+ var mergedImportedFiles = existingProjectDiscovery.ImportedFiles; // packages.config discovery doesn't produce imported files
408
+ var mergedAdditionalFiles = existingProjectDiscovery.AdditionalFiles.Concat(packagesConfigResult.AdditionalFiles)
409
+ .Distinct(StringComparer.OrdinalIgnoreCase)
410
+ .OrderBy(f => f)
411
+ .ToImmutableArray();
412
+ var mergedResult = new ProjectDiscoveryResult()
413
+ {
414
+ FilePath = existingProjectDiscovery.FilePath,
415
+ Dependencies = mergedDependencies,
416
+ TargetFrameworks = mergedTargetFrameworks,
417
+ Properties = mergedProperties,
418
+ ImportedFiles = mergedImportedFiles,
419
+ AdditionalFiles = mergedAdditionalFiles,
420
+ };
421
+ results[relativeProjectPath] = mergedResult;
422
+ }
423
+ else
334
424
  {
335
- FilePath = relativeProjectPath,
336
- Dependencies = packagesConfigResult.Dependencies,
337
- TargetFrameworks = packagesConfigResult.TargetFrameworks,
338
- ImportedFiles = [], // no imported files resolved for packages.config scenarios
339
- AdditionalFiles = packagesConfigResult.AdditionalFiles,
340
- };
425
+ // add packages.config results
426
+ results[relativeProjectPath] = new ProjectDiscoveryResult()
427
+ {
428
+ FilePath = relativeProjectPath,
429
+ Dependencies = packagesConfigResult.Dependencies,
430
+ TargetFrameworks = packagesConfigResult.TargetFrameworks,
431
+ ImportedFiles = [], // no imported files resolved for packages.config scenarios
432
+ AdditionalFiles = packagesConfigResult.AdditionalFiles,
433
+ };
434
+ }
341
435
  }
342
436
  }
343
437
  }
@@ -358,6 +452,6 @@ public partial class DiscoveryWorker : IDiscoveryWorker
358
452
  }
359
453
 
360
454
  var resultJson = JsonSerializer.Serialize(result, SerializerOptions);
361
- await File.WriteAllTextAsync(path: resultPath, resultJson);
455
+ await File.WriteAllTextAsync(resultPath, resultJson);
362
456
  }
363
457
  }
@@ -6,7 +6,7 @@ namespace NuGetUpdater.Core.Discover;
6
6
 
7
7
  internal static class PackagesConfigDiscovery
8
8
  {
9
- public static async Task<PackagesConfigDiscoveryResult?> Discover(string repoRootPath, string workspacePath, string projectPath, ILogger logger)
9
+ public static async Task<PackagesConfigDiscoveryResult?> Discover(string repoRootPath, string workspacePath, string projectPath, ExperimentsManager experimentsManager, ILogger logger)
10
10
  {
11
11
  var projectDirectory = Path.GetDirectoryName(projectPath)!;
12
12
  var additionalFiles = ProjectHelper.GetAllAdditionalFilesFromProject(projectPath, ProjectHelper.PathFormat.Full);
@@ -27,7 +27,7 @@ internal static class PackagesConfigDiscovery
27
27
  .ToImmutableArray();
28
28
 
29
29
  // generate `$(TargetFramework)` via MSBuild
30
- var tfms = await MSBuildHelper.GetTargetFrameworkValuesFromProject(repoRootPath, projectPath, logger);
30
+ var tfms = await MSBuildHelper.GetTargetFrameworkValuesFromProject(repoRootPath, projectPath, experimentsManager, logger);
31
31
 
32
32
  var additionalFilesRelative = additionalFiles.Select(p => Path.GetRelativePath(projectDirectory, p).NormalizePathToUnix()).ToImmutableArray();
33
33
  return new()
@@ -8,6 +8,8 @@ public record ProjectDiscoveryResult : IDiscoveryResultWithDependencies
8
8
  public required string FilePath { get; init; }
9
9
  public required ImmutableArray<Dependency> Dependencies { get; init; }
10
10
  public bool IsSuccess { get; init; } = true;
11
+ public string? ErrorDetails { get; init; }
12
+ public ErrorType? ErrorType { get; init; }
11
13
  public ImmutableArray<Property> Properties { get; init; } = [];
12
14
  public ImmutableArray<string> TargetFrameworks { get; init; } = [];
13
15
  public ImmutableArray<string> ReferencedProjectPaths { get; init; } = [];