dependabot-nuget 0.291.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 (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
  }