dependabot-nuget 0.285.0 → 0.287.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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/helpers/lib/NuGetUpdater/NuGetProjects/Directory.Build.props +5 -1
  3. data/helpers/lib/NuGetUpdater/NuGetProjects/NuGet.CommandLine/NuGet.CommandLine.csproj +1 -0
  4. data/helpers/lib/NuGetUpdater/NuGetProjects/NuGet.Configuration/NuGet.Configuration.csproj +1 -0
  5. data/helpers/lib/NuGetUpdater/NuGetProjects/NuGet.LibraryModel/NuGet.LibraryModel.csproj +1 -0
  6. data/helpers/lib/NuGetUpdater/NuGetProjects/NuGet.Packaging/NuGet.Packaging.csproj +1 -1
  7. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/RunCommand.cs +8 -1
  8. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/UpdateCommand.cs +7 -3
  9. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Update.cs +11 -0
  10. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/AnalyzeWorker.cs +2 -2
  11. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/CompatabilityChecker.cs +24 -5
  12. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/NuGetContext.cs +15 -4
  13. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/VersionFinder.cs +9 -38
  14. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/DiscoveryWorker.cs +10 -8
  15. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/ExperimentsManager.cs +52 -0
  16. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Files/ProjectBuildFile.cs +15 -8
  17. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/IAnalyzeWorker.cs +9 -0
  18. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/IDiscoveryWorker.cs +8 -0
  19. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/IUpdaterWorker.cs +9 -0
  20. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/RunWorker.cs +78 -61
  21. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackageReferenceUpdater.cs +21 -10
  22. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackagesConfigUpdater.cs +37 -5
  23. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/UpdaterWorker.cs +5 -3
  24. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +5 -6
  25. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/PathHelper.cs +51 -0
  26. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/AnalyzeWorkerTests.cs +302 -0
  27. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.cs +269 -0
  28. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/RunWorkerTests.cs +577 -43
  29. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/SerializationTests.cs +168 -0
  30. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/TestAnalyzeWorker.cs +37 -0
  31. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/TestDiscoveryWorker.cs +35 -0
  32. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/TestUpdaterWorker.cs +39 -0
  33. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/PackagesConfigUpdaterTests.cs +104 -3
  34. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTestBase.cs +51 -13
  35. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.DirsProj.cs +4 -2
  36. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackageReference.cs +62 -18
  37. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackagesConfig.cs +1 -1
  38. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/PathHelperTests.cs +14 -0
  39. data/helpers/lib/NuGetUpdater/global.json +1 -1
  40. data/lib/dependabot/nuget/file_updater.rb +8 -3
  41. data/lib/dependabot/nuget/native_helpers.rb +11 -12
  42. metadata +12 -6
  43. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/DependencySolverEnvironment.cs +0 -12
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d8dd6e625e132b28270ffbff6c1af11b0173e3a786a1f6518080c20adc028ac2
4
- data.tar.gz: 7afe9afdd9b8ab5ae40372ac51fb292efce75ed4d4b5ba7fee96468e00e835bc
3
+ metadata.gz: 3bb2d903f08322511a7917fba47df7b66208dbbaca42e52d30a1954f9ef3eaa2
4
+ data.tar.gz: f19cfed3ad2939c93812727546b53ecec603c712641a6c1d0581469cce72d584
5
5
  SHA512:
6
- metadata.gz: 0d9b4fdc7ba52be531cb199699324e7be1a68ae6a698619e961535f0cdb993833cc06e6e4f0df7a88f5b12e3a8bb06627a5ba2233e24bcafa4ade1badcf6a2bf
7
- data.tar.gz: e9619a91fc865a8aa45a812a923f089a10e67f0be0c8588908206726e4e2b6d95ee1626cc0897d3dad5c5aca1a2e89f1c657e25a9137d4f186e05a7813b3e34c
6
+ metadata.gz: a3fdc15bd985678106cf45f57c6af6dd4be57858f54b50abea6f4521a8aed3ab66b7ca67cb54f9e73dabe536d8c012619bbf049662207211bc92cbb5e938c321
7
+ data.tar.gz: 3542f489f10b67fe78b3bd55703cc420a58279bf5fb131da4a99be0d99a20fcd7430b124c4b49100929b74f39178b3c7e108e7bb0170baef5f5a91a16dfbd63f
@@ -3,7 +3,11 @@
3
3
  <PropertyGroup>
