dependabot-nuget 0.304.0 → 0.305.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 (29) hide show
  1. checksums.yaml +4 -4
  2. data/helpers/lib/NuGetUpdater/Directory.Packages.props +5 -5
  3. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/AnalyzeCommand.cs +1 -1
  4. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/CloneCommand.cs +1 -1
  5. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/DiscoverCommand.cs +2 -2
  6. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/RunCommand.cs +1 -1
  7. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/UpdateCommand.cs +1 -1
  8. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/AnalyzeWorker.cs +1 -1
  9. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/DiscoveryWorker.cs +1 -1
  10. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Files/ProjectBuildFile.cs +3 -1
  11. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/NuGetUpdater.Core.csproj +4 -0
  12. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/JobErrorBase.cs +1 -0
  13. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/PullRequestTextGenerator.cs +13 -12
  14. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/RunWorker.cs +52 -21
  15. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackageReferenceUpdater.cs +13 -2
  16. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/UpdateOperationBase.cs +3 -3
  17. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/UpdaterWorker.cs +1 -1
  18. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +33 -12
  19. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/OpenTelemetryLogger.cs +54 -0
  20. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.cs +77 -0
  21. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/PullRequestMessageTests.cs +45 -2
  22. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/PullRequestTextTests.cs +63 -44
  23. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/RunWorkerTests.cs +56 -8
  24. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/UpdatePermittedAndMessageTests.cs +90 -23
  25. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/TestBase.cs +1 -1
  26. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/PackageReferenceUpdaterTests.cs +60 -0
  27. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/LoggerTests.cs +61 -0
  28. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/MSBuildHelperTests.cs +44 -0
  29. metadata +7 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d547d192189427062cf34249cc425d0f2f9caf8819fd458ff9e2843ef55dcba1
4
- data.tar.gz: 23a7375b74154eb6340e503b338b165f76c6b4d0f7d86cea2db51094a174d121
3
+ metadata.gz: 137dd6d83f2c7ddfa976b7c2f4998569608d53591fcb78c71493a2a2f6ba38ba
4
+ data.tar.gz: 95c0a8c95e46fd79bbea338e110c675ee9e56983b587cea9392aa2161d939efa
5
5
  SHA512:
6
- metadata.gz: 3f18c6832a2d52a4a758a474c73d475695d0eccfbff98e3eb1c582e75078bbcc68f2ef49ceac6b759ff4c91fb56a70a46b6ca2873f03ddce279f724d13c5a9d7
7
- data.tar.gz: 2b5d56a8a30569675d58ddf27b9947c4eba359559940d5f64c5486756c0fa6df1d69ede0f3e0212a5adb04dd854ac6aebcc5564f9d4b8b65b08edac265b84537
6
+ metadata.gz: 4c1f3bc6e859ebadea39a5f5c142270458abd1b6778f872dc3026bec5489652bd652356ee37c8f8b5f5f57397e00c81914281f2d3380584afea88e8080eab0a2
7
+ data.tar.gz: 2b23f7200bf17572009da8f6eed84bbac0fd6e8a0d9c52eb781e6a32fa0933598adb25732d8488cef709eed07a10b88d2146618f8444350e35d0798e8c74030b
@@ -1,13 +1,10 @@
1
1
  <Project>
2
-
3
2
  <PropertyGroup>
4
3
  <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
5
4
  </PropertyGroup>
6
-
7
5
  <PropertyGroup>
8
6
  <MSBuildPackageVersion>17.12.6</MSBuildPackageVersion>
9
7
  </PropertyGroup>
10
-
11
8
  <ItemGroup>
12
9
  <PackageVersion Include="DiffPlex" Version="1.7.2" />
13
10
  <PackageVersion Include="GuiLabs.Language.Xml" Version="1.2.93" />
@@ -20,12 +17,16 @@
20
17
  <PackageVersion Include="Microsoft.CSharp" Version="4.7.0" />
21
18
  <PackageVersion Include="Microsoft.Extensions.FileProviders.Abstractions" Version="9.0.0" />
22
19
  <PackageVersion Include="Microsoft.Extensions.FileSystemGlobbing" Version="9.0.3" />
20
+ <PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.3" />
23
21
  <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
24
22
  <PackageVersion Include="Microsoft.VisualStudio.Setup.Configuration.Interop" Version="3.12.2149" />
25
23
  <PackageVersion Include="Microsoft.Web.Xdt" Version="3.1.0" />
26
24
  <PackageVersion Include="MSBuild.StructuredLogger" Version="2.2.386" />
27
25
  <PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
28
26
  <PackageVersion Include="NuGet.Core" Version="2.14.0" Aliases="CoreV2" />
