dependabot-nuget 0.267.0 → 0.270.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.
- checksums.yaml +4 -4
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Update.cs +1 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/AnalyzeWorker.cs +6 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/CompatabilityChecker.cs +16 -13
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/Requirement.cs +8 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/SdkPackageUpdater.cs +33 -14
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/DependencyConflictResolver.cs +689 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +226 -23
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/AnalyzeWorkerTests.cs +84 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/RequirementTests.cs +14 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/TemporaryEnvironment.cs +23 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackagesConfig.cs +164 -55
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.Sdk.cs +186 -10
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/MSBuildHelperTests.cs +785 -1
- data/lib/dependabot/nuget/file_updater.rb +44 -22
- data/lib/dependabot/nuget/native_helpers.rb +6 -1
- metadata +7 -5
@@ -0,0 +1,689 @@
|
|
1
|
+
using System.Collections.Immutable;
|
2
|
+
using System.Text;
|
3
|
+
using System.Text.RegularExpressions;
|
4
|
+
|
5
|
+
using NuGet.Common;
|
6
|
+
using NuGet.Configuration;
|
7
|
+
using NuGet.Frameworks;
|
8
|
+
using NuGet.Packaging.Core;
|
9
|
+
using NuGet.Protocol;
|
10
|
+
using NuGet.Protocol.Core.Types;
|
11
|
+
using NuGet.Versioning;
|
12
|
+
|
13
|
+
using NuGetUpdater.Core.Analyze;
|
14
|
+
|
15
|
+
using Task = System.Threading.Tasks.Task;
|
16
|
+
|
17
|
+
namespace NuGetUpdater.Core;
|
18
|
+
|
19
|
+
// Data type to store information of a given package
|
20
|
+
public class PackageToUpdate
|
21
|
+
{
|
22
|
+
public string PackageName { get; set; }
|
23
|
+
public string CurrentVersion { get; set; }
|
24
|
+
public string NewVersion { get; set; }
|
25
|
+
|
26
|
+
// Second version in case there's a "bounds" on the package version
|
27
|
+
public string SecondVersion { get; set; }
|
28
|
+
|
29
|
+
// Bool to determine if a package has to be a specific version
|
30
|
+
public bool IsSpecific { get; set; }
|
31
|
+
}
|
32
|
+
|
33
|
+
public class PackageManager
|
34
|
+
{
|
35
|
+
// Dictionaries to store the relationships of a package (dependencies and parents)
|
36
|
+
private readonly Dictionary<PackageToUpdate, HashSet<PackageToUpdate>> packageDependencies = new Dictionary<PackageToUpdate, HashSet<PackageToUpdate>>();
|
37
|
+
private readonly Dictionary<PackageToUpdate, HashSet<PackageToUpdate>> reverseDependencies = new Dictionary<PackageToUpdate, HashSet<PackageToUpdate>>();
|
38
|
+
|
39
|
+
// Path of the repository
|
40
|
+
private readonly string repoRoot;
|
41
|
+
|
42
|
+
// Path to the project within the repository
|
43
|
+
private readonly string projectPath;
|
44
|
+
|
45
|
+
public PackageManager(string repoRoot, string projectPath)
|
46
|
+
{
|
47
|
+
this.repoRoot = repoRoot;
|
48
|
+
this.projectPath = projectPath;
|
49
|
+
}
|
50
|
+
|
51
|
+
// Method alterted from VersionFinder.cs to find the metadata of a given package
|
52
|
+
private async Task<IPackageSearchMetadata?> FindPackageMetadataAsync(PackageIdentity packageIdentity, CancellationToken cancellationToken)
|
53
|
+
{
|
54
|
+
string? currentDirectory = Path.GetDirectoryName(projectPath);
|
55
|
+
string CurrentDirectory = currentDirectory ?? Environment.CurrentDirectory;
|
56
|
+
SourceCacheContext SourceCacheContext = new SourceCacheContext();
|
57
|
+
PackageDownloadContext PackageDownloadContext = new PackageDownloadContext(SourceCacheContext);
|
58
|
+
ILogger Logger = NullLogger.Instance;
|
59
|
+
|
60
|
+
IMachineWideSettings MachineWideSettings = new NuGet.CommandLine.CommandLineMachineWideSettings();
|
61
|
+
ISettings Settings = NuGet.Configuration.Settings.LoadDefaultSettings(
|
62
|
+
CurrentDirectory,
|
63
|
+
configFileName: null,
|
64
|
+
MachineWideSettings);
|
65
|
+
|
66
|
+
var globalPackagesFolder = SettingsUtility.GetGlobalPackagesFolder(Settings);
|
67
|
+
var sourceMapping = PackageSourceMapping.GetPackageSourceMapping(Settings);
|
68
|
+
var packageSources = sourceMapping.GetConfiguredPackageSources(packageIdentity.Id).ToHashSet();
|
69
|
+
var sourceProvider = new PackageSourceProvider(Settings);
|
70
|
+
|
71
|
+
ImmutableArray<PackageSource> PackageSources = sourceProvider.LoadPackageSources()
|
72
|
+
.Where(p => p.IsEnabled)
|
73
|
+
.ToImmutableArray();
|
74
|
+
|
75
|
+
var sources = packageSources.Count == 0
|
76
|
+
? PackageSources
|
77
|
+
: PackageSources
|
78
|
+
.Where(p => packageSources.Contains(p.Name))
|
79
|
+
.ToImmutableArray();
|
80
|
+
|
81
|
+
var message = new StringBuilder();
|
82
|
+
message.AppendLine($"finding info url for {packageIdentity}, using package sources: {string.Join(", ", sources.Select(s => s.Name))}");
|
83
|
+
|
84
|
+
foreach (var source in sources)
|
85
|
+
{
|
86
|
+
message.AppendLine($" checking {source.Name}");
|
87
|
+
var sourceRepository = Repository.Factory.GetCoreV3(source);
|
88
|
+
var feed = await sourceRepository.GetResourceAsync<MetadataResource>(cancellationToken);
|
89
|
+
if (feed is null)
|
90
|
+
{
|
91
|
+
message.AppendLine($" feed for {source.Name} was null");
|
92
|
+
continue;
|
93
|
+
}
|
94
|
+
|
95
|
+
try
|
96
|
+
{
|
97
|
+
var existsInFeed = await feed.Exists(
|
98
|
+
packageIdentity,
|
99
|
+
includeUnlisted: false,
|
100
|
+
SourceCacheContext,
|
101
|
+
NullLogger.Instance,
|
102
|
+
cancellationToken);
|
103
|
+
|
104
|
+
if (!existsInFeed)
|
105
|
+
{
|
106
|
+
message.AppendLine($" package {packageIdentity} does not exist in {source.Name}");
|
107
|
+
continue;
|
108
|
+
}
|
109
|
+
}
|
110
|
+
catch (FatalProtocolException)
|
111
|
+
{
|
112
|
+
// if anything goes wrong here, the package source obviously doesn't contain the requested package
|
113
|
+
continue;
|
114
|
+
}
|
115
|
+
|
116
|
+
var metadataResource = await sourceRepository.GetResourceAsync<PackageMetadataResource>(cancellationToken);
|
117
|
+
var metadata = await metadataResource.GetMetadataAsync(packageIdentity, SourceCacheContext, Logger, cancellationToken);
|
118
|
+
return metadata;
|
119
|
+
}
|
120
|
+
|
121
|
+
return null;
|
122
|
+
}
|
123
|
+
|
124
|
+
// Method to find the best match framework of a given package's target framework availability
|
125
|
+
public static NuGetFramework FindBestMatchFramework(IEnumerable<NuGet.Packaging.PackageDependencyGroup> dependencySet, string targetFrameworkString)
|
126
|
+
{
|
127
|
+
// Parse the given target framework string into a NuGetFramework object
|
128
|
+
var targetFramework = NuGetFramework.ParseFolder(targetFrameworkString);
|
129
|
+
var frameworkReducer = new FrameworkReducer();
|
130
|
+
|
131
|
+
// Collect all target frameworks from the dependency set
|
132
|
+
var availableFrameworks = dependencySet.Select(dg => dg.TargetFramework).ToList();
|
133
|
+
|
134
|
+
// Return bestmatch framework
|
135
|
+
return frameworkReducer.GetNearest(targetFramework, availableFrameworks);
|
136
|
+
}
|
137
|
+
|
138
|
+
// Method to get the dependencies of a package
|
139
|
+
public async Task<List<PackageToUpdate>> GetDependenciesAsync(PackageToUpdate package, string targetFramework, string projectDirectory)
|
140
|
+
{
|
141
|
+
if (!NuGetVersion.TryParse(package.NewVersion, out var otherVersion))
|
142
|
+
{
|
143
|
+
return null;
|
144
|
+
}
|
145
|
+
|
146
|
+
// Create a package identity to use for obtaining the metadata url
|
147
|
+
PackageIdentity packageIdentity = new PackageIdentity(package.PackageName, otherVersion);
|
148
|
+
|
149
|
+
bool specific = false;
|
150
|
+
|
151
|
+
List<PackageToUpdate> dependencyList = new List<PackageToUpdate>();
|
152
|
+
|
153
|
+
try
|
154
|
+
{
|
155
|
+
// Fetch package metadata URL
|
156
|
+
var metadataUrl = await FindPackageMetadataAsync(packageIdentity, CancellationToken.None);
|
157
|
+
IEnumerable<NuGet.Packaging.PackageDependencyGroup> dependencySet = metadataUrl?.DependencySets ?? [];
|
158
|
+
|
159
|
+
// Get the bestMatchFramework based off the dependencies
|
160
|
+
var bestMatchFramework = FindBestMatchFramework(dependencySet, targetFramework);
|
161
|
+
|
162
|
+
if (bestMatchFramework != null)
|
163
|
+
{
|
164
|
+
// Process the best match framework
|
165
|
+
var bestMatchGroup = dependencySet.First(dg => dg.TargetFramework == bestMatchFramework);
|
166
|
+
|
167
|
+
foreach (var packageDependency in bestMatchGroup.Packages)
|
168
|
+
{
|
169
|
+
string version = packageDependency.VersionRange.OriginalString;
|
170
|
+
string firstVersion = null;
|
171
|
+
string SecondVersion = null;
|
172
|
+
|
173
|
+
// Conditions to check if the version has bounds specified
|
174
|
+
if (version.StartsWith("[") && version.EndsWith("]"))
|
175
|
+
{
|
176
|
+
version = version.Trim('[', ']');
|
177
|
+
var versions = version.Split(',');
|
178
|
+
version = versions.FirstOrDefault().Trim();
|
179
|
+
if (versions.Length > 1)
|
180
|
+
{
|
181
|
+
SecondVersion = versions.LastOrDefault()?.Trim();
|
182
|
+
}
|
183
|
+
specific = true;
|
184
|
+
}
|
185
|
+
else if (version.StartsWith("[") && version.EndsWith(")"))
|
186
|
+
{
|
187
|
+
version = version.Trim('[', ')');
|
188
|
+
var versions = version.Split(',');
|
189
|
+
version = versions.FirstOrDefault().Trim();
|
190
|
+
if (versions.Length > 1)
|
191
|
+
{
|
192
|
+
SecondVersion = versions.LastOrDefault()?.Trim();
|
193
|
+
}
|
194
|
+
}
|
195
|
+
else if (version.StartsWith("(") && version.EndsWith("]"))
|
196
|
+
{
|
197
|
+
version = version.Trim('(', ']');
|
198
|
+
var versions = version.Split(',');
|
199
|
+
version = versions.FirstOrDefault().Trim();
|
200
|
+
if (versions.Length > 1)
|
201
|
+
{
|
202
|
+
SecondVersion = versions.LastOrDefault()?.Trim();
|
203
|
+
}
|
204
|
+
}
|
205
|
+
else if (version.StartsWith("(") && version.EndsWith(")"))
|
206
|
+
{
|
207
|
+
version = version.Trim('(', ')');
|
208
|
+
var versions = version.Split(',');
|
209
|
+
version = versions.FirstOrDefault().Trim();
|
210
|
+
if (versions.Length > 1)
|
211
|
+
{
|
212
|
+
SecondVersion = versions.LastOrDefault()?.Trim();
|
213
|
+
}
|
214
|
+
}
|
215
|
+
|
216
|
+
// Store the dependency data to later add to the dependencyList
|
217
|
+
PackageToUpdate dependencyPackage = new PackageToUpdate
|
218
|
+
{
|
219
|
+
PackageName = packageDependency.Id,
|
220
|
+
CurrentVersion = version,
|
221
|
+
};
|
222
|
+
|
223
|
+
if (specific == true)
|
224
|
+
{
|
225
|
+
dependencyPackage.IsSpecific = true;
|
226
|
+
}
|
227
|
+
|
228
|
+
if (SecondVersion != null)
|
229
|
+
{
|
230
|
+
dependencyPackage.SecondVersion = SecondVersion;
|
231
|
+
}
|
232
|
+
|
233
|
+
dependencyList.Add(dependencyPackage);
|
234
|
+
}
|
235
|
+
}
|
236
|
+
else
|
237
|
+
{
|
238
|
+
Console.WriteLine("No compatible framework found.");
|
239
|
+
}
|
240
|
+
}
|
241
|
+
catch (HttpRequestException ex)
|
242
|
+
{
|
243
|
+
Console.WriteLine($"HTTP error occurred: {ex.Message}");
|
244
|
+
}
|
245
|
+
catch (ArgumentNullException ex)
|
246
|
+
{
|
247
|
+
Console.WriteLine($"Argument is null error: {ex.ParamName}, {ex.Message}");
|
248
|
+
}
|
249
|
+
catch (InvalidOperationException ex)
|
250
|
+
{
|
251
|
+
Console.WriteLine($"Invalid operation exception: {ex.Message}");
|
252
|
+
}
|
253
|
+
catch (Exception ex)
|
254
|
+
{
|
255
|
+
Console.WriteLine($"An error occurred: {ex.Message}");
|
256
|
+
}
|
257
|
+
|
258
|
+
return dependencyList;
|
259
|
+
}
|
260
|
+
|
261
|
+
// Method AddDependency to create the relationships between a parent and child
|
262
|
+
private void AddDependency(PackageToUpdate parent, PackageToUpdate child)
|
263
|
+
{
|
264
|
+
if (!packageDependencies.ContainsKey(parent))
|
265
|
+
{
|
266
|
+
packageDependencies[parent] = new HashSet<PackageToUpdate>();
|
267
|
+
}
|
268
|
+
else if (packageDependencies[parent].Contains(child))
|
269
|
+
{
|
270
|
+
// Remove the old child dependency if it exists
|
271
|
+
packageDependencies[parent].Remove(child);
|
272
|
+
}
|
273
|
+
|
274
|
+
packageDependencies[parent].Add(child);
|
275
|
+
|
276
|
+
if (!reverseDependencies.ContainsKey(child))
|
277
|
+
{
|
278
|
+
reverseDependencies[child] = new HashSet<PackageToUpdate>();
|
279
|
+
}
|
280
|
+
else if (reverseDependencies[child].Contains(parent))
|
281
|
+
{
|
282
|
+
// Remove the old parent dependency if it exists
|
283
|
+
reverseDependencies[child].Remove(parent);
|
284
|
+
}
|
285
|
+
|
286
|
+
reverseDependencies[child].Add(parent);
|
287
|
+
}
|
288
|
+
|
289
|
+
// Method to get the dependencies of a package and add them as a dependency
|
290
|
+
public async Task PopulatePackageDependenciesAsync(List<PackageToUpdate> packages, string targetFramework, string projectDirectory)
|
291
|
+
{
|
292
|
+
// Loop through each package and get their dependencies
|
293
|
+
foreach (PackageToUpdate package in packages)
|
294
|
+
{
|
295
|
+
List<PackageToUpdate> dependencies = await GetDependenciesAsync(package, targetFramework, projectDirectory);
|
296
|
+
|
297
|
+
if (dependencies == null)
|
298
|
+
{
|
299
|
+
continue;
|
300
|
+
}
|
301
|
+
|
302
|
+
// Add each dependency based off if it exists or not
|
303
|
+
foreach (PackageToUpdate dependency in dependencies)
|
304
|
+
{
|
305
|
+
PackageToUpdate checkInExisting = packages.FirstOrDefault(p => string.Compare(p.PackageName, dependency.PackageName, StringComparison.OrdinalIgnoreCase) == 0);
|
306
|
+
if (checkInExisting != null)
|
307
|
+
{
|
308
|
+
checkInExisting.IsSpecific = dependency.IsSpecific;
|
309
|
+
AddDependency(package, checkInExisting);
|
310
|
+
}
|
311
|
+
else
|
312
|
+
{
|
313
|
+
AddDependency(package, dependency);
|
314
|
+
}
|
315
|
+
}
|
316
|
+
}
|
317
|
+
}
|
318
|
+
|
319
|
+
// Method to get the parent packages of a given package
|
320
|
+
public HashSet<PackageToUpdate> GetParentPackages(PackageToUpdate package)
|
321
|
+
{
|
322
|
+
if (reverseDependencies.TryGetValue(package, out var parents))
|
323
|
+
{
|
324
|
+
return parents;
|
325
|
+
}
|
326
|
+
|
327
|
+
return new HashSet<PackageToUpdate>();
|
328
|
+
}
|
329
|
+
|
330
|
+
// Method to update the version of a desired package based off framework
|
331
|
+
public async Task<string> UpdateVersion(List<PackageToUpdate> existingPackages, PackageToUpdate package, string targetFramework, string projectDirectory)
|
332
|
+
{
|
333
|
+
// Bool to track if the package was in the original existing list
|
334
|
+
bool inExisting = true;
|
335
|
+
|
336
|
+
// If there is no new version to update or if the current version isn't updated
|
337
|
+
if (package.NewVersion == null)
|
338
|
+
{
|
339
|
+
return "No new version";
|
340
|
+
}
|
341
|
+
|
342
|
+
// If the package is already updated or needs to be updated
|
343
|
+
if (package.CurrentVersion != null)
|
344
|
+
{
|
345
|
+
if (package.CurrentVersion == package.NewVersion)
|
346
|
+
{
|
347
|
+
return "Already updated to new version";
|
348
|
+
}
|
349
|
+
}
|
350
|
+
// Place the current version as the new version for updating purposes
|
351
|
+
else
|
352
|
+
{
|
353
|
+
package.CurrentVersion = package.NewVersion;
|
354
|
+
inExisting = false;
|
355
|
+
}
|
356
|
+
|
357
|
+
try
|
358
|
+
{
|
359
|
+
NuGetVersion CurrentVersion = new NuGetVersion(package.CurrentVersion);
|
360
|
+
NuGetVersion newerVersion = new NuGetVersion(package.NewVersion);
|
361
|
+
|
362
|
+
// If the CurrentVersion is less than or equal to the newerVersion, proceed with the update
|
363
|
+
if (CurrentVersion <= newerVersion)
|
364
|
+
{
|
365
|
+
string currentVersiontemp = package.CurrentVersion;
|
366
|
+
package.CurrentVersion = package.NewVersion;
|
367
|
+
|
368
|
+
// Check if the current package has dependencies
|
369
|
+
List<PackageToUpdate> dependencyList = await GetDependenciesAsync(package, targetFramework, projectDirectory);
|
370
|
+
|
371
|
+
// If there are dependencies
|
372
|
+
if (dependencyList != null)
|
373
|
+
{
|
374
|
+
foreach (PackageToUpdate dependency in dependencyList)
|
375
|
+
{
|
376
|
+
// Check if the dependency is in the existing packages
|
377
|
+
foreach (PackageToUpdate existingPackage in existingPackages)
|
378
|
+
{
|
379
|
+
// If you find the dependency
|
380
|
+
if (string.Equals(dependency.PackageName, existingPackage.PackageName, StringComparison.OrdinalIgnoreCase))
|
381
|
+
{
|
382
|
+
NuGetVersion existingCurrentVersion = new NuGetVersion(existingPackage.CurrentVersion);
|
383
|
+
NuGetVersion dependencyCurrentVersion = new NuGetVersion(dependency.CurrentVersion);
|
384
|
+
|
385
|
+
// Check if the existing version is less than the dependency's existing version
|
386
|
+
if (existingCurrentVersion < dependencyCurrentVersion)
|
387
|
+
{
|
388
|
+
// Create temporary copy of the current version and of the existing package
|
389
|
+
string dependencyOldVersion = existingPackage.CurrentVersion;
|
390
|
+
|
391
|
+
// Susbtitute the current version of the existingPackage with the dependency current version
|
392
|
+
existingPackage.CurrentVersion = dependency.CurrentVersion;
|
393
|
+
|
394
|
+
// If the family is compatible with the dependency's version, update with the dependency version
|
395
|
+
if (await AreAllParentsCompatibleAsync(existingPackages, existingPackage, targetFramework, projectDirectory) == true)
|
396
|
+
{
|
397
|
+
existingPackage.CurrentVersion = dependencyOldVersion;
|
398
|
+
string NewVersion = dependency.CurrentVersion;
|
399
|
+
existingPackage.NewVersion = dependency.CurrentVersion;
|
400
|
+
await UpdateVersion(existingPackages, existingPackage, targetFramework, projectDirectory);
|
401
|
+
}
|
402
|
+
// If not, resort to putting version back to normal and remove new version
|
403
|
+
else
|
404
|
+
{
|
405
|
+
existingPackage.CurrentVersion = dependencyOldVersion;
|
406
|
+
package.CurrentVersion = currentVersiontemp;
|
407
|
+
package.NewVersion = package.CurrentVersion;
|
408
|
+
return "Out of scope";
|
409
|
+
}
|
410
|
+
}
|
411
|
+
}
|
412
|
+
}
|
413
|
+
|
414
|
+
// If the dependency has brackets or parenthesis, it's a specific version
|
415
|
+
if (dependency.CurrentVersion.Contains('[') || dependency.CurrentVersion.Contains(']') || dependency.CurrentVersion.Contains('{') || dependency.CurrentVersion.Contains('}'))
|
416
|
+
{
|
417
|
+
dependency.IsSpecific = true;
|
418
|
+
}
|
419
|
+
|
420
|
+
await UpdateVersion(existingPackages, dependency, targetFramework, projectDirectory);
|
421
|
+
}
|
422
|
+
}
|
423
|
+
|
424
|
+
// Get the parent packages of the package and check the compatibility between its family
|
425
|
+
HashSet<PackageToUpdate> parentPackages = GetParentPackages(package);
|
426
|
+
|
427
|
+
foreach (PackageToUpdate parent in parentPackages)
|
428
|
+
{
|
429
|
+
bool isCompatible = await IsCompatibleAsync(parent, package, targetFramework, projectDirectory);
|
430
|
+
|
431
|
+
// If the parent and package are not compatible
|
432
|
+
if (!isCompatible)
|
433
|
+
{
|
434
|
+
// Attempt to find and update to a compatible version between the two
|
435
|
+
NuGetVersion compatibleVersion = await FindCompatibleVersionAsync(existingPackages, parent, package, targetFramework);
|
436
|
+
if (compatibleVersion == null)
|
437
|
+
{
|
438
|
+
return "Failed to update";
|
439
|
+
}
|
440
|
+
|
441
|
+
// If a version is found, update to that version
|
442
|
+
parent.NewVersion = compatibleVersion.ToString();
|
443
|
+
await UpdateVersion(existingPackages, parent, targetFramework, projectDirectory);
|
444
|
+
}
|
445
|
+
|
446
|
+
// If it's compatible and the package you updated wasn't in the existing package, check if the parent's dependencies version is the same as the current version
|
447
|
+
else if (isCompatible == true && inExisting == false)
|
448
|
+
{
|
449
|
+
List<PackageToUpdate> dependencyListParent = await GetDependenciesAsync(parent, targetFramework, projectDirectory);
|
450
|
+
|
451
|
+
PackageToUpdate parentDependency = dependencyListParent.FirstOrDefault(p => string.Compare(p.PackageName, package.PackageName, StringComparison.OrdinalIgnoreCase) == 0);
|
452
|
+
|
453
|
+
// If the parent's dependency current version is not the same as the current version of the package
|
454
|
+
if (parentDependency.CurrentVersion != package.CurrentVersion)
|
455
|
+
{
|
456
|
+
// Create a NugetContext instance to get the latest versions of the parent
|
457
|
+
NuGetContext nugetContext = new NuGetContext(Path.GetDirectoryName(projectPath));
|
458
|
+
Logger logger = null;
|
459
|
+
|
460
|
+
string currentVersionString = parent.CurrentVersion;
|
461
|
+
NuGetVersion currentVersionParent = NuGetVersion.Parse(currentVersionString);
|
462
|
+
|
463
|
+
var result = await VersionFinder.GetVersionsAsync(parent.PackageName, currentVersionParent, nugetContext, logger, CancellationToken.None);
|
464
|
+
var versions = result.GetVersions();
|
465
|
+
NuGetVersion latestVersion = versions.Where(v => !v.IsPrerelease).Max();
|
466
|
+
|
467
|
+
// Loop from the current version to the latest version, use next patch as a limit (unless there's a limit) so it doesn't look for versions that don't exist
|
468
|
+
for (NuGetVersion version = currentVersionParent; version <= latestVersion; version = NextPatch(version, versions))
|
469
|
+
{
|
470
|
+
NuGetVersion nextPatch = NextPatch(version, versions);
|
471
|
+
|
472
|
+
// If the next patch is the same as the currentVersioon, then the update is a Success
|
473
|
+
if (nextPatch == version)
|
474
|
+
{
|
475
|
+
return "Success";
|
476
|
+
}
|
477
|
+
|
478
|
+
string parentVersion = version.ToString();
|
479
|
+
parent.NewVersion = parentVersion;
|
480
|
+
|
481
|
+
// Check if the parent needs to be updated since the child isn't in the existing package list and the parent can update to a newer version to remove the dependency
|
482
|
+
List<PackageToUpdate> dependencyListParentTemp = await GetDependenciesAsync(parent, targetFramework, projectDirectory);
|
483
|
+
PackageToUpdate parentDependencyTemp = dependencyListParentTemp.FirstOrDefault(p => string.Compare(p.PackageName, package.PackageName, StringComparison.OrdinalIgnoreCase) == 0);
|
484
|
+
|
485
|
+
// If the newer package version of the parent has the same version as the parent's previous dependency, update
|
486
|
+
if (parentDependencyTemp.CurrentVersion == package.CurrentVersion)
|
487
|
+
{
|
488
|
+
parent.NewVersion = parentVersion;
|
489
|
+
parent.CurrentVersion = null;
|
490
|
+
await UpdateVersion(existingPackages, parent, targetFramework, projectDirectory);
|
491
|
+
package.IsSpecific = true;
|
492
|
+
return "Success";
|
493
|
+
}
|
494
|
+
}
|
495
|
+
parent.CurrentVersion = currentVersionString;
|
496
|
+
}
|
497
|
+
}
|
498
|
+
}
|
499
|
+
}
|
500
|
+
|
501
|
+
else
|
502
|
+
{
|
503
|
+
Console.WriteLine("Current version is >= latest version");
|
504
|
+
}
|
505
|
+
}
|
506
|
+
catch
|
507
|
+
{
|
508
|
+
return "Failed to update";
|
509
|
+
}
|
510
|
+
|
511
|
+
return "Success";
|
512
|
+
}
|
513
|
+
|
514
|
+
// Method to determine if a parent and child are compatible with their versions
|
515
|
+
public async Task<bool> IsCompatibleAsync(PackageToUpdate parent, PackageToUpdate child, string targetFramework, string projectDirectory)
|
516
|
+
{
|
517
|
+
// Get the dependencies of the parent
|
518
|
+
List<PackageToUpdate> dependencies = await GetDependenciesAsync(parent, targetFramework, projectDirectory);
|
519
|
+
|
520
|
+
foreach (PackageToUpdate dependency in dependencies)
|
521
|
+
{
|
522
|
+
|
523
|
+
// If the child is present
|
524
|
+
if (string.Equals(dependency.PackageName, child.PackageName, StringComparison.OrdinalIgnoreCase))
|
525
|
+
{
|
526
|
+
NuGetVersion dependencyVersion = new NuGetVersion(dependency.CurrentVersion);
|
527
|
+
NuGetVersion childVersion = new NuGetVersion(child.CurrentVersion);
|
528
|
+
|
529
|
+
// If the dependency version of the parent and the childversion is the same, or if the child version can be >=
|
530
|
+
if (dependencyVersion == childVersion || (childVersion > dependencyVersion && dependency.IsSpecific != true))
|
531
|
+
{
|
532
|
+
return true;
|
533
|
+
}
|
534
|
+
else
|
535
|
+
{
|
536
|
+
return false;
|
537
|
+
}
|
538
|
+
}
|
539
|
+
}
|
540
|
+
|
541
|
+
return false;
|
542
|
+
}
|
543
|
+
|
544
|
+
// Method to update a version to the next available version for a package
|
545
|
+
public NuGetVersion NextPatch(NuGetVersion version, IEnumerable<NuGetVersion> allVersions)
|
546
|
+
{
|
547
|
+
var versions = allVersions.Where(v => v > version);
|
548
|
+
|
549
|
+
if (!versions.Any())
|
550
|
+
{
|
551
|
+
// If there are no greater versions, return current version
|
552
|
+
return version;
|
553
|
+
}
|
554
|
+
|
555
|
+
// Find smallest version in the versions
|
556
|
+
return versions.Min();
|
557
|
+
}
|
558
|
+
|
559
|
+
// Method to find a compatible version with the child for the parent to update to
|
560
|
+
public async Task<NuGetVersion> FindCompatibleVersionAsync(List<PackageToUpdate> existingPackages, PackageToUpdate possibleParent, PackageToUpdate possibleDependency, string targetFramework)
|
561
|
+
{
|
562
|
+
string packageId = possibleParent.PackageName;
|
563
|
+
string currentVersionString = possibleParent.CurrentVersion;
|
564
|
+
NuGetVersion CurrentVersion = NuGetVersion.Parse(currentVersionString);
|
565
|
+
string currentVersionStringDependency = possibleDependency.CurrentVersion;
|
566
|
+
NuGetVersion currentVersionDependency = NuGetVersion.Parse(currentVersionStringDependency);
|
567
|
+
|
568
|
+
// Create a NugetContext instance to get the latest versions of the parent
|
569
|
+
NuGetContext nugetContext = new NuGetContext(Path.GetDirectoryName(projectPath));
|
570
|
+
Logger logger = null;
|
571
|
+
|
572
|
+
var result = await VersionFinder.GetVersionsAsync(possibleParent.PackageName, CurrentVersion, nugetContext, logger, CancellationToken.None);
|
573
|
+
var versions = result.GetVersions();
|
574
|
+
|
575
|
+
// If there are no versions
|
576
|
+
if (versions.Length == 0)
|
577
|
+
{
|
578
|
+
return null;
|
579
|
+
}
|
580
|
+
|
581
|
+
NuGetVersion latestVersion = versions
|
582
|
+
.Where(v => !v.IsPrerelease)
|
583
|
+
.Max();
|
584
|
+
|
585
|
+
// If there's a version bounds that the parent has
|
586
|
+
if (possibleParent.SecondVersion != null)
|
587
|
+
{
|
588
|
+
NuGetVersion SecondVersion = NuGetVersion.Parse(possibleParent.SecondVersion);
|
589
|
+
latestVersion = SecondVersion;
|
590
|
+
}
|
591
|
+
|
592
|
+
// If there is no later version
|
593
|
+
if (CurrentVersion == latestVersion)
|
594
|
+
{
|
595
|
+
return null;
|
596
|
+
}
|
597
|
+
|
598
|
+
// If the current version of the parent is less than the current version of the dependency
|
599
|
+
else if (CurrentVersion < currentVersionDependency)
|
600
|
+
{
|
601
|
+
return currentVersionDependency;
|
602
|
+
}
|
603
|
+
|
604
|
+
// Loop from the current version to the latest version, use next patch as a limit (unless there's a limit) so it doesn't look for versions that don't exist
|
605
|
+
for (NuGetVersion version = CurrentVersion; version <= latestVersion; version = NextPatch(version, versions))
|
606
|
+
{
|
607
|
+
possibleParent.NewVersion = version.ToString();
|
608
|
+
|
609
|
+
NuGetVersion nextPatch = NextPatch(version, versions);
|
610
|
+
|
611
|
+
// If the next patch is the same as the CurrentVersion, then nothing is needed
|
612
|
+
if (nextPatch == version)
|
613
|
+
{
|
614
|
+
return nextPatch;
|
615
|
+
}
|
616
|
+
|
617
|
+
// Check if there's compatibility with parent and dependency
|
618
|
+
if (await IsCompatibleAsync(possibleParent, possibleDependency, targetFramework, nugetContext.CurrentDirectory))
|
619
|
+
{
|
620
|
+
// Check if parents are compatible, recursively
|
621
|
+
if (await AreAllParentsCompatibleAsync(existingPackages, possibleParent, targetFramework, nugetContext.CurrentDirectory))
|
622
|
+
{
|
623
|
+
// If compatible, return the new version
|
624
|
+
if (Regex.IsMatch(possibleParent.NewVersion, @"[a-zA-Z]"))
|
625
|
+
{
|
626
|
+
possibleParent.IsSpecific = true;
|
627
|
+
}
|
628
|
+
return version;
|
629
|
+
}
|
630
|
+
}
|
631
|
+
}
|
632
|
+
|
633
|
+
// If no compatible version is found, return null
|
634
|
+
return null;
|
635
|
+
}
|
636
|
+
|
637
|
+
// Method to determine if all the parents of a given package are compatible with the parent's desired version
|
638
|
+
public async Task<bool> AreAllParentsCompatibleAsync(List<PackageToUpdate> existingPackages, PackageToUpdate possibleParent, string targetFramework, string projectDirectory)
|
639
|
+
{
|
640
|
+
// Get the possibleParent parentPackages
|
641
|
+
HashSet<PackageToUpdate> parentPackages = GetParentPackages(possibleParent);
|
642
|
+
|
643
|
+
foreach (PackageToUpdate parent in parentPackages)
|
644
|
+
{
|
645
|
+
// Check compatibility between the possibleParent and current parent
|
646
|
+
bool isCompatible = await IsCompatibleAsync(parent, possibleParent, targetFramework, projectDirectory);
|
647
|
+
|
648
|
+
// If the possibleParent and parent are not compatible
|
649
|
+
if (!isCompatible)
|
650
|
+
{
|
651
|
+
// Find a compatible version if possible
|
652
|
+
NuGetVersion compatibleVersion = await FindCompatibleVersionAsync(existingPackages, parent, possibleParent, targetFramework);
|
653
|
+
if (compatibleVersion == null)
|
654
|
+
{
|
655
|
+
return false;
|
656
|
+
}
|
657
|
+
|
658
|
+
parent.NewVersion = compatibleVersion.ToString();
|
659
|
+
await UpdateVersion(existingPackages, parent, targetFramework, projectDirectory);
|
660
|
+
}
|
661
|
+
|
662
|
+
// Recursively check if all ancestors are compatible
|
663
|
+
if (!await AreAllParentsCompatibleAsync(existingPackages, parent, targetFramework, projectDirectory))
|
664
|
+
{
|
665
|
+
return false;
|
666
|
+
}
|
667
|
+
}
|
668
|
+
|
669
|
+
return true;
|
670
|
+
}
|
671
|
+
|
672
|
+
// Method to update the existing packages with new version of the desired packages to update
|
673
|
+
public void UpdateExistingPackagesWithNewVersions(List<PackageToUpdate> existingPackages, List<PackageToUpdate> packagesToUpdate)
|
674
|
+
{
|
675
|
+
foreach (PackageToUpdate packageToUpdate in packagesToUpdate)
|
676
|
+
{
|
677
|
+
PackageToUpdate existingPackage = existingPackages.FirstOrDefault(p => string.Compare(p.PackageName, packageToUpdate.PackageName, StringComparison.OrdinalIgnoreCase) == 0);
|
678
|
+
|
679
|
+
if (existingPackage != null)
|
680
|
+
{
|
681
|
+
existingPackage.NewVersion = packageToUpdate.NewVersion;
|
682
|
+
}
|
683
|
+
else
|
684
|
+
{
|
685
|
+
Console.WriteLine($"Package {packageToUpdate.PackageName} not found in existing packages");
|
686
|
+
}
|
687
|
+
}
|
688
|
+
}
|
689
|
+
}
|