4
4
  <DefineConstants>$(DefineConstants);IS_CORECLR</DefineConstants>
5
5
  <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
6
- <NoWarn>$(NoWarn);NU1701</NoWarn>
6
+ <NoWarn>$(NoWarn);CA1305</NoWarn><!-- behavior of StringBuilder could vary based on user's locale -->
7
+ <NoWarn>$(NoWarn);CA2022</NoWarn><!-- avoid inexact Stream.Read() -->
8
+ <NoWarn>$(NoWarn);NU1701</NoWarn><!-- package target framework may not be compatible -->
9
+ <NoWarn>$(NoWarn);NU1903</NoWarn><!-- package has a known high severity vulnerability -->
10
+ <NoWarn>$(NoWarn);SYSLIB0014</NoWarn><!-- obsolete -->
7
11
  <NuGetSourceLocation>$(MSBuildThisFileDirectory)..\..\NuGet.Client</NuGetSourceLocation>
8
12
  <SharedDirectory>$(NuGetSourceLocation)\build\Shared</SharedDirectory>
9
13
  <Version>6.8.0</Version>
@@ -3,6 +3,7 @@
3
3
  <PropertyGroup>
4
4
  <TargetFramework>$(CommonTargetFramework)</TargetFramework>
5
5
  <NoWarn>$(NoWarn);CA1416</NoWarn>
6
+ <NoWarn>$(NoWarn);SYSLIB0018</NoWarn><!-- ReflectionOnly loading is not supported -->
6
7
  </PropertyGroup>
7
8
 
8
9
  <ItemGroup>
@@ -3,6 +3,7 @@
3
3
  <PropertyGroup>
4
4
  <TargetFramework>$(CommonTargetFramework)</TargetFramework>
5
5
  <NoWarn>$(NoWarn);CS1591;RS0041</NoWarn>
6
+ <Nullable>enable</Nullable>
6
7
  </PropertyGroup>
7
8
 
8
9
  <ItemGroup>
@@ -3,6 +3,7 @@
3
3
  <PropertyGroup>
4
4
  <TargetFramework>$(CommonTargetFramework)</TargetFramework>
5
5
  <NoWarn>$(NoWarn);CS1591;RS0041</NoWarn>
6
+ <Nullable>enable</Nullable>
6
7
  </PropertyGroup>
7
8
 
8
9
  <ItemGroup>
@@ -3,7 +3,7 @@
3
3
  <PropertyGroup>
4
4
  <TargetFramework>$(CommonTargetFramework)</TargetFramework>
5
5
  <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
6
- <NoWarn>$(NoWarn);CS0414;CS1591;CS1574;CS1573;CS1572;RS0041</NoWarn>
6
+ <NoWarn>$(NoWarn);CA1305;CS0414;CS1591;CS1574;CS1573;CS1572;RS0041</NoWarn>
7
7
  </PropertyGroup>
8
8
 
9
9
  <ItemGroup>
@@ -1,6 +1,8 @@
1
1
  using System.CommandLine;
2
2
 
3
3
  using NuGetUpdater.Core;
4
+ using NuGetUpdater.Core.Analyze;
5
+ using NuGetUpdater.Core.Discover;
4
6
  using NuGetUpdater.Core.Run;
5
7
 
6
8
  namespace NuGetUpdater.Cli.Commands;
@@ -31,7 +33,12 @@ internal static class RunCommand
31
33
  command.SetHandler(async (jobPath, repoContentsPath, apiUrl, jobId, outputPath, baseCommitSha) =>
32
34
  {
33
35
  var apiHandler = new HttpApiHandler(apiUrl.ToString(), jobId);
34
- var worker = new RunWorker(apiHandler, new ConsoleLogger());
36
+ var logger = new ConsoleLogger();
37
+ var experimentsManager = await ExperimentsManager.FromJobFileAsync(jobPath.FullName, logger);
38
+ var discoverWorker = new DiscoveryWorker(logger);
39
+ var analyzeWorker = new AnalyzeWorker(logger);
40
+ var updateWorker = new UpdaterWorker(experimentsManager, logger);
41
+ var worker = new RunWorker(apiHandler, discoverWorker, analyzeWorker, updateWorker, logger);
35
42
  await worker.RunAsync(jobPath, repoContentsPath, baseCommitSha, outputPath);
36
43
  }, JobPathOption, RepoContentsPathOption, ApiUrlOption, JobIdOption, OutputPathOption, BaseCommitShaOption);