27
+ <PackageVersion Include="OpenTelemetry" Version="1.11.2" />
28
+ <PackageVersion Include="OpenTelemetry.Exporter.Console" Version="1.11.2" />
29
+ <PackageVersion Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.11.2" />
29
30
  <PackageVersion Include="Semver" Version="3.0.0" />
30
31
  <PackageVersion Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
31
32
  <PackageVersion Include="System.ComponentModel.Composition" Version="9.0.3" />
@@ -39,5 +40,4 @@
39
40
  <PackageVersion Include="xunit" Version="2.9.3" />
40
41
  <PackageVersion Include="xunit.runner.visualstudio" Version="3.0.2" />
41
42
  </ItemGroup>
42
-
43
- </Project>
43
+ </Project>
@@ -30,7 +30,7 @@ internal static class AnalyzeCommand
30
30
 
31
31
  command.SetHandler(async (jobId, jobPath, repoRoot, discoveryPath, dependencyPath, analysisDirectory) =>
32
32
  {
33
- var logger = new ConsoleLogger();
33
+ var logger = new OpenTelemetryLogger();
34
34
  var (experimentsManager, _errorResult) = await ExperimentsManager.FromJobFileAsync(jobId, jobPath.FullName);
35
35
  var worker = new AnalyzeWorker(jobId, experimentsManager, logger);
36
36
  await worker.RunAsync(repoRoot.FullName, discoveryPath.FullName, dependencyPath.FullName, analysisDirectory.FullName);
@@ -28,7 +28,7 @@ internal static class CloneCommand
28
28
  command.SetHandler(async (jobPath, repoContentsPath, apiUrl, jobId) =>
29
29
  {
30
30
  var apiHandler = new HttpApiHandler(apiUrl.ToString(), jobId);
31
- var logger = new ConsoleLogger();
31
+ var logger = new OpenTelemetryLogger();
32
32
  var gitCommandHandler = new ShellGitCommandHandler(logger);
33
33
  var worker = new CloneWorker(jobId, apiHandler, gitCommandHandler);
34
34
  var exitCode = await worker.RunAsync(jobPath, repoContentsPath);
@@ -28,7 +28,8 @@ internal static class DiscoverCommand
28
28
 
29
29
  command.SetHandler(async (jobId, jobPath, repoRoot, workspace, outputPath) =>
30
30
  {
31
- MSBuildHelper.RegisterMSBuild(repoRoot.FullName, repoRoot.FullName);
31
+ var logger = new OpenTelemetryLogger();
32
+ MSBuildHelper.RegisterMSBuild(repoRoot.FullName, repoRoot.FullName, logger);
32
33
  var (experimentsManager, error) = await ExperimentsManager.FromJobFileAsync(jobId, jobPath.FullName);
33
34
  if (error is not null)
34
35
  {
@@ -43,7 +44,6 @@ internal static class DiscoverCommand
43
44
  return;
44
45
  }
45
46
 
46
- var logger = new ConsoleLogger();
47
47
  var worker = new DiscoveryWorker(jobId, experimentsManager, logger);
48
48
  await worker.RunAsync(repoRoot.FullName, workspace, outputPath.FullName);
49
49
  }, JobIdOption, JobPathOption, RepoRootOption, WorkspaceOption, OutputOption);
@@ -34,7 +34,7 @@ internal static class RunCommand
34
34
  {
35
35
  var apiHandler = new HttpApiHandler(apiUrl.ToString(), jobId);
36
36
  var (experimentsManager, _errorResult) = await ExperimentsManager.FromJobFileAsync(jobId, jobPath.FullName);
37
- var logger = new ConsoleLogger();
37
+ var logger = new OpenTelemetryLogger();
38
38
  var discoverWorker = new DiscoveryWorker(jobId, experimentsManager, logger);
39
39
  var analyzeWorker = new AnalyzeWorker(jobId, experimentsManager, logger);
40
40
  var updateWorker = new UpdaterWorker(jobId, experimentsManager, logger);
@@ -46,7 +46,7 @@ internal static class UpdateCommand
46
46
  var resultOutputPath = context.ParseResult.GetValueForOption(ResultOutputPathOption);
47
47
 
48
48
  var (experimentsManager, _error) = await ExperimentsManager.FromJobFileAsync(jobId, jobPath.FullName);
49
- var logger = new ConsoleLogger();
49
+ var logger = new OpenTelemetryLogger();
50
50
  var worker = new UpdaterWorker(jobId, experimentsManager, logger);
51
51
  await worker.RunAsync(repoRoot.FullName, solutionOrProjectFile.FullName, dependencyName, previousVersion, newVersion, isTransitive, resultOutputPath);
52
52
  setExitCode(0);
@@ -67,7 +67,7 @@ public partial class AnalyzeWorker : IAnalyzeWorker
67
67
 
68
68
  public async Task<AnalysisResult> RunAsync(string repoRoot, WorkspaceDiscoveryResult discovery, DependencyInfo dependencyInfo)
69
69
  {
70
- MSBuildHelper.RegisterMSBuild(repoRoot, repoRoot);
70
+ MSBuildHelper.RegisterMSBuild(repoRoot, repoRoot, _logger);
71
71
 
72
72
  var startingDirectory = PathHelper.JoinPath(repoRoot, discovery.Path);
73
73
 
@@ -65,7 +65,7 @@ public partial class DiscoveryWorker : IDiscoveryWorker
65
65
 
66
66
  public async Task<WorkspaceDiscoveryResult> RunAsync(string repoRootPath, string workspacePath)
67
67
  {
68
- MSBuildHelper.RegisterMSBuild(repoRootPath, workspacePath);
68
+ MSBuildHelper.RegisterMSBuild(repoRootPath, workspacePath, _logger);
69
69
 
70
70
  // the `workspacePath` variable is relative to a repository root, so a rooted path actually isn't rooted; the
71
71
  // easy way to deal with this is to just trim the leading "/" if it exists
@@ -8,13 +8,15 @@ internal sealed class ProjectBuildFile : XmlBuildFile
8
8
  => Parse(basePath, path, File.ReadAllText(path));
9
9
 
10
10
  public static ProjectBuildFile Parse(string basePath, string path, string xml)
11
- => new(basePath, path, Parser.ParseText(xml));
11
+ => new(basePath, path, Parse(xml));
12
12
 
13
13
  public ProjectBuildFile(string basePath, string path, XmlDocumentSyntax contents)
14
14
  : base(basePath, path, contents)
15
15
  {
16
16
  }
17
17
 
18
+ public static XmlDocumentSyntax Parse(string contents) => Parser.ParseText(contents);
19
+
18
20
  public IXmlElementSyntax ProjectNode => Contents.RootSyntax;
19
21
 
20
22
  public IEnumerable<IXmlElementSyntax> SdkNodes => ProjectNode
@@ -23,8 +23,12 @@
23
23
  <PackageReference Include="GuiLabs.Language.Xml" />
24
24
  <PackageReference Include="DiffPlex" />
25
25
  <PackageReference Include="Microsoft.Build.Locator" />
26
+ <PackageReference Include="Microsoft.Extensions.Logging" />
26
27
  <PackageReference Include="MSBuild.StructuredLogger" />
27
28
  <PackageReference Include="NuGet.Core" Aliases="CoreV2" />
29
+ <PackageReference Include="OpenTelemetry" />
30
+ <PackageReference Include="OpenTelemetry.Exporter.Console" />
31
+ <PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" />
28
32
  </ItemGroup>
29
33
 
30
34
  <ItemGroup>
@@ -31,6 +31,7 @@ public abstract record JobErrorBase : MessageBase
31
31
  HttpStatusCode.Unauthorized or
32
32
  HttpStatusCode.Forbidden => new PrivateSourceAuthenticationFailure(NuGetContext.GetPackageSourceUrls(currentDirectory)),
33
33
  HttpStatusCode.TooManyRequests => new PrivateSourceBadResponse(NuGetContext.GetPackageSourceUrls(currentDirectory)),
34
+ HttpStatusCode.ServiceUnavailable => new PrivateSourceBadResponse(NuGetContext.GetPackageSourceUrls(currentDirectory)),
34
35
  _ => new UnknownError(ex, jobId),
35
36
  },
36
37
  InvalidProjectFileException invalidProjectFile => new DependencyFileNotParseable(invalidProjectFile.ProjectFile),
@@ -1,44 +1,45 @@
1
- using NuGet.Versioning;
1
+ using System.Collections.Immutable;
2
2
 
3
3
  using NuGetUpdater.Core.Run.ApiModel;
4
+ using NuGetUpdater.Core.Updater;
4
5
 
5
6
  namespace NuGetUpdater.Core.Run;
6
7
 
7
8
  public class PullRequestTextGenerator
8
9
  {
9
- public static string GetPullRequestTitle(Job job, ReportedDependency[] updatedDependencies, DependencyFile[] updatedFiles, string? dependencyGroupName = null)
10
+ public static string GetPullRequestTitle(Job job, ImmutableArray<UpdateOperationBase> updateOperationsPerformed, string? dependencyGroupName)
10
11
  {
11
12
  // simple version looks like
12
13
  // Update Some.Package to 1.2.3
13
14
  // if multiple packages are updated to multiple versions, result looks like:
14
15
  // Update Package.A to 1.0.0, 2.0.0; Package.B to 3.0.0, 4.0.0
15
- var dependencySets = updatedDependencies
16
- .GroupBy(d => d.Name, StringComparer.OrdinalIgnoreCase)
16
+ var dependencySets = updateOperationsPerformed
17
+ .GroupBy(d => d.DependencyName, StringComparer.OrdinalIgnoreCase)
17
18
  .OrderBy(g => g.Key, StringComparer.OrdinalIgnoreCase)
18
19
  .Select(g => new
19
20
  {
20
21
  Name = g.Key,
21
22
  Versions = g
22
- .Where(d => d.Version is not null)
23
- .Select(d => d.Version!)
24
- .OrderBy(d => NuGetVersion.Parse(d))
23
+ .Select(d => d.NewVersion)
24
+ .OrderBy(v => v)
25
25
  .ToArray()
26
26
  })
27
27
  .ToArray();
28
28
  var updatedPartTitles = dependencySets
29
- .Select(d => $"{d.Name} to {string.Join(", ", d.Versions)}")
29
+ .Select(d => $"{d.Name} to {string.Join(", ", d.Versions.Select(v => v.ToString()))}")
30
30
  .ToArray();
31
31
  var title = $"{job.CommitMessageOptions?.Prefix}Update {string.Join("; ", updatedPartTitles)}";
32
32
  return title;
33
33
  }
34
34
 
35
- public static string GetPullRequestCommitMessage(Job job, ReportedDependency[] updatedDependencies, DependencyFile[] updatedFiles, string? dependencyGroupName = null)
35
+ public static string GetPullRequestCommitMessage(Job job, ImmutableArray<UpdateOperationBase> updateOperationsPerformed, string? dependencyGroupName)
36
36
  {
37
- return GetPullRequestTitle(job, updatedDependencies, updatedFiles, dependencyGroupName);
37
+ return GetPullRequestTitle(job, updateOperationsPerformed, dependencyGroupName);
38
38
  }
39
39
 
40
- public static string GetPullRequestBody(Job job, ReportedDependency[] updatedDependencies, DependencyFile[] updatedFiles, string? dependencyGroupName = null)
40
+ public static string GetPullRequestBody(Job job, ImmutableArray<UpdateOperationBase> updateOperationsPerformed, string? dependencyGroupName)
41
41
  {
42
- return GetPullRequestTitle(job, updatedDependencies, updatedFiles, dependencyGroupName);
42
+ var report = UpdateOperationBase.GenerateUpdateOperationReport(updateOperationsPerformed);
43
+ return report;
43
44
  }
44
45
  }
@@ -69,7 +69,7 @@ public class RunWorker
69
69
 
70
70
  try
71
71
  {
72
- MSBuildHelper.RegisterMSBuild(repoContentsPath.FullName, repoContentsPath.FullName);
72
+ MSBuildHelper.RegisterMSBuild(repoContentsPath.FullName, repoContentsPath.FullName, _logger);
73
73
 
74
74
  var experimentsManager = ExperimentsManager.GetExperimentsManager(job.Experiments);
75
75
  var allDependencyFiles = new Dictionary<string, DependencyFile>();
@@ -291,7 +291,12 @@ public class RunWorker
291
291
  .Select(kvp => kvp.Value)
292
292
  .ToArray();
293
293
 
294
- var resultMessage = GetPullRequestApiMessage(job, updatedDependencyFileList, actualUpdatedDependencies.ToArray(), baseCommitSha);
294
+ var normalizedUpdateOperationsPerformed = UpdateOperationBase.NormalizeUpdateOperationCollection(repoContentsPath.FullName, updateOperationsPerformed);
295
+ var report = UpdateOperationBase.GenerateUpdateOperationReport(normalizedUpdateOperationsPerformed);
296
+ _logger.Info(report);
297
+
298
+ var sortedUpdatedDependencies = actualUpdatedDependencies.OrderBy(d => d.Name, StringComparer.OrdinalIgnoreCase).ToArray();
299
+ var resultMessage = GetPullRequestApiMessage(job, updatedDependencyFileList, sortedUpdatedDependencies, normalizedUpdateOperationsPerformed, baseCommitSha);
295
300
  switch (resultMessage)
296
301
  {
297
302
  case ClosePullRequest close:
@@ -321,10 +326,6 @@ public class RunWorker
321
326
  await SendApiMessage(new SecurityUpdateNotNeeded(depName));
322
327
  }
323
328
 
324
- var normalizedUpdateOperationsPerformed = UpdateOperationBase.NormalizeUpdateOperationCollection(repoContentsPath.FullName, updateOperationsPerformed);
325
- var report = UpdateOperationBase.GenerateUpdateOperationReport(normalizedUpdateOperationsPerformed);
326
- _logger.Info(report);
327
-
328
329
  var result = new RunResult()
329
330
  {
330
331
  Base64DependencyFiles = originalDependencyFileContents.OrderBy(kvp => kvp.Key).Select(kvp =>
@@ -365,10 +366,16 @@ public class RunWorker
365
366
  }
366
367
  }
367
368
 
368
- internal static MessageBase? GetPullRequestApiMessage(Job job, DependencyFile[] updatedFiles, ReportedDependency[] updatedDependencies, string baseCommitSha)
369
+ internal static MessageBase? GetPullRequestApiMessage(
370
+ Job job,
371
+ DependencyFile[] updatedFiles,
372
+ ReportedDependency[] updatedDependencies,
373
+ ImmutableArray<UpdateOperationBase> updateOperationsPerformed,
374
+ string baseCommitSha
375
+ )
369
376
  {
370
- updatedDependencies = updatedDependencies.OrderBy(d => d.Name, StringComparer.OrdinalIgnoreCase).ToArray();
371
- var updatedDependenciesSet = updatedDependencies.Select(d => d.Name).ToHashSet(StringComparer.OrdinalIgnoreCase);
377
+ var updatedDependencyNames = updateOperationsPerformed.Select(u => u.DependencyName).OrderBy(d => d, StringComparer.OrdinalIgnoreCase).ToArray();
378
+ var updatedDependenciesSet = updatedDependencyNames.ToHashSet(StringComparer.OrdinalIgnoreCase);
372
379
 
373
380
  // all pull request dependencies with optional group name
374
381
  var existingPullRequests = job.GetAllExistingPullRequests();
@@ -390,12 +397,12 @@ public class RunWorker
390
397
  return new UpdatePullRequest()
391
398
  {
392
399
  DependencyGroup = existingPullRequest.Item1,
393
- DependencyNames = updatedDependencies.Select(d => d.Name).ToImmutableArray(),
400
+ DependencyNames = [.. updatedDependencyNames],
394
401
  UpdatedDependencyFiles = updatedFiles,
395
402
  BaseCommitSha = baseCommitSha,
396
- CommitMessage = PullRequestTextGenerator.GetPullRequestCommitMessage(job, updatedDependencies, updatedFiles, existingPullRequest.Item1),
397
- PrTitle = PullRequestTextGenerator.GetPullRequestTitle(job, updatedDependencies, updatedFiles, existingPullRequest.Item1),
398
- PrBody = PullRequestTextGenerator.GetPullRequestBody(job, updatedDependencies, updatedFiles, existingPullRequest.Item1),
403
+ CommitMessage = PullRequestTextGenerator.GetPullRequestCommitMessage(job, updateOperationsPerformed, existingPullRequest.Item1),
404
+ PrTitle = PullRequestTextGenerator.GetPullRequestTitle(job, updateOperationsPerformed, existingPullRequest.Item1),
405
+ PrBody = PullRequestTextGenerator.GetPullRequestBody(job, updateOperationsPerformed, existingPullRequest.Item1),
399
406
  };
400
407
  }
401
408
  else
@@ -422,16 +429,16 @@ public class RunWorker
422
429
  }
423
430
  else
424
431
  {
425
- if (updatedDependencies.Any())
432
+ if (updatedDependencyNames.Any())
426
433
  {
427
434
  return new CreatePullRequest()
428
435
  {
429
436
  Dependencies = updatedDependencies,
430
437
  UpdatedDependencyFiles = updatedFiles,
431
438
  BaseCommitSha = baseCommitSha,
432
- CommitMessage = PullRequestTextGenerator.GetPullRequestCommitMessage(job, updatedDependencies, updatedFiles),
433
- PrTitle = PullRequestTextGenerator.GetPullRequestTitle(job, updatedDependencies, updatedFiles),
434
- PrBody = PullRequestTextGenerator.GetPullRequestBody(job, updatedDependencies, updatedFiles),
439
+ CommitMessage = PullRequestTextGenerator.GetPullRequestCommitMessage(job, updateOperationsPerformed, dependencyGroupName: null),
440
+ PrTitle = PullRequestTextGenerator.GetPullRequestTitle(job, updateOperationsPerformed, dependencyGroupName: null),
441
+ PrBody = PullRequestTextGenerator.GetPullRequestBody(job, updateOperationsPerformed, dependencyGroupName: null),
435
442
  };
436
443
  }
437
444
  }
@@ -547,9 +554,33 @@ public class RunWorker
547
554
  .ToArray();
548
555
  if (existingPullRequests.Length > 0)
549
556
  {
550
- var existingPrVersion = existingPullRequests[0].Item2.First(d => d.DependencyName.Equals(dependency.Name, StringComparison.OrdinalIgnoreCase)).DependencyVersion;
551
- message = new PullRequestExistsForLatestVersion(dependency.Name, existingPrVersion.ToString());
552
- return false;
557
+ // found a matching pr...
558
+ if (job.UpdatingAPullRequest)
559
+ {
560
+ // ...and we've been asked to update it
561
+ return true;
562
+ }
563
+ else
564
+ {
565
+ // ...but no update requested => don't perform any update and report error
566
+ var existingPrVersion = existingPullRequests[0].Item2.First(d => d.DependencyName.Equals(dependency.Name, StringComparison.OrdinalIgnoreCase)).DependencyVersion;
567
+ message = new PullRequestExistsForLatestVersion(dependency.Name, existingPrVersion.ToString());
568
+ return false;
569
+ }
570
+ }
571
+ else
572
+ {
573
+ // no matching pr...
574
+ if (job.UpdatingAPullRequest)
575
+ {
576
+ // ...but we've been asked to perform an update => no update possible, nothing to report
577
+ return false;
578
+ }
579
+ else
580
+ {
581
+ // ...and no update specifically requested => create new
582
+ return true;
583
+ }
553
584
  }
554
585
  }
555
586
  else
@@ -565,7 +596,7 @@ public class RunWorker
565
596
  }
566
597
  }
567
598
 
568
- return isVulnerable;
599
+ return false;
569
600
  }
570
601
  else
571
602
  {
@@ -399,7 +399,7 @@ internal static class PackageReferenceUpdater
399
399
  }
400
400
 
401
401
  /// <returns>The updated files.</returns>
402
- private static async Task<IEnumerable<string>> UpdateTransitiveDependencyAsync(
402
+ internal static async Task<IEnumerable<string>> UpdateTransitiveDependencyAsync(
403
403
  string repoRootPath,
404
404
  string projectPath,
405
405
  string dependencyName,
@@ -419,6 +419,17 @@ internal static class PackageReferenceUpdater
419
419
  else
420
420
  {
421
421
  updatedFiles = await AddTransitiveDependencyAsync(repoRootPath, projectPath, dependencyName, newDependencyVersion, experimentsManager, logger);
422
+
423
+ // files directly modified on disk by an external tool need to be refreshed in-memory
424
+ foreach (var updatedFile in updatedFiles)
425
+ {
426
+ var matchingBuildFile = buildFiles.FirstOrDefault(bf => PathComparer.Instance.Compare(updatedFile, bf.Path) == 0);
427
+ if (matchingBuildFile is not null)
428
+ {
429
+ var updatedContents = await File.ReadAllTextAsync(updatedFile);
430
+ matchingBuildFile.Update(ProjectBuildFile.Parse(updatedContents));
431
+ }
432
+ }
422
433
  }
423
434
 
424
435
  return updatedFiles;
@@ -671,7 +682,7 @@ internal static class PackageReferenceUpdater
671
682
  }
672
683
 
673
684
  /// <returns>The updated files.</returns>
674
- private static (UpdateResult, IEnumerable<UpdateOperationBase>) TryUpdateDependencyVersion(
685
+ internal static (UpdateResult, IEnumerable<UpdateOperationBase>) TryUpdateDependencyVersion(
675
686
  ImmutableArray<ProjectBuildFile> buildFiles,
676
687
  string dependencyName,
677
688
  string? previousDependencyVersion,
@@ -112,14 +112,14 @@ public abstract record UpdateOperationBase
112
112
  public record DirectUpdate : UpdateOperationBase
113
113
  {
114
114
  public override string Type => nameof(DirectUpdate);
115
- public override string GetReport() => $"Updated {DependencyName} to {NewVersion} in {string.Join("", UpdatedFiles)}";
115
+ public override string GetReport() => $"Updated {DependencyName} to {NewVersion} in {string.Join(", ", UpdatedFiles)}";
116
116
  public sealed override string ToString() => GetString();
117
117
  }
118
118
 
119
119
  public record PinnedUpdate : UpdateOperationBase
120
120
  {
121
121
  public override string Type => nameof(PinnedUpdate);
122
- public override string GetReport() => $"Pinned {DependencyName} at {NewVersion} in {string.Join("", UpdatedFiles)}";
122
+ public override string GetReport() => $"Pinned {DependencyName} at {NewVersion} in {string.Join(", ", UpdatedFiles)}";
123
123
  public sealed override string ToString() => GetString();
124
124
  }
125
125
 
@@ -129,7 +129,7 @@ public record ParentUpdate : UpdateOperationBase, IEquatable<UpdateOperationBase
129
129
  public required string ParentDependencyName { get; init; }
130
130
  public required NuGetVersion ParentNewVersion { get; init; }
131
131
 
132
- public override string GetReport() => $"Updated {DependencyName} to {NewVersion} indirectly via {ParentDependencyName}/{ParentNewVersion} in {string.Join("", UpdatedFiles)}";
132
+ public override string GetReport() => $"Updated {DependencyName} to {NewVersion} indirectly via {ParentDependencyName}/{ParentNewVersion} in {string.Join(", ", UpdatedFiles)}";
133
133
 
134
134
  bool IEquatable<UpdateOperationBase>.Equals(UpdateOperationBase? other)
135
135
  {
@@ -66,7 +66,7 @@ public class UpdaterWorker : IUpdaterWorker
66
66
 
67
67
  public async Task<UpdateOperationResult> RunAsync(string repoRootPath, string workspacePath, string dependencyName, string previousDependencyVersion, string newDependencyVersion, bool isTransitive)
68
68
  {
69
- MSBuildHelper.RegisterMSBuild(Environment.CurrentDirectory, repoRootPath);
69
+ MSBuildHelper.RegisterMSBuild(Environment.CurrentDirectory, repoRootPath, _logger);
70
70
 
71
71
  if (!Path.IsPathRooted(workspacePath) || !File.Exists(workspacePath))
72
72
  {
@@ -33,7 +33,7 @@ internal static partial class MSBuildHelper
33
33
 
34
34
  public static string GetFileFromRuntimeDirectory(string fileName) => Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!, fileName);
35
35
 
36
- public static void RegisterMSBuild(string currentDirectory, string rootDirectory)
36
+ public static void RegisterMSBuild(string currentDirectory, string rootDirectory, ILogger logger)
37
37
  {
38
38
  // Ensure MSBuild types are registered before calling a method that loads the types
39
39
  if (!IsMSBuildRegistered)
@@ -45,7 +45,7 @@ internal static partial class MSBuildHelper
45
45
  MSBuildPath = defaultInstance.MSBuildPath;
46
46
  MSBuildLocator.RegisterInstance(defaultInstance);
47
47
  return Task.FromResult(0);
48
- }).Wait();
48
+ }, logger).Wait();
49
49
  }
50
50
  }
51
51
 
@@ -54,11 +54,10 @@ internal static partial class MSBuildHelper
54
54
  string rootDirectory,
55
55
  ExperimentsManager experimentsManager,
56
56
  Func<Task<T>> action,
57
- ILogger? logger = null,
57
+ ILogger logger,
58
58
  bool retainMSBuildSdks = false
59
59
  )
60
60
  {
61
- logger ??= new ConsoleLogger();
62
61
  if (experimentsManager.InstallDotnetSdks)
63
62
  {
64
63
  logger.Info($"{nameof(ExperimentsManager.InstallDotnetSdks)} == true; retaining `global.json` contents.");
@@ -820,7 +819,7 @@ internal static partial class MSBuildHelper
820
819
  experimentsManager
821
820
  );
822
821
  return (exitCode, stdOut, stdErr);
823
- });
822
+ }, logger);
824
823
  ThrowOnError(stdOut);
