dependabot-nuget 0.290.0 → 0.292.0

Sign up to get free protection for your applications and to get access to all the features.
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; } = [];