dependabot-nuget 0.291.0 → 0.292.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) 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.Core/Analyze/AnalyzeWorker.cs +11 -0
  13. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/RequirementConverter.cs +19 -1
  14. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/BadRequirementException.cs +9 -0
  15. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Clone/CloneWorker.cs +39 -8
  16. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/DiscoveryWorker.cs +67 -12
  17. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/ErrorType.cs +1 -0
  18. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/ExperimentsManager.cs +28 -5
  19. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/NuGetUpdater.Core.csproj +1 -0
  20. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/BadRequirement.cs +10 -0
  21. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/CommitOptions.cs +1 -1
  22. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/DependencyFileNotFound.cs +3 -2
  23. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/JobErrorBase.cs +1 -2
  24. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/JobRepoNotFound.cs +1 -4
  25. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/PrivateSourceAuthenticationFailure.cs +1 -1
  26. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/UnknownError.cs +6 -2
  27. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/UpdateNotPossible.cs +1 -1
  28. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/RunWorker.cs +9 -3
  29. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/LockFileUpdater.cs +1 -1
  30. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/WebApplicationTargetsConditionPatcher.cs +12 -1
  31. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/DependencyConflictResolver.cs +0 -7
  32. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +8 -3
  33. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/ProjectHelper.cs +4 -4
  34. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Clone/CloneWorkerTests.cs +60 -2
  35. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTestBase.cs +10 -1
  36. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.PackagesConfig.cs +56 -0
  37. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/ExpectedDiscoveryResults.cs +1 -0
  38. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/RunWorkerTests.cs +1 -1
  39. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/SerializationTests.cs +76 -40
  40. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTestBase.cs +20 -2
  41. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.GlobalJson.cs +2 -2
  42. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.LockFile.cs +251 -0
  43. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackageReference.cs +6 -6
  44. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackagesConfig.cs +63 -5
  45. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/MSBuildHelperTests.cs +38 -20
  46. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/ProjectHelperTests.cs +65 -0
  47. data/helpers/lib/NuGetUpdater/global.json +1 -1
  48. data/lib/dependabot/nuget/language.rb +21 -5
  49. data/lib/dependabot/nuget/native_helpers.rb +2 -0
  50. data/lib/dependabot/nuget/package_manager.rb +4 -4
  51. metadata +10 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c3d38c911d42c1014432f79bd79e560129291135bb6f5039ff09e2e9229db253
4
- data.tar.gz: a7417d90f096fb3de7a6a142b2b42c7aac61f299ba57d49ddbe1a1359d4dba3f
3
+ metadata.gz: fc0cf2e8a7f769337787f983af710cd8a4c30c464b1118f22589c522b3ed107a
4
+ data.tar.gz: b1e2fb7957b3cd3daf6331e199ebce5b0f94d84f42033de6e6f0e39bef4295b8
5
5
  SHA512:
6
- metadata.gz: b3cb757cc8d4a2ba7c0e3bc1f22e6d53e06e6de5bae39e460b86f9c697aaef7e95c0f62bf6583a63376c5367a6ec6e820389546f78dc3d7a190a331a1db720c6
7
- data.tar.gz: f51885f03309f8e8d0f7359fffc41305c98e26f00a3e20af3ac67681f20c7e7d3f018e8093f89fe1591ac22a8f22453b49bb427d5a377aedb0a23f1cdfd1bffc
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!;
@@ -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
  }
@@ -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>()
@@ -8,6 +8,8 @@ using Microsoft.Build.Definition;
8
8
  using Microsoft.Build.Evaluation;
9
9
  using Microsoft.Build.Exceptions;
10
10
 
11
+ using NuGet.Frameworks;
12
+
11
13
  using NuGetUpdater.Core.Analyze;
12
14
  using NuGetUpdater.Core.Utilities;
13
15
 
@@ -57,6 +59,16 @@ public partial class DiscoveryWorker : IDiscoveryWorker
57
59
  Projects = [],
58
60
  };
59
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
+ }
60
72
 
61
73
  return result;
62
74
  }
@@ -364,19 +376,62 @@ public partial class DiscoveryWorker : IDiscoveryWorker
364
376
  }
365
377
  }
366
378
 
367
- if (!results.ContainsKey(relativeProjectPath) &&
368
- packagesConfigResult is not null &&
369
- packagesConfigResult.Dependencies.Length > 0)
379
+ if (packagesConfigResult is not null)
370
380
  {
371
- // project contained only packages.config dependencies
372
- 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
373
424
  {
374
- FilePath = relativeProjectPath,
375
- Dependencies = packagesConfigResult.Dependencies,
376
- TargetFrameworks = packagesConfigResult.TargetFrameworks,
377
- ImportedFiles = [], // no imported files resolved for packages.config scenarios
378
- AdditionalFiles = packagesConfigResult.AdditionalFiles,
379
- };
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
+ }
380
435
  }
381
436
  }
382
437
  }
@@ -397,6 +452,6 @@ public partial class DiscoveryWorker : IDiscoveryWorker
397
452
  }
398
453
 
399
454
  var resultJson = JsonSerializer.Serialize(result, SerializerOptions);
400
- await File.WriteAllTextAsync(path: resultPath, resultJson);
455
+ await File.WriteAllTextAsync(resultPath, resultJson);
401
456
  }
402
457
  }
@@ -4,6 +4,7 @@ public enum ErrorType
4
4
  {
5
5
  None,
6
6
  AuthenticationFailure,
7
+ BadRequirement,
7
8
  MissingFile,
8
9
  UpdateNotPossible,
9
10
  DependencyFileNotParseable,
@@ -30,19 +30,42 @@ public record ExperimentsManager
30
30
  };
31
31
  }
32
32
 
