dependabot-nuget 0.323.0 → 0.325.1

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 (80) hide show
  1. checksums.yaml +4 -4
  2. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Program.cs +0 -4
  3. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/AnalyzeWorker.cs +1 -31
  4. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/DependencyFinder.cs +0 -3
  5. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/DependencyInfo.cs +1 -0
  6. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/VersionFinder.cs +64 -10
  7. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Clone/CloneWorker.cs +1 -1
  8. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/DependencySolver/MSBuildDependencySolver.cs +10 -4
  9. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/DiscoveryWorker.cs +4 -4
  10. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/PackagesConfigDiscovery.cs +2 -2
  11. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/SdkProjectDiscovery.cs +31 -41
  12. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/ExperimentsManager.cs +3 -6
  13. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/Cooldown.cs +83 -0
  14. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/Job.cs +2 -1
  15. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ModifiedFilesTracker.cs +9 -1
  16. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/PullRequestBodyGenerator/DetailedPullRequestBodyGenerator.cs +6 -0
  17. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/RunWorker.cs +8 -1
  18. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/UpdateHandlers/CreateSecurityUpdatePullRequestHandler.cs +1 -1
  19. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/UpdateHandlers/GroupUpdateAllVersionsHandler.cs +79 -67
  20. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/UpdateHandlers/RefreshGroupUpdatePullRequestHandler.cs +1 -1
  21. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/UpdateHandlers/RefreshSecurityUpdatePullRequestHandler.cs +1 -1
  22. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/UpdateHandlers/RefreshVersionUpdatePullRequestHandler.cs +1 -1
  23. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/FileWriters/FileWriterWorker.cs +10 -7
  24. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/FileWriters/XmlFileWriter.cs +245 -125
  25. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/LockFileUpdater.cs +4 -11
  26. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackageReferenceUpdater.cs +4 -5
  27. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/UpdaterWorker.cs +1 -1
  28. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/DependencyConflictResolver.cs +2 -2
  29. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +14 -31
  30. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/NuGetHelper.cs +3 -5
  31. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/ProcessExtensions.cs +12 -13
  32. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/XmlExtensions.cs +3 -3
  33. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/AnalyzeWorkerTests.cs +78 -2
  34. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/VersionFinderTests.cs +126 -3
  35. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Clone/CloneWorkerTests.cs +14 -0
  36. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/DependencySolver/MSBuildDependencySolverTests.cs +1 -2
  37. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTestBase.cs +2 -2
  38. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.Project.cs +1 -2
  39. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.cs +0 -6
  40. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/SdkProjectDiscoveryTests.cs +2 -3
  41. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/MockNuGetPackage.cs +1 -2
  42. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/ApiModel/CooldownTests.cs +99 -0
  43. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/MiscellaneousTests.cs +168 -4
  44. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/PullRequestBodyGenerator/DetailedPullRequestBodyGeneratorTests.cs +71 -0
  45. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/SerializationTests.cs +71 -0
  46. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/UpdateHandlers/GroupUpdateAllVersionsHandlerTests.cs +70 -39
  47. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/FileWriters/FileWriterWorkerTests.cs +43 -30
  48. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/FileWriters/XmlFileWriterTests.cs +76 -3
  49. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/PackageReferenceUpdaterTests.cs +0 -2
  50. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/MSBuildHelperTests.cs +11 -27
  51. data/lib/dependabot/nuget.rb +3 -11
  52. metadata +8 -54
  53. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/AnalyzeCommand.cs +0 -49
  54. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/DiscoverCommand.cs +0 -60
  55. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/FrameworkCheckCommand.cs +0 -35
  56. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/UpdateCommand.cs +0 -58
  57. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Analyze.cs +0 -380
  58. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Discover.cs +0 -557
  59. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.FrameworkCheck.cs +0 -37
  60. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Update.cs +0 -226
  61. data/lib/dependabot/nuget/analysis/analysis_json_reader.rb +0 -65
  62. data/lib/dependabot/nuget/analysis/dependency_analysis.rb +0 -66
  63. data/lib/dependabot/nuget/cache_manager.rb +0 -29
  64. data/lib/dependabot/nuget/discovery/dependency_details.rb +0 -102
  65. data/lib/dependabot/nuget/discovery/dependency_file_discovery.rb +0 -122
  66. data/lib/dependabot/nuget/discovery/discovery_json_reader.rb +0 -266
  67. data/lib/dependabot/nuget/discovery/evaluation_details.rb +0 -63
  68. data/lib/dependabot/nuget/discovery/project_discovery.rb +0 -104
  69. data/lib/dependabot/nuget/discovery/property_details.rb +0 -43
  70. data/lib/dependabot/nuget/discovery/workspace_discovery.rb +0 -61
  71. data/lib/dependabot/nuget/file_fetcher.rb +0 -46
  72. data/lib/dependabot/nuget/file_parser.rb +0 -153
  73. data/lib/dependabot/nuget/file_updater.rb +0 -256
  74. data/lib/dependabot/nuget/language.rb +0 -98
  75. data/lib/dependabot/nuget/metadata_finder.rb +0 -197
  76. data/lib/dependabot/nuget/native_helpers.rb +0 -364
  77. data/lib/dependabot/nuget/nuget_config_credential_helpers.rb +0 -88
  78. data/lib/dependabot/nuget/package_manager.rb +0 -51
  79. data/lib/dependabot/nuget/update_checker/requirements_updater.rb +0 -105
  80. data/lib/dependabot/nuget/update_checker.rb +0 -210
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 26ba648681c32dbfe7954c99639f36e5821f0560fc4f635909948679d3a6f096
4
- data.tar.gz: b60ee9bb959e0fd7f28cee03715ae92e44c95628ced1c0995031cc251cdf2248
3
+ metadata.gz: 98a3aaadac2374836aa138812ba65d5bdb7da68196ae992a85aa6d36c83e9a0d
4
+ data.tar.gz: 0db65a955c849ec34d031dc2f27898e882b716e6ce461d6d6f5c7725d4670780
5
5
  SHA512:
6
- metadata.gz: eb9f9dad2d2371cce1b482afcf3178ba465617e76dc1c616a5c7345b7c91717def61586f55aeb05afc04979c866f172ec5e82e64cc1de2722754d5301f6ce0bd
7
- data.tar.gz: aaa8de59bd6a7741139c095f62527bdb64d76e23e64f1ab224ef8548d9847e72edf34e1dd6089ec7a0042798bcc1e51f63deb05b065267c96f4678aca6bdf8ab
6
+ metadata.gz: 1df7608093901ee09ae6217b3873c8e5065b6699cb2224f7f638a6b4fc7161ab127c27a6717f80ab187d6186db7f0aed63023605f2cb53940061814dc26d7fee
7
+ data.tar.gz: 2e8023ea7b48ede332ad91b2e94531e27bf5a64c6a5f3387a74eef9c83b5f161613b3eb78cc7d2c46145cf7666d62b17184e5d07096b7f5c66c425b97b96e295
@@ -19,10 +19,6 @@ internal sealed class Program
19
19
  var command = new RootCommand
20
20
  {
21
21
  CloneCommand.GetCommand(setExitCode),
22
- FrameworkCheckCommand.GetCommand(setExitCode),
23
- DiscoverCommand.GetCommand(setExitCode),
24
- AnalyzeCommand.GetCommand(setExitCode),
25
- UpdateCommand.GetCommand(setExitCode),
26
22
  RunCommand.GetCommand(setExitCode),
27
23
  };
28
24
  command.TreatUnmatchedTokensAsErrors = true;
@@ -143,7 +143,6 @@ public partial class AnalyzeWorker : IAnalyzeWorker
143
143
  dependenciesToUpdate,
144
144
  updatedVersion,
145
145
  nugetContext,
146
- _experimentsManager,
147
146
  _logger,
148
147
  CancellationToken.None);
149
148
  }
@@ -238,6 +237,7 @@ public partial class AnalyzeWorker : IAnalyzeWorker
238
237
  var versionResult = await VersionFinder.GetVersionsAsync(
239
238
  projectFrameworks,
240
239
  dependencyInfo,
240
+ DateTimeOffset.UtcNow,
241
241
  nugetContext,
242
242
  logger,
243
243
  cancellationToken);
@@ -253,34 +253,6 @@ public partial class AnalyzeWorker : IAnalyzeWorker
253
253
  cancellationToken);
254
254
  }
255
255
 
256
- internal static async Task<NuGetVersion?> FindUpdatedVersionAsync(
257
- ImmutableHashSet<string> packageIds,
258
- ImmutableArray<NuGetFramework> projectFrameworks,
259
- NuGetVersion version,
260
- bool findLowestVersion,
261
- NuGetContext nugetContext,
262
- ILogger logger,
263
- CancellationToken cancellationToken)
264
- {
265
- var versionResult = await VersionFinder.GetVersionsAsync(
266
- projectFrameworks,
267
- packageIds.First(),
268
- version,
269
- nugetContext,
270
- logger,
271
- cancellationToken);
272
-
273
- return await FindUpdatedVersionAsync(
274
- packageIds,
275
- version.ToNormalizedString(),
276
- versionResult,
277
- projectFrameworks,
278
- findLowestVersion,
279
- nugetContext,
280
- logger,
281
- cancellationToken);
282
- }
283
-
284
256
  internal static async Task<NuGetVersion?> FindUpdatedVersionAsync(
285
257
  ImmutableHashSet<string> packageIds,
286
258
  string versionString,
@@ -397,7 +369,6 @@ public partial class AnalyzeWorker : IAnalyzeWorker
397
369
  ImmutableHashSet<string> packageIds,
398
370
  NuGetVersion updatedVersion,
399
371
  NuGetContext nugetContext,
400
- ExperimentsManager experimentsManager,
401
372
  ILogger logger,
402
373
  CancellationToken cancellationToken)
403
374
  {
@@ -438,7 +409,6 @@ public partial class AnalyzeWorker : IAnalyzeWorker
438
409
  packageIds,
439
410
  updatedVersion,
440
411
  nugetContext,
441
- experimentsManager,
442
412
  logger,
443
413
  cancellationToken);
444
414
 
@@ -1,6 +1,5 @@
1
1
  using System.Collections.Immutable;
2
2
 
3
- using NuGet.Frameworks;
4
3
  using NuGet.Versioning;
5
4
 
6
5
  namespace NuGetUpdater.Core.Analyze;
@@ -14,7 +13,6 @@ internal static class DependencyFinder
14
13
  ImmutableHashSet<string> packageIds,