825
824
  if (exitCode != 0)
826
825
  {
@@ -969,6 +968,7 @@ internal static partial class MSBuildHelper
969
968
  ThrowOnMissingPackages(output);
970
969
  ThrowOnUpdateNotPossible(output);
971
970
  ThrowOnRateLimitExceeded(output);
971
+ ThrowOnServiceUnavailable(output);
972
972
  }
973
973
 
974
974
  private static void ThrowOnUnauthenticatedFeed(string stdout)
@@ -999,6 +999,19 @@ internal static partial class MSBuildHelper
999
999
  }
1000
1000
  }
1001
1001
 
1002
+ private static void ThrowOnServiceUnavailable(string stdout)
1003
+ {
1004
+ var serviceUnavailableMessageSnippets = new string[]
1005
+ {
1006
+ "503 (Service Unavailable)",
1007
+ "Response status code does not indicate success: 503",
1008
+ };
1009
+ if (serviceUnavailableMessageSnippets.Any(stdout.Contains))
1010
+ {
1011
+ throw new HttpRequestException(message: stdout, inner: null, statusCode: System.Net.HttpStatusCode.ServiceUnavailable);
1012
+ }
1013
+ }
1014
+
1002
1015
  private static void ThrowOnMissingFile(string output)
1003
1016
  {
1004
1017
  var missingFile = GetMissingFile(output);
@@ -1071,6 +1084,7 @@ internal static partial class MSBuildHelper
1071
1084
  TryGetGlobalJsonPath(repoRootPath, projectPath, out var globalJsonPath);
1072
1085
  var safeGlobalJsonName = $"{globalJsonPath}{Guid.NewGuid()}";
1073
1086
  HashSet<string> targetFrameworks = new(StringComparer.OrdinalIgnoreCase);
1087
+ var repoRootDirectoryInfo = new DirectoryInfo(repoRootPath);
1074
1088
 
1075
1089
  try
1076
1090
  {
@@ -1099,12 +1113,23 @@ internal static partial class MSBuildHelper
1099
1113
  // load the project even if it imports a file that doesn't exist (e.g. a file that's generated at restore
1100
1114
  // or build time).
1101
1115
  using var projectCollection = new ProjectCollection(); // do this in a one-off instance and don't pollute the global collection
1102
- Project project = Project.FromFile(projectPath, new ProjectOptions
1116
+ var project = Project.FromFile(projectPath, new ProjectOptions
1103
1117
  {
1104
1118
  LoadSettings = ProjectLoadSettings.IgnoreMissingImports,
1105
1119
  ProjectCollection = projectCollection,
1106
1120
  });
1107
- buildFileList.AddRange(project.Imports.Select(i => i.ImportedProject.FullPath.NormalizePathToUnix()));
1121
+ var allImportedPaths = project.Imports.Select(i => i.ImportedProject.FullPath.NormalizePathToUnix()).ToArray();
1122
+ var importedPathsInRepo = allImportedPaths.Where(p => PathHelper.IsFileUnderDirectory(repoRootDirectoryInfo, new FileInfo(p))).ToArray();
1123
+ var projectDir = Path.GetDirectoryName(projectPath)!;
1124
+ var intermediateDir = new DirectoryInfo(Path.Combine(projectDir, project.GetPropertyValue("BaseIntermediateOutputPath")));
1125
+ var outputDir = new DirectoryInfo(Path.Combine(projectDir, project.GetPropertyValue("BaseOutputPath")));
1126
+ var nonTransitivePathsInRepo = importedPathsInRepo.Where(p =>
1127
+ {
1128
+ var fi = new FileInfo(p);
1129
+ return !PathHelper.IsFileUnderDirectory(intermediateDir, fi)
1130
+ && !PathHelper.IsFileUnderDirectory(outputDir, fi);
1131
+ }).ToArray();
1132
+ buildFileList.AddRange(nonTransitivePathsInRepo.Select(p => p.NormalizePathToUnix()));
1108
1133
 
1109
1134
  // use the MSBuild-evaluated value so we don't have to try to manually parse XML
1110
1135
  IEnumerable<ProjectProperty> targetFrameworkProperties = project.Properties.Where(p => p.Name.Equals("TargetFramework", StringComparison.OrdinalIgnoreCase)).ToList();
@@ -1153,11 +1178,7 @@ internal static partial class MSBuildHelper
1153
1178
  }
1154
1179
  }
1155
1180
 
1156
- var repoRootPathPrefix = repoRootPath.NormalizePathToUnix() + "/";
1157
- var buildFiles = buildFileList
1158
- .Where(f => f.StartsWith(repoRootPathPrefix, StringComparison.OrdinalIgnoreCase))
1159
- .Distinct();
1160
- var result = buildFiles
1181
+ var result = buildFileList
1161
1182
  .Where(File.Exists)
1162
1183
  .Select(path => ProjectBuildFile.Open(repoRootPath, path))
1163
1184
  .ToImmutableArray();
@@ -0,0 +1,54 @@
1
+ using System.Globalization;
2
+
3
+ using Microsoft.Extensions.Logging;
4
+
5
+ using OpenTelemetry;
6
+ using OpenTelemetry.Logs;
7
+
8
+ namespace NuGetUpdater.Core
9
+ {
10
+ public class OpenTelemetryLogger : ILogger, IDisposable
11
+ {
12
+ private readonly ILoggerFactory _loggerFactory;
13
+ private readonly Microsoft.Extensions.Logging.ILogger _logger;
14
+
15
+ public OpenTelemetryLogger()
16
+ {
17
+ _loggerFactory = LoggerFactory.Create(builder =>
18
+ {
19
+ builder.AddOpenTelemetry(logging =>
20
+ {
21
+ logging.AddProcessor(new SimpleLogRecordExportProcessor(new CustomConsoleExporter()));
22
+ logging.AddOtlpExporter();
23
+ });
24
+ });
25
+
26
+ _logger = _loggerFactory.CreateLogger<OpenTelemetryLogger>();
27
+ }
28
+
29
+ public void LogRaw(string message)
30
+ {
31
+ _logger.LogInformation(message);
32
+ }
33
+
34
+ public void Dispose()
35
+ {
36
+ _loggerFactory?.Dispose();
37
+ }
38
+ }
39
+
40
+ // We do this because the exporter that comes from AddConsoleExporter() prepends "LogRecord.Timestamp" in front of strings
41
+ internal class CustomConsoleExporter : BaseExporter<LogRecord>
42
+ {
43
+ public override ExportResult Export(in Batch<LogRecord> batch)
44
+ {
45
+ foreach (var logRecord in batch)
46
+ {
47
+ Console.WriteLine(logRecord.Body ?? string.Empty);
48
+ }
49
+
50
+ return ExportResult.Success;
51
+ }
52
+ }
53
+
54
+ }