37
44
 
@@ -6,6 +6,7 @@ namespace NuGetUpdater.Cli.Commands;
6
6
 
7
7
  internal static class UpdateCommand
8
8
  {
9
+ internal static readonly Option<FileInfo> JobPathOption = new("--job-path") { IsRequired = true };
9
10
  internal static readonly Option<DirectoryInfo> RepoRootOption = new("--repo-root", () => new DirectoryInfo(Environment.CurrentDirectory)) { IsRequired = false };
10
11
  internal static readonly Option<FileInfo> SolutionOrProjectFileOption = new("--solution-or-project") { IsRequired = true };
11
12
  internal static readonly Option<string> DependencyNameOption = new("--dependency") { IsRequired = true };
@@ -18,6 +19,7 @@ internal static class UpdateCommand
18
19
  {
19
20
  Command command = new("update", "Applies the changes from an analysis report to update a dependency.")
20
21
  {
22
+ JobPathOption,
21
23
  RepoRootOption,
22
24
  SolutionOrProjectFileOption,
23
25
  DependencyNameOption,
@@ -29,12 +31,14 @@ internal static class UpdateCommand
29
31
 
30
32
  command.TreatUnmatchedTokensAsErrors = true;
31
33
 
32
- command.SetHandler(async (repoRoot, solutionOrProjectFile, dependencyName, newVersion, previousVersion, isTransitive, resultOutputPath) =>
34
+ command.SetHandler(async (jobPath, repoRoot, solutionOrProjectFile, dependencyName, newVersion, previousVersion, isTransitive, resultOutputPath) =>
33
35
  {
34
- var worker = new UpdaterWorker(new ConsoleLogger());
36
+ var logger = new ConsoleLogger();
37
+ var experimentsManager = await ExperimentsManager.FromJobFileAsync(jobPath.FullName, logger);
38
+ var worker = new UpdaterWorker(experimentsManager, logger);
35
39
  await worker.RunAsync(repoRoot.FullName, solutionOrProjectFile.FullName, dependencyName, previousVersion, newVersion, isTransitive, resultOutputPath);
36
40
  setExitCode(0);
37
- }, RepoRootOption, SolutionOrProjectFileOption, DependencyNameOption, NewVersionOption, PreviousVersionOption, IsTransitiveOption, ResultOutputPathOption);
41
+ }, JobPathOption, RepoRootOption, SolutionOrProjectFileOption, DependencyNameOption, NewVersionOption, PreviousVersionOption, IsTransitiveOption, ResultOutputPathOption);
38
42
 
39
43
  return command;
40
44
  }
@@ -1,3 +1,4 @@
1
+ using System.IO;
1
2
  using System.Text;
2
3
 
3
4
  using NuGetUpdater.Core;
@@ -18,6 +19,8 @@ public partial class EntryPointTests
18
19
  await Run(path =>
19
20
  [
20
21
  "update",
22
+ "--job-path",
23
+ Path.Combine(path, "job.json"),
21
24
  "--repo-root",
22
25
  path,
23
26
  "--solution-or-project",
@@ -119,6 +122,8 @@ public partial class EntryPointTests
119
122
  await Run(path =>
120
123
  [
121
124
  "update",
125
+ "--job-path",
126
+ Path.Combine(path, "job.json"),
122
127
  "--repo-root",
123
128
  path,
124
129
  "--solution-or-project",
@@ -197,6 +202,8 @@ public partial class EntryPointTests
197
202
  await Run(path =>
198
203
  [
199
204
  "update",
205
+ "--job-path",
206
+ Path.Combine(path, "job.json"),
200
207
  "--repo-root",
201
208
  path,
202
209
  "--solution-or-project",
@@ -325,6 +332,7 @@ public partial class EntryPointTests
325
332
  MockNuGetPackage.CreateSimplePackage("Some.Package", "13.0.1", "net8.0"),
326
333
  ];
327
334
  await MockNuGetPackagesInDirectory(testPackages, tempDir.DirectoryPath);
335
+ await MockJobFileInDirectory(tempDir.DirectoryPath);
328
336
 
329
337
  var globalJsonPath = Path.Join(tempDir.DirectoryPath, "global.json");
330
338
  var srcGlobalJsonPath = Path.Join(tempDir.DirectoryPath, "src", "global.json");
@@ -353,6 +361,8 @@ public partial class EntryPointTests
353
361
  IEnumerable<string> executableArgs = [
354
362
  executableName,
355
363
  "update",
364
+ "--job-path",
365
+ Path.Combine(tempDir.DirectoryPath, "job.json"),
356
366
  "--repo-root",
357
367
  tempDir.DirectoryPath,
358
368
  "--solution-or-project",
@@ -402,6 +412,7 @@ public partial class EntryPointTests
402
412
 
403
413
  try
404
414
  {
415
+ await MockJobFileInDirectory(path);
405
416
  await MockNuGetPackagesInDirectory(packages, path);
406
417
 
407
418
  var args = getArgs(path);
@@ -12,7 +12,7 @@ namespace NuGetUpdater.Core.Analyze;
12
12
 
13
13
  using MultiDependency = (string PropertyName, ImmutableArray<string> TargetFrameworks, ImmutableHashSet<string> DependencyNames);
14
14
 
15
- public partial class AnalyzeWorker
15
+ public partial class AnalyzeWorker : IAnalyzeWorker
16
16
  {
17
17
  public const string AnalysisDirectoryName = "./.dependabot/analysis";
18
18
 
@@ -50,7 +50,7 @@ public partial class AnalyzeWorker
50
50
  when (ex.StatusCode == HttpStatusCode.Unauthorized || ex.StatusCode == HttpStatusCode.Forbidden)
51
51
  {
52
52
  var localPath = PathHelper.JoinPath(repoRoot, discovery.Path);
53
- var nugetContext = new NuGetContext(localPath);
53
+ using var nugetContext = new NuGetContext(localPath);
54
54
  analysisResult = new AnalysisResult
55
55
  {
56
56
  ErrorType = ErrorType.AuthenticationFailure,
@@ -24,11 +24,16 @@ internal static class CompatibilityChecker
24
24
  ILogger logger,
25
25
  CancellationToken cancellationToken)
26
26
  {
27
- var (isDevDependency, packageFrameworks) = await GetPackageInfoAsync(
27
+ var packageInfo = await GetPackageInfoAsync(
28
28
  package,
29
29
  nugetContext,
30
30
  cancellationToken);
31
+ if (packageInfo is null)
32
+ {
33
+ return false;
34
+ }
31
35
 
36
+ var (isDevDependency, packageFrameworks) = packageInfo.GetValueOrDefault();
32
37
  return PerformCheck(package, projectFrameworks, isDevDependency, packageFrameworks, logger);
33
38
  }
34
39
 
@@ -70,7 +75,7 @@ internal static class CompatibilityChecker
70
75
  return true;
71
76
  }
72
77
 
73
- internal static async Task<PackageInfo> GetPackageInfoAsync(
78
+ internal static async Task<PackageReaders?> GetPackageReadersAsync(
74
79
  PackageIdentity package,
75
80
  NuGetContext nugetContext,
76
81
  CancellationToken cancellationToken)
@@ -79,7 +84,21 @@ internal static class CompatibilityChecker
79
84
  var readers = File.Exists(tempPackagePath)
80
85
  ? ReadPackage(tempPackagePath)
81
86
  : await DownloadPackageAsync(package, nugetContext, cancellationToken);
87
+ return readers;
88
+ }
89
+
90
+ internal static async Task<PackageInfo?> GetPackageInfoAsync(
91
+ PackageIdentity package,
92
+ NuGetContext nugetContext,
93
+ CancellationToken cancellationToken)
94
+ {
95
+ var readersOption = await GetPackageReadersAsync(package, nugetContext, cancellationToken);
96
+ if (readersOption is null)
97
+ {
98
+ return null;
99
+ }
82
100
 
101
+ var readers = readersOption.GetValueOrDefault();
83
102
  var nuspecStream = await readers.CoreReader.GetNuspecAsync(cancellationToken);
84
103
  var reader = new NuspecReader(nuspecStream);
85
104
 
@@ -127,7 +146,7 @@ internal static class CompatibilityChecker
127
146
  return (archiveReader, archiveReader);
128
147
  }
129
148
 
130
- internal static async Task<PackageReaders> DownloadPackageAsync(
149
+ internal static async Task<PackageReaders?> DownloadPackageAsync(
131
150
  PackageIdentity package,
132
151
  NuGetContext context,
133
152
  CancellationToken cancellationToken)
@@ -179,13 +198,13 @@ internal static class CompatibilityChecker
179
198
  var isDownloaded = await downloader.CopyNupkgFileToAsync(tempPackagePath, cancellationToken);
180
199
  if (!isDownloaded)
181
200
  {
182
- throw new Exception($"Failed to download package [{package.Id}/{package.Version}] from [${source.SourceUri}]");
201
+ continue;
183
202
  }
184
203
 
185
204
  return (downloader.CoreReader, downloader.ContentReader);
186
205
  }
187
206
 
188
- throw new Exception($"Package [{package.Id}/{package.Version}] does not exist in any of the configured sources.");
207
+ return null;
189
208
  }
190
209
 
191
210
  internal static string GetTempPackagePath(PackageIdentity package, NuGetContext context)
@@ -142,11 +142,22 @@ internal record NuGetContext : IDisposable
142
142
  }
143
143
 
144
144
  var metadataResource = await sourceRepository.GetResourceAsync<PackageMetadataResource>(cancellationToken);
145
- var metadata = await metadataResource.GetMetadataAsync(packageIdentity, SourceCacheContext, Logger, cancellationToken);
146
- var url = metadata.ProjectUrl ?? metadata.LicenseUrl;
147
- if (url is not null)
145
+ if (metadataResource is not null)
148
146
  {
149
- return url.ToString();
147
+ try
148
+ {
149
+ var metadata = await metadataResource.GetMetadataAsync(packageIdentity, SourceCacheContext, Logger, cancellationToken);
150
+ var url = metadata.ProjectUrl ?? metadata.LicenseUrl;
151
+ if (url is not null)
152
+ {
153
+ return url.ToString();
154
+ }
155
+ }
156
+ catch (ArgumentException)
157
+ {
158
+ // there was an issue deserializing the package metadata; this doesn't necessarily mean the package
159
+ // is unavailable, just that we can't return a URL
160
+ }
150
161
  }
151
162
  }
152
163
 
@@ -151,46 +151,17 @@ internal static class VersionFinder
151
151
  ILogger logger,
152
152
  CancellationToken cancellationToken)
153
153
  {
154
- var includePrerelease = version.IsPrerelease;
155
-
156
- var sourceMapping = PackageSourceMapping.GetPackageSourceMapping(nugetContext.Settings);
157
- var packageSources = sourceMapping.GetConfiguredPackageSources(packageId).ToHashSet();
158
- var sources = packageSources.Count == 0
159
- ? nugetContext.PackageSources
160
- : nugetContext.PackageSources
161
- .Where(p => packageSources.Contains(p.Name))
162
- .ToImmutableArray();
163
-
164
- foreach (var source in sources)
154
+ // if it can be downloaded, it exists
155
+ var downloader = await CompatibilityChecker.DownloadPackageAsync(new PackageIdentity(packageId, version), nugetContext, cancellationToken);
156
+ var packageAndVersionExists = downloader is not null;
157
+ if (packageAndVersionExists)
165
158
  {
166
- var sourceRepository = Repository.Factory.GetCoreV3(source);
167
- var feed = await sourceRepository.GetResourceAsync<MetadataResource>();
168
- if (feed is null)
169
- {
170
- logger.Log($"Failed to get MetadataResource for [{source.Source}]");
171
- continue;
172
- }
173
-
174
- try
175
- {
176
- // a non-compliant v2 API returning 404 can cause this to throw
177
- var existsInFeed = await feed.Exists(
178
- new PackageIdentity(packageId, version),
179
- includeUnlisted: false,
180
- nugetContext.SourceCacheContext,
181
- NullLogger.Instance,
182
- cancellationToken);
183
- if (existsInFeed)
184
- {
185
- return true;
186
- }
187
- }
188
- catch (FatalProtocolException)
189
- {
190
- // if anything goes wrong here, the package source obviously doesn't contain the requested package
191
- }
159
+ // release the handles
160
+ var readers = downloader.GetValueOrDefault();
161
+ (readers.CoreReader as IDisposable)?.Dispose();
162
+ (readers.ContentReader as IDisposable)?.Dispose();
192
163
  }
193
164
 
194
- return false;
165
+ return packageAndVersionExists;
195
166
  }
196
167
  }
@@ -12,7 +12,7 @@ using NuGetUpdater.Core.Utilities;
12
12
 
13
13
  namespace NuGetUpdater.Core.Discover;
14
14
 
15
- public partial class DiscoveryWorker
15
+ public partial class DiscoveryWorker : IDiscoveryWorker
16
16
  {
17
17
  public const string DiscoveryResultFileName = "./.dependabot/discovery.json";
18
18
 
@@ -58,7 +58,7 @@ public partial class DiscoveryWorker
58
58
  return result;
59
59
  }
60
60
 
61
- internal async Task<WorkspaceDiscoveryResult> RunAsync(string repoRootPath, string workspacePath)
61
+ public async Task<WorkspaceDiscoveryResult> RunAsync(string repoRootPath, string workspacePath)
62
62
  {
63
63
  MSBuildHelper.RegisterMSBuild(Environment.CurrentDirectory, repoRootPath);
64
64
 
@@ -275,22 +275,24 @@ public partial class DiscoveryWorker
275
275
  foreach (var projectPath in projectPaths)
276
276
  {
277
277
  // If there is some MSBuild logic that needs to run to fully resolve the path skip the project
278
- if (!File.Exists(projectPath))
278
+ // Ensure file existence is checked case-insensitively
279
+ var actualProjectPath = PathHelper.ResolveCaseInsensitivePathInsideRepoRoot(projectPath, repoRootPath);
280
+ if (actualProjectPath == null)
279
281
  {
280
282
  continue;
281
283
  }
282
284
 
283
- if (_processedProjectPaths.Contains(projectPath))
285
+ if (_processedProjectPaths.Contains(actualProjectPath))
284
286
  {
285
287
  continue;
286
288
  }
287
- _processedProjectPaths.Add(projectPath);
289
+ _processedProjectPaths.Add(actualProjectPath);
288
290
 
289
- var relativeProjectPath = Path.GetRelativePath(workspacePath, projectPath);
291
+ var relativeProjectPath = Path.GetRelativePath(workspacePath, actualProjectPath);
290
292
  var packagesConfigDependencies = PackagesConfigDiscovery.Discover(workspacePath, projectPath, _logger)
291
293
  ?.Dependencies;
292
294
 
293
- var projectResults = await SdkProjectDiscovery.DiscoverAsync(repoRootPath, workspacePath, projectPath, _logger);
295
+ var projectResults = await SdkProjectDiscovery.DiscoverAsync(repoRootPath, workspacePath, actualProjectPath, _logger);
294
296
 
295
297
  // Determine if there were unrestored MSBuildSdks
296
298
  var msbuildSdks = projectResults.SelectMany(p => p.Dependencies.Where(d => d.Type == DependencyType.MSBuildSdk)).ToImmutableArray();
@@ -299,7 +301,7 @@ public partial class DiscoveryWorker
299
301
  // If new SDKs were restored, then we need to rerun SdkProjectDiscovery.
300
302
  if (await TryRestoreMSBuildSdksAsync(repoRootPath, workspacePath, msbuildSdks, _logger))
301
303
  {
302
- projectResults = await SdkProjectDiscovery.DiscoverAsync(repoRootPath, workspacePath, projectPath, _logger);
304
+ projectResults = await SdkProjectDiscovery.DiscoverAsync(repoRootPath, workspacePath, actualProjectPath, _logger);
303
305
  }
304
306
  }
305
307
 
@@ -0,0 +1,52 @@
1
+ using System.Text.Json;
2
+
3
+ using NuGetUpdater.Core.Run;
4
+
5
+ namespace NuGetUpdater.Core;
6
+
7
+ public record ExperimentsManager
8
+ {
9
+ public bool UseLegacyDependencySolver { get; init; } = false;
10
+
11
+ public static ExperimentsManager GetExperimentsManager(Dictionary<string, object>? experiments)
12
+ {
13
+ return new ExperimentsManager()
14
+ {
15
+ UseLegacyDependencySolver = IsEnabled(experiments, "nuget_legacy_dependency_solver"),
16
+ };
17
+ }
18
+
19
+ public static async Task<ExperimentsManager> FromJobFileAsync(string jobFilePath, ILogger logger)
20
+ {
21
+ var jobFileContent = await File.ReadAllTextAsync(jobFilePath);
22
+ try
23
+ {
24
+ var jobWrapper = RunWorker.Deserialize(jobFileContent);
25
+ return GetExperimentsManager(jobWrapper.Job.Experiments);
26
+ }
27
+ catch (JsonException ex)
28
+ {
29
+ // the following message has been specifically designed to match the format of `Dependabot.logger.info(...)` from Ruby
30
+ logger.Log($"{DateTime.UtcNow:yyyy/MM/dd HH:mm:ss} INFO Error deserializing job file: {ex.ToString()}: {jobFileContent}");
31
+ return new ExperimentsManager();
32
+ }
33
+ }
34
+
35
+ private static bool IsEnabled(Dictionary<string, object>? experiments, string experimentName)
36
+ {
37
+ if (experiments is null)
38
+ {
39
+ return false;
40
+ }
41
+
42
+ if (experiments.TryGetValue(experimentName, out var value))
43
+ {
44
+ if ((value?.ToString() ?? "").Equals("true", StringComparison.OrdinalIgnoreCase))
45
+ {
46
+ return true;
47
+ }
48
+ }
49
+
50
+ return false;
51
+ }
52
+ }
@@ -43,7 +43,7 @@ internal sealed class ProjectBuildFile : XmlBuildFile
43
43
  {
44
44
  var sdkDependencies = GetSdkDependencies();
45
45
  var packageDependencies = PackageItemNodes
46
- .Select(GetPackageDependency)
46
+ .SelectMany(e => GetPackageDependencies(e) ?? Enumerable.Empty<Dependency>())
47
47
  .OfType<Dependency>();
48
48
  return sdkDependencies.Concat(packageDependencies);
49
49
  }
@@ -89,8 +89,9 @@ internal sealed class ProjectBuildFile : XmlBuildFile
89
89
  : new Dependency(name, version, DependencyType.MSBuildSdk);
90
90
  }
91
91
 
92
- private static Dependency? GetPackageDependency(IXmlElementSyntax element)
92
+ private static IEnumerable<Dependency>? GetPackageDependencies(IXmlElementSyntax element)
93
93
  {
94
+ List<Dependency> dependencies = [];
94
95
  var isUpdate = false;
95
96
 
96
97
  var name = element.GetAttributeOrSubElementValue("Include", StringComparison.OrdinalIgnoreCase)?.Trim();
@@ -113,12 +114,18 @@ internal sealed class ProjectBuildFile : XmlBuildFile
113
114
  isVersionOverride = version is not null;
114
115
  }
115
116
 
116
- return new Dependency(
117
- Name: name,
118
- Version: version?.Length == 0 ? null : version,
119
- Type: GetDependencyType(element.Name),
120
- IsUpdate: isUpdate,
121
- IsOverride: isVersionOverride);
117
+ dependencies.AddRange(
118
+ name.Split(';', StringSplitOptions.RemoveEmptyEntries)
119
+ .Select(dep => new Dependency(
120
+ Name: dep.Trim(),
121
+ Version: string.IsNullOrEmpty(version) ? null : version,
122
+ Type: GetDependencyType(element.Name),
123
+ IsUpdate: isUpdate,
124
+ IsOverride: isVersionOverride))
125
+ );
126
+
127
+
128
+ return dependencies;
122
129
  }
123
130
 
124
131
  private static DependencyType GetDependencyType(string name)
@@ -0,0 +1,9 @@
1
+ using NuGetUpdater.Core.Analyze;
2
+ using NuGetUpdater.Core.Discover;
3
+
4
+ namespace NuGetUpdater.Core;
5
+
6
+ public interface IAnalyzeWorker
7
+ {
8
+ Task<AnalysisResult> RunAsync(string repoRoot, WorkspaceDiscoveryResult discovery, DependencyInfo dependencyInfo);
9
+ }
@@ -0,0 +1,8 @@
1
+ using NuGetUpdater.Core.Discover;
2
+
3
+ namespace NuGetUpdater.Core;
4
+
5
+ public interface IDiscoveryWorker
6
+ {
7
+ Task<WorkspaceDiscoveryResult> RunAsync(string repoRootPath, string workspacePath);
8
+ }
@@ -0,0 +1,9 @@
1
+
2
+ using NuGetUpdater.Core.Updater;
3
+
4
+ namespace NuGetUpdater.Core;
5
+
6
+ public interface IUpdaterWorker
7
+ {
8
+ Task<UpdateOperationResult> RunAsync(string repoRootPath, string workspacePath, string dependencyName, string previousDependencyVersion, string newDependencyVersion, bool isTransitive);
9
+ }