dependabot-nuget 0.285.0 → 0.287.0

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