15
14
  NuGetVersion version,
16
15
  NuGetContext nugetContext,
17
- ExperimentsManager experimentsManager,
18
16
  ILogger logger,
19
17
  CancellationToken cancellationToken)
20
18
  {
@@ -31,7 +29,6 @@ internal static class DependencyFinder
31
29
  projectPath,
32
30
  framework,
33
31
  packages,
34
- experimentsManager,
35
32
  logger);
36
33
  var updatedDependencies = new List<Dependency>();
37
34
  foreach (var dependency in dependencies)
@@ -12,4 +12,5 @@ public sealed record DependencyInfo
12
12
  public ImmutableArray<Requirement> IgnoredVersions { get; init; }
13
13
  public ImmutableArray<SecurityVulnerability> Vulnerabilities { get; init; }
14
14
  public ImmutableArray<ConditionUpdateType> IgnoredUpdateTypes { get; init; } = [];
15
+ public Cooldown? Cooldown { get; init; } = null;
15
16
  }
@@ -16,39 +16,67 @@ namespace NuGetUpdater.Core.Analyze;
16
16
 
17
17
  internal static class VersionFinder
18
18
  {
19
+ public static Task<VersionResult> GetVersionsByNameAsync(
20
+ ImmutableArray<NuGetFramework> projectTfms,
21
+ string dependencyName,
22
+ NuGetVersion currentVersion,
23
+ NuGetContext nugetContext,
24
+ ILogger logger,
25
+ CancellationToken cancellationToken
26
+ )
27
+ {
28
+ var dependencyInfo = new DependencyInfo()
29
+ {
30
+ Name = dependencyName,
31
+ Version = currentVersion.ToString(),
32
+ IsVulnerable = false,
33
+ };
34
+ return GetVersionsAsync(
35
+ projectTfms,
36
+ dependencyInfo,
37
+ currentVersion,
38
+ DateTime.UtcNow,
39
+ nugetContext,
40
+ logger,
41
+ cancellationToken
42
+ );
43
+ }
44
+
19
45
  public static Task<VersionResult> GetVersionsAsync(
20
46
  ImmutableArray<NuGetFramework> projectTfms,
21
- string packageId,
47
+ DependencyInfo dependencyInfo,
22
48
  NuGetVersion currentVersion,
49
+ DateTimeOffset currentTime,
23
50
  NuGetContext nugetContext,
24
51
  ILogger logger,
25
52
  CancellationToken cancellationToken)
26
53
  {
27
54
  var versionFilter = CreateVersionFilter(currentVersion);
28
55
 
29
- return GetVersionsAsync(projectTfms, packageId, currentVersion, versionFilter, nugetContext, logger, cancellationToken);
56
+ return GetVersionsAsync(projectTfms, dependencyInfo, currentVersion, versionFilter, currentTime, nugetContext, logger, cancellationToken);
30
57
  }
31
58
 
32
59
  public static Task<VersionResult> GetVersionsAsync(
33
60
  ImmutableArray<NuGetFramework> projectTfms,
34
61
  DependencyInfo dependencyInfo,
62
+ DateTimeOffset currentTime,
35
63
  NuGetContext nugetContext,
36
64
  ILogger logger,
37
65
  CancellationToken cancellationToken)
38
66
  {
39
- var packageId = dependencyInfo.Name;
40
67
  var versionRange = VersionRange.Parse(dependencyInfo.Version);
41
68
  var currentVersion = versionRange.MinVersion!;
42
69
  var versionFilter = CreateVersionFilter(dependencyInfo, versionRange);
43
70
 
44
- return GetVersionsAsync(projectTfms, packageId, currentVersion, versionFilter, nugetContext, logger, cancellationToken);
71
+ return GetVersionsAsync(projectTfms, dependencyInfo, currentVersion, versionFilter, currentTime, nugetContext, logger, cancellationToken);
45
72
  }
46
73
 
47
74
  public static async Task<VersionResult> GetVersionsAsync(
48
75
  ImmutableArray<NuGetFramework> projectTfms,
49
- string packageId,
76
+ DependencyInfo dependencyInfo,
50
77
  NuGetVersion currentVersion,
51
78
  Func<NuGetVersion, bool> versionFilter,
79
+ DateTimeOffset currentTime,
52
80
  NuGetContext nugetContext,
53
81
  ILogger logger,
54
82
  CancellationToken cancellationToken)
@@ -57,7 +85,7 @@ internal static class VersionFinder
57
85
  VersionResult result = new(currentVersion);
58
86
 
59
87
  var sourceMapping = PackageSourceMapping.GetPackageSourceMapping(nugetContext.Settings);
60
- var packageSources = sourceMapping.GetConfiguredPackageSources(packageId).ToHashSet();
88
+ var packageSources = sourceMapping.GetConfiguredPackageSources(dependencyInfo.Name).ToHashSet();
61
89
  var sources = packageSources.Count == 0
62
90
  ? nugetContext.PackageSources
63
91
  : nugetContext.PackageSources
@@ -67,6 +95,7 @@ internal static class VersionFinder
67
95
  foreach (var source in sources)
68
96
  {
69
97
  MetadataResource? feed = null;
98
+ PackageMetadataResource? metadataResource = null;
70
99
  try
71
100
  {
72
101
  var sourceRepository = Repository.Factory.GetCoreV3(source);
@@ -84,9 +113,19 @@ internal static class VersionFinder
84
113
  continue;
85
114
  }
86
115
 
116
+ if (dependencyInfo.Cooldown is not null)
117
+ {
118
+ metadataResource = await sourceRepository.GetResourceAsync<PackageMetadataResource>();
119
+ if (metadataResource is null)
120
+ {
121
+ logger.Warn($"Failed to get {nameof(PackageMetadataResource)} for [{source.Source}]");
122
+ continue;
123
+ }
124
+ }
125
+
87
126
  // a non-compliant v2 API returning 404 can cause this to throw
88
127
  var existsInFeed = await feed.Exists(
89
- packageId,
128
+ dependencyInfo.Name,
90
129
  includePrerelease,
91
130
  includeUnlisted: false,
92
131
  nugetContext.SourceCacheContext,
@@ -109,7 +148,7 @@ internal static class VersionFinder
109
148
  }
110
149
 
111
150
  var feedVersions = (await feed.GetVersions(
112
- packageId,
151
+ dependencyInfo.Name,
113
152
  includePrerelease,
114
153
  includeUnlisted: false,
115
154
  nugetContext.SourceCacheContext,
@@ -124,15 +163,30 @@ internal static class VersionFinder
124
163
  var versions = feedVersions.Where(versionFilter).ToArray();
125
164
  foreach (var version in versions)
126
165
  {
166
+ var packageIdentity = new PackageIdentity(dependencyInfo.Name, version);
167
+
168
+ // check tfm
127
169
  var isTfmCompatible = await CompatibilityChecker.CheckAsync(
128
- new PackageIdentity(packageId, version),
170
+ packageIdentity,
129
171
  projectTfms,
130
172
  nugetContext,
131
173
  logger,
132
174
  CancellationToken.None);
175
+
176
+ // dotnet-tools.json and global.json packages won't specify a TFM, so they're always compatible
133
177
  if (isTfmCompatible || projectTfms.IsEmpty)
134
178
  {
135
- // dotnet-tools.json and global.json packages won't specify a TFM, so they're always compatible
179
+ // check date
180
+ if (dependencyInfo.Cooldown is not null)
181
+ {
182
+ var metadata = await metadataResource!.GetMetadataAsync(packageIdentity, nugetContext.SourceCacheContext, NullLogger.Instance, CancellationToken.None);
183
+ if (!dependencyInfo.Cooldown.IsVersionUpdateAllowed(currentTime, metadata?.Published, currentVersion, version))
184
+ {
185
+ logger.Info($"Skipping update of {dependencyInfo.Name} from {currentVersion} to {version} due to cooldown settings. Package publish date: {metadata?.Published}");
186
+ continue;
187
+ }
188
+ }
189
+
136
190
  result.Add(source, version);
137
191
  }
138
192
  }
@@ -170,7 +170,7 @@ public class CloneWorker
170
170
  {
171
171
  return job.Source.Provider switch
172
172
  {
173
- "azure" when !string.IsNullOrWhiteSpace(job.Source.Hostname) => $"https://{job.Source.Hostname}/{job.Source.Repo}",
173
+ "azure" or "github" when !string.IsNullOrWhiteSpace(job.Source.Hostname) => $"https://{job.Source.Hostname}/{job.Source.Repo}",
174
174
  "azure" => $"https://dev.azure.com/{job.Source.Repo}",
175
175
  "github" => $"https://github.com/{job.Source.Repo}",
176
176
  _ => throw new ArgumentException($"Unknown provider: {job.Source.Provider}")
@@ -1,31 +1,37 @@
1
1
  using System.Collections.Immutable;
2
2
 
3
+ using NuGetUpdater.Core.Updater.FileWriters;
4
+
3
5
  namespace NuGetUpdater.Core.DependencySolver;
4
6
 
5
7
  public class MSBuildDependencySolver : IDependencySolver
6
8
  {
7
9
  private readonly DirectoryInfo _repoContentsPath;
8
10
  private readonly FileInfo _projectPath;
9
- private readonly ExperimentsManager _experimentsManager;
10
11
  private readonly ILogger _logger;
11
12
 
12
- public MSBuildDependencySolver(DirectoryInfo repoContentsPath, FileInfo projectPath, ExperimentsManager experimentsManager, ILogger logger)
13
+ public MSBuildDependencySolver(DirectoryInfo repoContentsPath, FileInfo projectPath, ILogger logger)
13
14
  {
14
15
  _repoContentsPath = repoContentsPath;
15
16
  _projectPath = projectPath;
16
- _experimentsManager = experimentsManager;
17
17
  _logger = logger;
18
18
  }
19
19
 
20
20
  public async Task<ImmutableArray<Dependency>?> SolveAsync(ImmutableArray<Dependency> existingTopLevelDependencies, ImmutableArray<Dependency> desiredDependencies, string targetFramework)
21
21
  {
22
+ var projectExtension = _projectPath.Extension.ToLowerInvariant();
23
+ if (!XmlFileWriter.SupportedProjectFileExtensions.Contains(projectExtension))
24
+ {
25
+ // not a real project, nothing to solve.
26
+ return null;
27
+ }
28
+
22
29
  var result = await MSBuildHelper.ResolveDependencyConflicts(
23
30
  _repoContentsPath.FullName,
24
31
  _projectPath.FullName,
25
32
  targetFramework,
26
33
  existingTopLevelDependencies,
27
34
  desiredDependencies,
28
- _experimentsManager,
29
35
  _logger);
30
36
  return result;
31
37
  }
@@ -160,7 +160,7 @@ public partial class DiscoveryWorker : IDiscoveryWorker
160
160
 
161
161
  _logger.Info($" Restoring MSBuild SDKs: {string.Join(", ", keys)}");
162
162
 
163
- return await NuGetHelper.DownloadNuGetPackagesAsync(repoRootPath, workspacePath, msbuildSdks, _experimentsManager, logger);
163
+ return await NuGetHelper.DownloadNuGetPackagesAsync(repoRootPath, workspacePath, msbuildSdks, logger);
164
164
  }
165
165
 
166
166
  private async Task<ImmutableArray<ProjectDiscoveryResult>> RunForDirectoryAsync(string repoRootPath, string workspacePath)
@@ -335,8 +335,8 @@ public partial class DiscoveryWorker : IDiscoveryWorker
335
335
  _processedProjectPaths.Add(projectPath);
336
336
 
337
337
  var relativeProjectPath = Path.GetRelativePath(workspacePath, projectPath).NormalizePathToUnix();
338
- var packagesConfigResult = await PackagesConfigDiscovery.Discover(repoRootPath, workspacePath, projectPath, _experimentsManager, _logger);
339
- var projectResults = await SdkProjectDiscovery.DiscoverAsync(repoRootPath, workspacePath, projectPath, _experimentsManager, _logger);
338
+ var packagesConfigResult = await PackagesConfigDiscovery.Discover(repoRootPath, workspacePath, projectPath, _logger);
339
+ var projectResults = await SdkProjectDiscovery.DiscoverAsync(repoRootPath, workspacePath, projectPath, _logger);
340
340
 
341
341
  // Determine if there were unrestored MSBuildSdks
342
342
  var msbuildSdks = projectResults.SelectMany(p => p.Dependencies.Where(d => d.Type == DependencyType.MSBuildSdk)).ToImmutableArray();
@@ -345,7 +345,7 @@ public partial class DiscoveryWorker : IDiscoveryWorker
345
345
  // If new SDKs were restored, then we need to rerun SdkProjectDiscovery.
346
346
  if (await TryRestoreMSBuildSdksAsync(repoRootPath, workspacePath, msbuildSdks, _logger))
347
347
  {
348
- projectResults = await SdkProjectDiscovery.DiscoverAsync(repoRootPath, workspacePath, projectPath, _experimentsManager, _logger);
348
+ projectResults = await SdkProjectDiscovery.DiscoverAsync(repoRootPath, workspacePath, projectPath, _logger);
349
349
  }
350
350
  }
351
351
 
@@ -6,7 +6,7 @@ namespace NuGetUpdater.Core.Discover;
6
6
 
7
7
  internal static class PackagesConfigDiscovery
8
8
  {
9
- public static async Task<PackagesConfigDiscoveryResult?> Discover(string repoRootPath, string workspacePath, string projectPath, ExperimentsManager experimentsManager, ILogger logger)
9
+ public static async Task<PackagesConfigDiscoveryResult?> Discover(string repoRootPath, string workspacePath, string projectPath, ILogger logger)
10
10
  {
11
11
  var projectDirectory = Path.GetDirectoryName(projectPath)!;
12
12
  var additionalFiles = ProjectHelper.GetAllAdditionalFilesFromProject(projectPath, ProjectHelper.PathFormat.Full);
@@ -27,7 +27,7 @@ internal static class PackagesConfigDiscovery
27
27
  .ToImmutableArray();
28
28
 
29
29
  // generate `$(TargetFramework)` via MSBuild
30
- var tfms = await MSBuildHelper.GetTargetFrameworkValuesFromProject(repoRootPath, projectPath, experimentsManager, logger);
30
+ var tfms = await MSBuildHelper.GetTargetFrameworkValuesFromProject(repoRootPath, projectPath, logger);
31
31
 
32
32
  var additionalFilesRelative = additionalFiles.Select(p => Path.GetRelativePath(projectDirectory, p).NormalizePathToUnix()).ToImmutableArray();
33
33
  return new()
@@ -53,7 +53,7 @@ internal static class SdkProjectDiscovery
53
53
  "web.config",
54
54
  };
55
55
 
56
- public static async Task<ImmutableArray<ProjectDiscoveryResult>> DiscoverAsync(string repoRootPath, string workspacePath, string startingProjectPath, ExperimentsManager experimentsManager, ILogger logger)
56
+ public static async Task<ImmutableArray<ProjectDiscoveryResult>> DiscoverAsync(string repoRootPath, string workspacePath, string startingProjectPath, ILogger logger)
57
57
  {
58
58
  // N.b., there are many paths used in this function. The MSBuild binary log always reports fully qualified paths, so that's what will be used
59
59
  // throughout until the very end when the appropriate kind of relative path is returned.
@@ -91,45 +91,40 @@ internal static class SdkProjectDiscovery
91
91
  // projectPath additionalFiles
92
92
 
93
93
  var requiresManualPackageResolution = false;
94
- var tfms = await MSBuildHelper.GetTargetFrameworkValuesFromProject(repoRootPath, startingProjectPath, experimentsManager, logger);
94
+ var tfms = await MSBuildHelper.GetTargetFrameworkValuesFromProject(repoRootPath, startingProjectPath, logger);
95
95
  foreach (var tfm in tfms)
96
96
  {
97
97
  // create a binlog
98
98
  var binLogPath = Path.Combine(Path.GetTempPath(), $"msbuild_{Guid.NewGuid():d}.binlog");
99
99
  try
100
100
  {
101
- // TODO: once the updater image has all relevant SDKs installed, we won't have to sideline global.json anymore
102
- var (exitCode, stdOut, stdErr) = await MSBuildHelper.HandleGlobalJsonAsync(startingProjectDirectory, repoRootPath, experimentsManager, async () =>
101
+ // the built-in target `GenerateBuildDependencyFile` forces resolution of all NuGet packages, but doesn't invoke a full build
102
+ var dependencyDiscoveryTargetingPacksPropsPath = MSBuildHelper.GetFileFromRuntimeDirectory("DependencyDiscoveryTargetingPacks.props");
103
+ var dependencyDiscoveryTargetsPath = MSBuildHelper.GetFileFromRuntimeDirectory("DependencyDiscovery.targets");
104
+ var args = new List<string>()
103
105
  {
104
- // the built-in target `GenerateBuildDependencyFile` forces resolution of all NuGet packages, but doesn't invoke a full build
105
- var dependencyDiscoveryTargetingPacksPropsPath = MSBuildHelper.GetFileFromRuntimeDirectory("DependencyDiscoveryTargetingPacks.props");
106
- var dependencyDiscoveryTargetsPath = MSBuildHelper.GetFileFromRuntimeDirectory("DependencyDiscovery.targets");
107
- var args = new List<string>()
108
- {
109
- "build",
110
- startingProjectPath,
111
- "/t:_DiscoverDependencies",
112
- $"/p:TargetFramework={tfm}",
113
- $"/p:CustomBeforeMicrosoftCommonProps={dependencyDiscoveryTargetingPacksPropsPath}",
114
- $"/p:CustomAfterMicrosoftCommonCrossTargetingTargets={dependencyDiscoveryTargetsPath}",
115
- $"/p:CustomAfterMicrosoftCommonTargets={dependencyDiscoveryTargetsPath}",
116
- "/p:TreatWarningsAsErrors=false", // if using CPM and a project also sets TreatWarningsAsErrors to true, this can cause discovery to fail; explicitly don't allow that
117
- "/p:MSBuildTreatWarningsAsErrors=false",
118
- $"/bl:{binLogPath}"
119
- };
120
- var (exitCode, stdOut, stdErr) = await ProcessEx.RunDotnetWithoutMSBuildEnvironmentVariablesAsync(args, startingProjectDirectory, experimentsManager);
121
- if (exitCode != 0 && stdOut.Contains("error : Object reference not set to an instance of an object."))
122
- {
123
- // https://github.com/NuGet/Home/issues/11761#issuecomment-1105218996
124
- // Due to a bug in NuGet, there can be a null reference exception thrown and adding this command line argument will work around it,
125
- // but this argument can't always be added; it can cause problems in other instances, so we're taking the approach of not using it
126
- // unless we have to.
127
- args.Add("/RestoreProperty:__Unused__=__Unused__");
128
- (exitCode, stdOut, stdErr) = await ProcessEx.RunDotnetWithoutMSBuildEnvironmentVariablesAsync(args, startingProjectDirectory, experimentsManager);
129
- }
106
+ "build",
107
+ startingProjectPath,
108
+ "/t:_DiscoverDependencies",
109
+ $"/p:TargetFramework={tfm}",
110
+ $"/p:CustomBeforeMicrosoftCommonProps={dependencyDiscoveryTargetingPacksPropsPath}",
111
+ $"/p:CustomAfterMicrosoftCommonCrossTargetingTargets={dependencyDiscoveryTargetsPath}",
112
+ $"/p:CustomAfterMicrosoftCommonTargets={dependencyDiscoveryTargetsPath}",
113
+ "/p:TreatWarningsAsErrors=false", // if using CPM and a project also sets TreatWarningsAsErrors to true, this can cause discovery to fail; explicitly don't allow that
114
+ "/p:MSBuildTreatWarningsAsErrors=false",
115
+ $"/bl:{binLogPath}"
116
+ };
117
+ var (exitCode, stdOut, stdErr) = await ProcessEx.RunDotnetWithoutMSBuildEnvironmentVariablesAsync(args, startingProjectDirectory);
118
+ if (exitCode != 0 && stdOut.Contains("error : Object reference not set to an instance of an object."))
119
+ {
120
+ // https://github.com/NuGet/Home/issues/11761#issuecomment-1105218996
121
+ // Due to a bug in NuGet, there can be a null reference exception thrown and adding this command line argument will work around it,
122
+ // but this argument can't always be added; it can cause problems in other instances, so we're taking the approach of not using it
123
+ // unless we have to.
124
+ args.Add("/RestoreProperty:__Unused__=__Unused__");
125
+ (exitCode, stdOut, stdErr) = await ProcessEx.RunDotnetWithoutMSBuildEnvironmentVariablesAsync(args, startingProjectDirectory);
126
+ }
130
127
 
131
- return (exitCode, stdOut, stdErr);
132
- }, logger, retainMSBuildSdks: true);
133
128
  MSBuildHelper.ThrowOnError(stdOut);
134
129
  if (stdOut.Contains("_DependencyDiscovery_LegacyProjects::UseTemporaryProject"))
135
130
  {
@@ -185,7 +180,7 @@ internal static class SdkProjectDiscovery
185
180
  }
186
181
  break;
187
182
  case NamedNode namedNode when namedNode is AddItem or RemoveItem:
188
- ProcessResolvedPackageReference(namedNode, packagesPerProject, topLevelPackagesPerProject, explicitPackageVersionsPerProject, experimentsManager);
183
+ ProcessResolvedPackageReference(namedNode, packagesPerProject, topLevelPackagesPerProject, explicitPackageVersionsPerProject);
189
184
 
190
185
  if (namedNode is AddItem addItem)
191
186
  {
@@ -247,8 +242,6 @@ internal static class SdkProjectDiscovery
247
242
  }
248
243
  break;
249
244
  case Target target when target.Name == "_HandlePackageFileConflicts":
250
- // this only works if we've installed the exact SDK required
251
- if (experimentsManager.InstallDotnetSdks)
252
245
  {
253
246
  var projectEvaluation = GetNearestProjectEvaluation(target);
254
247
  if (projectEvaluation is null)
@@ -340,7 +333,6 @@ internal static class SdkProjectDiscovery
340
333
  tfms,
341
334
  packagesPerProject,
342
335
  explicitPackageVersionsPerProject,
343
- experimentsManager,
344
336
  logger
345
337
  );
346
338
  }
@@ -565,7 +557,6 @@ internal static class SdkProjectDiscovery
565
557
  ImmutableArray<string> targetFrameworks,
566
558
  Dictionary<string, Dictionary<string, Dictionary<string, string>>> packagesPerProject,
567
559
  Dictionary<string, Dictionary<string, Dictionary<string, string>>> explicitPackageVersionsPerProject,
568
- ExperimentsManager experimentsManager,
569
560
  ILogger logger
570
561
  )
571
562
  {
@@ -580,9 +571,9 @@ internal static class SdkProjectDiscovery
580
571
  .Select(kvp => new Dependency(kvp.Key, kvp.Value, DependencyType.PackageReference, TargetFrameworks: targetFrameworks))
581
572
  .ToImmutableArray();
582
573
 
583
- var tempProjectPath = await MSBuildHelper.CreateTempProjectAsync(tempDirectory, repoRootPath, projectPath, targetFrameworks, topLevelDependencies, experimentsManager, logger);
574
+ var tempProjectPath = await MSBuildHelper.CreateTempProjectAsync(tempDirectory, repoRootPath, projectPath, targetFrameworks, topLevelDependencies, logger);
584
575
  var tempProjectDirectory = Path.GetDirectoryName(tempProjectPath)!;
585
- var rediscoveredDependencies = await DiscoverAsync(tempProjectDirectory, tempProjectDirectory, tempProjectPath, experimentsManager, logger);
576
+ var rediscoveredDependencies = await DiscoverAsync(tempProjectDirectory, tempProjectDirectory, tempProjectPath, logger);
586
577
  var rediscoveredDependenciesForThisProject = rediscoveredDependencies.Single(); // we started with a single temp project, this will be the only result
587
578
 
588
579
  // re-build packagesPerProject
@@ -612,8 +603,7 @@ internal static class SdkProjectDiscovery
612
603
  NamedNode node,
613
604
  Dictionary<string, Dictionary<string, Dictionary<string, string>>> packagesPerProject, // projectPath -> tfm -> (packageName, packageVersion)
614
605
  Dictionary<string, Dictionary<string, HashSet<string>>> topLevelPackagesPerProject, // projectPath -> tfm -> packageName
615
- Dictionary<string, Dictionary<string, Dictionary<string, string>>> packageVersionsPerProject, // projectPath -> tfm -> (packageName, packageVersion)
616
- ExperimentsManager experimentsManager
606
+ Dictionary<string, Dictionary<string, Dictionary<string, string>>> packageVersionsPerProject // projectPath -> tfm -> (packageName, packageVersion)
617
607
  )
618
608
  {
619
609
  var doRemoveOperation = node is RemoveItem;
@@ -7,17 +7,15 @@ namespace NuGetUpdater.Core;
7
7
 
8
8
  public record ExperimentsManager
9
9
  {
10
+ public bool EnableCooldown { get; init; } = false;
10
11
  public bool GenerateSimplePrBody { get; init; } = false;
11
- public bool InstallDotnetSdks { get; init; } = false;
12
- public bool NativeUpdater { get; init; } = false;
13
12
 
14
13
  public Dictionary<string, object> ToDictionary()
15
14
  {
16
15
  return new()
17
16
  {
17
+ ["enable_cooldown_for_nuget"] = EnableCooldown,
18
18
  ["nuget_generate_simple_pr_body"] = GenerateSimplePrBody,
19
- ["nuget_install_dotnet_sdks"] = InstallDotnetSdks,
20
- ["nuget_native_updater"] = NativeUpdater,
21
19
  };
22
20
  }
23
21
 
@@ -25,9 +23,8 @@ public record ExperimentsManager
25
23
  {
26
24
  return new ExperimentsManager()
27
25
  {
26
+ EnableCooldown = IsEnabled(experiments, "enable_cooldown_for_nuget"),
28
27
  GenerateSimplePrBody = IsEnabled(experiments, "nuget_generate_simple_pr_body"),
29
- InstallDotnetSdks = IsEnabled(experiments, "nuget_install_dotnet_sdks"),
30
- NativeUpdater = IsEnabled(experiments, "nuget_native_updater"),
31
28
  };
32
29
  }
33
30
 
@@ -0,0 +1,83 @@
1
+ using System.Collections.Immutable;
2
+ using System.IO.Enumeration;
3
+ using System.Text.Json.Serialization;
4
+
5
+ using NuGet.Versioning;
6
+
7
+ namespace NuGetUpdater.Core.Run.ApiModel;
8
+
9
+ public record Cooldown
10
+ {
11
+ [JsonPropertyName("default-days")]
12
+ public int DefaultDays { get; init; } = 0;
13
+
14
+ [JsonPropertyName("semver-major-days")]
15
+ public int SemVerMajorDays { get; init; } = 0;
16
+
17
+ [JsonPropertyName("semver-minor-days")]
18
+ public int SemVerMinorDays { get; init; } = 0;
19
+
20
+ [JsonPropertyName("semver-patch-days")]
21
+ public int SemVerPatchDays { get; init; } = 0;
22
+
23
+ [JsonPropertyName("include")]
24
+ public ImmutableArray<string>? Include { get; init; } = null;
25
+
26
+ [JsonPropertyName("exclude")]
27
+ public ImmutableArray<string>? Exclude { get; init; } = null;
28
+
29
+ public bool AppliesToPackage(string packageName)
30
+ {
31
+ var isExcluded = Exclude?.Any(exclude => FileSystemName.MatchesSimpleExpression(exclude, packageName)) ?? false;
32
+ if (isExcluded)
33
+ {
34
+ return false;
35
+ }
36
+
37
+ var isIncluded = Include is null ||
38
+ Include.Value.Length == 0 ||
39
+ Include.Value.Any(include => FileSystemName.MatchesSimpleExpression(include, packageName));
40
+ return isIncluded;
41
+ }
42
+
43
+ public int GetCooldownDays(NuGetVersion currentPackageVersion, NuGetVersion candidateUpdateVersion)
44
+ {
45
+ var majorDays = SemVerMajorDays > 0 ? SemVerMajorDays : DefaultDays;
46
+ var minorDays = SemVerMinorDays > 0 ? SemVerMinorDays : DefaultDays;
47
+ var patchDays = SemVerPatchDays > 0 ? SemVerPatchDays : DefaultDays;
48
+
49
+ var isMajorBump = candidateUpdateVersion.Major > currentPackageVersion.Major;
50
+ var isMinorBump = candidateUpdateVersion.Major == currentPackageVersion.Major && candidateUpdateVersion.Minor > currentPackageVersion.Minor;
51
+ var isPatchBump = candidateUpdateVersion.Major == currentPackageVersion.Major && candidateUpdateVersion.Minor == currentPackageVersion.Minor && candidateUpdateVersion.Patch > currentPackageVersion.Patch;
52
+
53
+ if (isMajorBump)
54
+ {
55
+ return majorDays;
56
+ }
57
+ else if (isMinorBump)
58
+ {
59
+ return minorDays;
60
+ }
61
+ else if (isPatchBump)
62
+ {
63
+ return patchDays;
64
+ }
65
+
66
+ // possible if it's a change in pre-release version
67
+ return DefaultDays;
68
+ }
69
+
70
+ public bool IsVersionUpdateAllowed(DateTimeOffset currentTime, DateTimeOffset? packagePublishTime, NuGetVersion currentPackageVersion, NuGetVersion candidateUpdateVersion)
71
+ {
72
+ if (packagePublishTime is null)
73
+ {
74
+ // default to allow it
75
+ return true;
76
+ }
77
+
78
+ var daysSincePublish = (currentTime - packagePublishTime.Value).TotalDays;
79
+ var requiredCooldownDays = GetCooldownDays(currentPackageVersion, candidateUpdateVersion);
80
+ var isUpdateAllowed = daysSincePublish >= requiredCooldownDays;
81
+ return isUpdateAllowed;
82
+ }
83
+ }
@@ -40,6 +40,7 @@ public sealed record Job
40
40
  public CommitOptions? CommitMessageOptions { get; init; } = null;
41
41
  public ImmutableArray<Dictionary<string, object>>? CredentialsMetadata { get; init; } = null;
42
42
  public int MaxUpdaterRunTime { get; init; } = 0;
43
+ public Cooldown? Cooldown { get; init; } = null;
43
44
 
44
45
  public ImmutableArray<string> GetAllDirectories()
45
46
  {
@@ -120,7 +121,7 @@ public sealed record Job
120
121
  }
121
122
 
122
123
  var version = NuGetVersion.Parse(dependency.Version);
123
- var dependencyInfo = RunWorker.GetDependencyInfo(this, dependency);
124
+ var dependencyInfo = RunWorker.GetDependencyInfo(this, dependency, allowCooldown: false);
124
125
  var isVulnerable = dependencyInfo.Vulnerabilities.Any(v => v.IsVulnerable(version));
125
126
 
126
127
  bool IsAllowed(AllowedUpdate allowedUpdate)