33
- public static async Task<ExperimentsManager> FromJobFileAsync(string jobFilePath, ILogger logger)
33
+ public static async Task<(ExperimentsManager ExperimentsManager, NativeResult? ErrorResult)> FromJobFileAsync(string jobFilePath)
34
34
  {
35
- var jobFileContent = await File.ReadAllTextAsync(jobFilePath);
35
+ var experimentsManager = new ExperimentsManager();
36
+ NativeResult? errorResult = null;
36
37
  try
37
38
  {
39
+ var jobFileContent = await File.ReadAllTextAsync(jobFilePath);
38
40
  var jobWrapper = RunWorker.Deserialize(jobFileContent);
39
- return GetExperimentsManager(jobWrapper.Job.Experiments);
41
+ experimentsManager = GetExperimentsManager(jobWrapper.Job.Experiments);
42
+ }
43
+ catch (BadRequirementException ex)
44
+ {
45
+ errorResult = new NativeResult
46
+ {
47
+ ErrorType = ErrorType.BadRequirement,
48
+ ErrorDetails = ex.Message,
49
+ };
40
50
  }
41
51
  catch (JsonException ex)
42
52
  {
43
- logger.Info($"Error deserializing job file: {ex.ToString()}: {jobFileContent}");
44
- return new ExperimentsManager();
53
+ errorResult = new NativeResult
54
+ {
55
+ ErrorType = ErrorType.Unknown,
56
+ ErrorDetails = $"Error deserializing job file: {ex}: {File.ReadAllText(jobFilePath)}",
57
+ };
58
+ }
59
+ catch (Exception ex)
60
+ {
61
+ errorResult = new NativeResult
62
+ {
63
+ ErrorType = ErrorType.Unknown,
64
+ ErrorDetails = ex.ToString(),
65
+ };
45
66
  }
67
+
68
+ return (experimentsManager, errorResult);
46
69
  }
47
70
 
48
71
  private static bool IsEnabled(Dictionary<string, object>? experiments, string experimentName)
@@ -26,6 +26,7 @@
26
26
  </ItemGroup>
27
27
 
28
28
  <ItemGroup>
29
+ <InternalsVisibleTo Include="NuGetUpdater.Cli" />
29
30
  <InternalsVisibleTo Include="NuGetUpdater.Cli.Test" />
30
31
  <InternalsVisibleTo Include="NuGetUpdater.Core.Test" />
31
32
  </ItemGroup>
@@ -0,0 +1,10 @@
1
+ namespace NuGetUpdater.Core.Run.ApiModel;
2
+
3
+ public record BadRequirement : JobErrorBase
4
+ {
5
+ public BadRequirement(string details)
6
+ : base("illformed_requirement")
7
+ {
8
+ Details["message"] = details;
9
+ }
10
+ }
@@ -4,5 +4,5 @@ public record CommitOptions
4
4
  {
5
5
  public string? Prefix { get; init; } = null;
6
6
  public string? PrefixDevelopment { get; init; } = null;
7
- public string? IncludeScope { get; init; } = null;
7
+ public bool? IncludeScope { get; init; } = null;
8
8
  }
@@ -2,9 +2,10 @@ namespace NuGetUpdater.Core.Run.ApiModel;
2
2
 
3
3
  public record DependencyFileNotFound : JobErrorBase
4
4
  {
5
- public DependencyFileNotFound(string filePath)
5
+ public DependencyFileNotFound(string message, string filePath)
6
6
  : base("dependency_file_not_found")
7
7
  {
8
- Details = filePath;
8
+ Details["message"] = message;
9
+ Details["file-path"] = filePath;
9
10
  }
10
11
  }
@@ -13,6 +13,5 @@ public abstract record JobErrorBase
13
13
  public string Type { get; }
14
14
 
15
15
  [JsonPropertyName("error-details")]
16
- [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
17
- public object? Details { get; init; } = null;
16
+ public Dictionary<string, object> Details { get; init; } = new();
18
17
  }
@@ -5,9 +5,6 @@ public record JobRepoNotFound : JobErrorBase
5
5
  public JobRepoNotFound(string message)
6
6
  : base("job_repo_not_found")
7
7
  {
8
- Details = new Dictionary<string, string>()
9
- {
10
- ["message"] = message
11
- };
8
+ Details["message"] = message;
12
9
  }
13
10
  }
@@ -5,6 +5,6 @@ public record PrivateSourceAuthenticationFailure : JobErrorBase
5
5
  public PrivateSourceAuthenticationFailure(string[] urls)
6
6
  : base("private_source_authentication_failure")
7
7
  {
8
- Details = $"({string.Join("|", urls)})";
8
+ Details["source"] = $"({string.Join("|", urls)})";
9
9
  }
10
10
  }
@@ -2,9 +2,13 @@ namespace NuGetUpdater.Core.Run.ApiModel;
2
2
 
3
3
  public record UnknownError : JobErrorBase
4
4
  {
5
- public UnknownError(string details)
5
+ public UnknownError(Exception ex, string jobId)
6
6
  : base("unknown_error")
7
7
  {
8
- Details = details;
8
+ Details["error-class"] = ex.GetType().Name;
9
+ Details["error-message"] = ex.Message;
10
+ Details["error-backtrace"] = ex.StackTrace ?? "<unknown>";
11
+ Details["package-manager"] = "nuget";
12
+ Details["job-id"] = jobId;
9
13
  }
10
14
  }
@@ -5,6 +5,6 @@ public record UpdateNotPossible : JobErrorBase
5
5
  public UpdateNotPossible(string[] dependencies)
6
6
  : base("update_not_possible")
7
7
  {
8
- Details = dependencies;
8
+ Details["dependencies"] = dependencies;
9
9
  }
10
10
  }