dependabot-nuget 0.263.0 → 0.264.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/AnalyzeCommand.cs +37 -0
  3. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/DiscoverCommand.cs +3 -3
  4. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Program.cs +1 -0
  5. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Analyze.cs +169 -0
  6. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Discover.cs +79 -67
  7. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.FrameworkCheck.cs +0 -4
  8. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Update.cs +10 -11
  9. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/AnalysisResult.cs +11 -0
  10. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/AnalyzeWorker.cs +441 -0
  11. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/CompatabilityChecker.cs +177 -0
  12. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/DependencyFinder.cs +47 -0
  13. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/DependencyInfo.cs +12 -0
  14. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/Extensions.cs +36 -0
  15. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/NuGetContext.cs +128 -0
  16. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/Requirement.cs +105 -0
  17. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/RequirementConverter.cs +17 -0
  18. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/SecurityVulnerability.cs +11 -0
  19. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/SecurityVulnerabilityExtensions.cs +36 -0
  20. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/VersionFinder.cs +179 -0
  21. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/VersionResult.cs +54 -0
  22. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Dependency.cs +5 -2
  23. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/DiscoveryWorker.cs +2 -1
  24. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/WorkspaceDiscoveryResult.cs +2 -2
  25. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/FrameworkChecker/CompatabilityChecker.cs +0 -2
  26. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/FrameworkChecker/FrameworkCompatibilityService.cs +0 -3
  27. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/FrameworkChecker/SupportedFrameworks.cs +0 -3
  28. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/BindingRedirectManager.cs +0 -5
  29. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/BindingRedirectResolver.cs +0 -4
  30. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/SdkPackageUpdater.cs +6 -2
  31. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/WebApplicationTargetsConditionPatcher.cs +0 -4
  32. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/XmlFilePreAndPostProcessor.cs +0 -2
  33. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/HashSetExtensions.cs +0 -2
  34. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/JsonHelper.cs +0 -4
  35. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/Logger.cs +0 -3
  36. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +7 -8
  37. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/PathHelper.cs +0 -4
  38. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/ProcessExtensions.cs +0 -3
  39. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/XmlExtensions.cs +0 -4
  40. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/AnalyzeWorkerTestBase.cs +90 -0
  41. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/AnalyzeWorkerTests.cs +304 -0
  42. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/CompatibilityCheckerTests.cs +145 -0
  43. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/ExpectedAnalysisResult.cs +8 -0
  44. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/RequirementTests.cs +69 -0
  45. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/SecurityVulnerabilityExtensionsTests.cs +78 -0
  46. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/VersionFinderTests.cs +193 -0
  47. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTestBase.cs +1 -2
  48. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.DotNetToolsJson.cs +2 -2
  49. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.GlobalJson.cs +2 -2
  50. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.PackagesConfig.cs +1 -1
  51. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.Proj.cs +1 -1
  52. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.Project.cs +102 -9
  53. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.cs +4 -4
  54. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/ExpectedDiscoveryResults.cs +2 -2
  55. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/MockNuGetPackage.cs +8 -2
  56. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTestBase.cs +2 -1
  57. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/MSBuildHelperTests.cs +8 -7
  58. data/lib/dependabot/nuget/analysis/analysis_json_reader.rb +63 -0
  59. data/lib/dependabot/nuget/analysis/dependency_analysis.rb +63 -0
  60. data/lib/dependabot/nuget/file_fetcher.rb +7 -6
  61. data/lib/dependabot/nuget/file_parser.rb +28 -21
  62. data/lib/dependabot/nuget/file_updater.rb +22 -25
  63. data/lib/dependabot/nuget/metadata_finder.rb +2 -160
  64. data/lib/dependabot/nuget/native_discovery/native_dependency_details.rb +102 -0
  65. data/lib/dependabot/nuget/native_discovery/native_dependency_file_discovery.rb +129 -0
  66. data/lib/dependabot/nuget/native_discovery/native_directory_packages_props_discovery.rb +44 -0
  67. data/lib/dependabot/nuget/native_discovery/native_discovery_json_reader.rb +174 -0
  68. data/lib/dependabot/nuget/native_discovery/native_evaluation_details.rb +63 -0
  69. data/lib/dependabot/nuget/native_discovery/native_project_discovery.rb +82 -0
  70. data/lib/dependabot/nuget/native_discovery/native_property_details.rb +43 -0
  71. data/lib/dependabot/nuget/native_discovery/native_workspace_discovery.rb +68 -0
  72. data/lib/dependabot/nuget/native_helpers.rb +59 -0
  73. data/lib/dependabot/nuget/native_update_checker/native_requirements_updater.rb +105 -0
  74. data/lib/dependabot/nuget/native_update_checker/native_update_checker.rb +200 -0
  75. data/lib/dependabot/nuget/nuget_config_credential_helpers.rb +3 -2
  76. data/lib/dependabot/nuget/update_checker.rb +47 -0
  77. metadata +39 -5
@@ -0,0 +1,36 @@
1
+ using System.Collections.Immutable;
2
+
3
+ using NuGet.Frameworks;
4
+ using NuGet.Versioning;
5
+
6
+ using NuGetUpdater.Core;
7
+
8
+ internal static class Extensions
9
+ {
10
+ public static ImmutableArray<Dependency> GetDependencies(this ImmutableDictionary<NuGetFramework, ImmutableArray<Dependency>> dependenciesByTfm)
11
+ {
12
+ Dictionary<string, Dependency> dependencies = [];
13
+ foreach (var (_framework, dependenciesForTfm) in dependenciesByTfm)
14
+ {
15
+ foreach (var dependency in dependenciesForTfm)
16
+ {
17
+ if (dependencies.TryGetValue(dependency.Name, out Dependency? value))
18
+ {
19
+ if (NuGetVersion.Parse(value.Version!) < NuGetVersion.Parse(dependency.Version!))
20
+ {
21
+ dependencies[dependency.Name] = dependency with
22
+ {
23
+ TargetFrameworks = [.. value.TargetFrameworks ?? [], .. dependency.TargetFrameworks ?? []]
24
+ };
25
+ }
26
+ }
27
+ else
28
+ {
29
+ dependencies.Add(dependency.Name, dependency);
30
+ }
31
+ }
32
+ }
33
+
34
+ return [.. dependencies.Values];
35
+ }
36
+ }
@@ -0,0 +1,128 @@
1
+ using System.Collections.Immutable;
2
+ using System.Text;
3
+
4
+ using NuGet.CommandLine;
5
+ using NuGet.Common;
6
+ using NuGet.Configuration;
7
+ using NuGet.Packaging.Core;
8
+ using NuGet.Protocol;
9
+ using NuGet.Protocol.Core.Types;
10
+ using NuGet.Versioning;
11
+
12
+ namespace NuGetUpdater.Core.Analyze;
13
+
14
+ internal record NuGetContext : IDisposable
15
+ {
16
+ public SourceCacheContext SourceCacheContext { get; }
17
+ public PackageDownloadContext PackageDownloadContext { get; }
18
+ public string CurrentDirectory { get; }
19
+ public ISettings Settings { get; }
20
+ public IMachineWideSettings MachineWideSettings { get; }
21
+ public ImmutableArray<PackageSource> PackageSources { get; }
22
+ public ILogger Logger { get; }
23
+ public string TempPackageDirectory { get; }
24
+
25
+ public NuGetContext(string? currentDirectory = null, ILogger? logger = null)
26
+ {
27
+ SourceCacheContext = new SourceCacheContext();
28
+ PackageDownloadContext = new PackageDownloadContext(SourceCacheContext);
29
+ CurrentDirectory = currentDirectory ?? Environment.CurrentDirectory;
30
+ MachineWideSettings = new CommandLineMachineWideSettings();
31
+ Settings = NuGet.Configuration.Settings.LoadDefaultSettings(
32
+ CurrentDirectory,
33
+ configFileName: null,
34
+ MachineWideSettings);
35
+ var sourceProvider = new PackageSourceProvider(Settings);
36
+ PackageSources = sourceProvider.LoadPackageSources()
37
+ .Where(p => p.IsEnabled)
38
+ .ToImmutableArray();
39
+ Logger = logger ?? NullLogger.Instance;
40
+ TempPackageDirectory = Path.Combine(Path.GetTempPath(), ".dependabot", "packages");
41
+ }
42
+
43
+ public void Dispose()
44
+ {
45
+ SourceCacheContext.Dispose();
46
+ }
47
+
48
+ private readonly Dictionary<PackageIdentity, string?> _packageInfoUrlCache = new();
49
+
50
+ public async Task<string?> GetPackageInfoUrlAsync(string packageId, string packageVersion, CancellationToken cancellationToken)
51
+ {
52
+ var packageIdentity = new PackageIdentity(packageId, NuGetVersion.Parse(packageVersion));
53
+ if (_packageInfoUrlCache.TryGetValue(packageIdentity, out var cachedUrl))
54
+ {
55
+ return cachedUrl;
56
+ }
57
+
58
+ var infoUrl = await FindPackageInfoUrlAsync(packageIdentity, cancellationToken);
59
+ _packageInfoUrlCache[packageIdentity] = infoUrl;
60
+
61
+ return infoUrl;
62
+ }
63
+
64
+ private async Task<string?> FindPackageInfoUrlAsync(PackageIdentity packageIdentity, CancellationToken cancellationToken)
65
+ {
66
+ var globalPackagesFolder = SettingsUtility.GetGlobalPackagesFolder(Settings);
67
+ var sourceMapping = PackageSourceMapping.GetPackageSourceMapping(Settings);
68
+ var packageSources = sourceMapping.GetConfiguredPackageSources(packageIdentity.Id).ToHashSet();
69
+ var sources = packageSources.Count == 0
70
+ ? PackageSources
71
+ : PackageSources
72
+ .Where(p => packageSources.Contains(p.Name))
73
+ .ToImmutableArray();
74
+
75
+ var message = new StringBuilder();
76
+ message.AppendLine($"finding info url for {packageIdentity}, using package sources: {string.Join(", ", sources.Select(s => s.Name))}");
77
+
78
+ foreach (var source in sources)
79
+ {
80
+ message.AppendLine($" checking {source.Name}");
81
+ var sourceRepository = Repository.Factory.GetCoreV3(source);
82
+ var feed = await sourceRepository.GetResourceAsync<MetadataResource>(cancellationToken);
83
+ if (feed is null)
84
+ {
85
+ message.AppendLine($" feed for {source.Name} was null");
86
+ continue;
87
+ }
88
+
89
+ var existsInFeed = await feed.Exists(
90
+ packageIdentity,
91
+ includeUnlisted: false,
92
+ SourceCacheContext,
93
+ NullLogger.Instance,
94
+ cancellationToken);
95
+ if (!existsInFeed)
96
+ {
97
+ message.AppendLine($" package {packageIdentity} does not exist in {source.Name}");
98
+ continue;
99
+ }
100
+
101
+ var downloadResource = await sourceRepository.GetResourceAsync<DownloadResource>(cancellationToken);
102
+ using var downloadResult = await downloadResource.GetDownloadResourceResultAsync(packageIdentity, PackageDownloadContext, globalPackagesFolder, Logger, cancellationToken);
103
+ if (downloadResult.Status == DownloadResourceResultStatus.Available)
104
+ {
105
+ var repositoryMetadata = downloadResult.PackageReader.NuspecReader.GetRepositoryMetadata();
106
+ message.AppendLine($" repometadata: type=[{repositoryMetadata.Type}], url=[{repositoryMetadata.Url}], branch=[{repositoryMetadata.Branch}], commit=[{repositoryMetadata.Commit}]");
107
+ if (!string.IsNullOrEmpty(repositoryMetadata.Url))
108
+ {
109
+ return repositoryMetadata.Url;
110
+ }
111
+ }
112
+ else
113
+ {
114
+ message.AppendLine($" download result status: {downloadResult.Status}");
115
+ }
116
+
117
+ var metadataResource = await sourceRepository.GetResourceAsync<PackageMetadataResource>(cancellationToken);
118
+ var metadata = await metadataResource.GetMetadataAsync(packageIdentity, SourceCacheContext, Logger, cancellationToken);
119
+ var url = metadata.ProjectUrl ?? metadata.LicenseUrl;
120
+ if (url is not null)
121
+ {
122
+ return url.ToString();
123
+ }
124
+ }
125
+
126
+ return null;
127
+ }
128
+ }
@@ -0,0 +1,105 @@
1
+ using System.Collections.Immutable;
2
+
3
+ using NuGet.Versioning;
4
+
5
+ namespace NuGetUpdater.Core.Analyze;
6
+
7
+ /// <summary>
8
+ /// A Requirement is a set of one or more version restrictions. It supports a
9
+ /// few (=, !=, >, <, >=, <=, ~>) different restriction operators.
10
+ /// </summary>
11
+ /// <remarks>
12
+ /// See Gem::Version for a description on how versions and requirements work
13
+ /// together in RubyGems.
14
+ /// </remarks>
15
+ public class Requirement
16
+ {
17
+ private static readonly ImmutableDictionary<string, Func<NuGetVersion, NuGetVersion, bool>> Operators = new Dictionary<string, Func<NuGetVersion, NuGetVersion, bool>>()
18
+ {
19
+ ["="] = (v, r) => v == r,
20
+ ["!="] = (v, r) => v != r,
21
+ [">"] = (v, r) => v > r,
22
+ ["<"] = (v, r) => v < r,
23
+ [">="] = (v, r) => v >= r,
24
+ ["<="] = (v, r) => v <= r,
25
+ ["~>"] = (v, r) => v >= r && v.Version < Bump(r),
26
+ }.ToImmutableDictionary();
27
+
28
+ public static Requirement Parse(string requirement)
29
+ {
30
+ var splitIndex = requirement.LastIndexOfAny(['=', '>', '<']);
31
+
32
+ // Throw if the requirement is all operator and no version.
33
+ if (splitIndex == requirement.Length - 1)
34
+ {
35
+ throw new ArgumentException($"`{requirement}` is a invalid requirement string", nameof(requirement));
36
+ }
37
+
38
+ string[] parts = splitIndex == -1
39
+ ? [requirement.Trim()]
40
+ : [requirement[..(splitIndex + 1)].Trim(), requirement[(splitIndex + 1)..].Trim()];
41
+
42
+ var op = parts.Length == 1 ? "=" : parts[0];
43
+ var version = NuGetVersion.Parse(parts[^1]);
44
+
45
+ return new Requirement(op, version);
46
+ }
47
+
48
+ public string Operator { get; }
49
+ public NuGetVersion Version { get; }
50
+
51
+ public Requirement(string op, NuGetVersion version)
52
+ {
53
+ if (!Operators.ContainsKey(op))
54
+ {
55
+ throw new ArgumentException("Invalid operator", nameof(op));
56
+ }
57
+
58
+ Operator = op;
59
+ Version = version;
60
+ }
61
+
62
+ public override string ToString()
63
+ {
64
+ return $"{Operator} {Version}";
65
+ }
66
+
67
+ public bool IsSatisfiedBy(NuGetVersion version)
68
+ {
69
+ return Operators[Operator](version, Version);
70
+ }
71
+
72
+ private static readonly Dictionary<string, Version> BumpMap = [];
73
+ /// <summary>
74
+ /// Return a new version object where the next to the last revision
75
+ /// number is one greater (e.g., 5.3.1 => 5.4).
76
+ /// </summary>
77
+ /// <remarks>
78
+ /// This logic intended to be similar to RubyGems Gem::Version#bump
79
+ /// </remarks>
80
+ public static Version Bump(NuGetVersion version)
81
+ {
82
+ if (BumpMap.TryGetValue(version.OriginalVersion!, out var bumpedVersion))
83
+ {
84
+ return bumpedVersion;
85
+ }
86
+
87
+ var versionParts = version.OriginalVersion! // Get the original string this version was created from
88
+ .Split('-')[0] // Get the version part without pre-release
89
+ .Split('.') // Split into Major.Minor.Patch.Revision if present
90
+ .Select(int.Parse)
91
+ .ToArray();
92
+
93
+ if (versionParts.Length > 1)
94
+ {
95
+ versionParts = versionParts[..^1]; // Remove the last part
96
+ }
97
+
98
+ versionParts[^1]++; // Increment the new last part
99
+
100
+ bumpedVersion = NuGetVersion.Parse(string.Join('.', versionParts)).Version;
101
+ BumpMap[version.OriginalVersion!] = bumpedVersion;
102
+
103
+ return bumpedVersion;
104
+ }
105
+ }
@@ -0,0 +1,17 @@
1
+ using System.Text.Json;
2
+ using System.Text.Json.Serialization;
3
+
4
+ namespace NuGetUpdater.Core.Analyze;
5
+
6
+ public class RequirementConverter : JsonConverter<Requirement>
7
+ {
8
+ public override Requirement? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
9
+ {
10
+ return Requirement.Parse(reader.GetString()!);
11
+ }
12
+
13
+ public override void Write(Utf8JsonWriter writer, Requirement value, JsonSerializerOptions options)
14
+ {
15
+ writer.WriteStringValue(value.ToString());
16
+ }
17
+ }
@@ -0,0 +1,11 @@
1
+ using System.Collections.Immutable;
2
+
3
+ namespace NuGetUpdater.Core.Analyze;
4
+
5
+ public sealed record SecurityVulnerability
6
+ {
7
+ public required string DependencyName { get; init; }
8
+ public required string PackageManager { get; init; }
9
+ public required ImmutableArray<Requirement> VulnerableVersions { get; init; }
10
+ public required ImmutableArray<Requirement> SafeVersions { get; init; }
11
+ }
@@ -0,0 +1,36 @@
1
+ using NuGet.Versioning;
2
+
3
+ namespace NuGetUpdater.Core.Analyze;
4
+
5
+ public static class SecurityVulnerabilityExtensions
6
+ {
7
+ // This logic taken from Dependabot security_advisory.rb
8
+ public static bool IsVulnerable(this SecurityVulnerability vulnerability, NuGetVersion version)
9
+ {
10
+ var inSafeRange = vulnerability.SafeVersions
11
+ .Any(r => r.IsSatisfiedBy(version));
12
+ if (inSafeRange)
13
+ {
14
+ // If version is known safe for this advisory, it's not vulnerable
15
+ return false;
16
+ }
17
+
18
+ var inVulnerableRange = vulnerability.VulnerableVersions
19
+ .Any(r => r.IsSatisfiedBy(version));
20
+ if (inVulnerableRange)
21
+ {
22
+ // If in the vulnerable range and not known safe, it's vulnerable
23
+ return true;
24
+ }
25
+
26
+ if (vulnerability.VulnerableVersions.Length > 0)
27
+ {
28
+ // If a vulnerable range present but not met, it's not vulnerable
29
+ return false;
30
+ }
31
+
32
+ // Finally, if no vulnerable range provided, but a safe range provided,
33
+ // and this versions isn't included (checked earlier), it's vulnerable
34
+ return vulnerability.SafeVersions.Length > 0;
35
+ }
36
+ }
@@ -0,0 +1,179 @@
1
+ using System.Collections.Immutable;
2
+
3
+ using NuGet.Common;
4
+ using NuGet.Configuration;
5
+ using NuGet.Packaging.Core;
6
+ using NuGet.Protocol;
7
+ using NuGet.Protocol.Core.Types;
8
+ using NuGet.Versioning;
9
+
10
+ namespace NuGetUpdater.Core.Analyze;
11
+
12
+ internal static class VersionFinder
13
+ {
14
+ public static Task<VersionResult> GetVersionsAsync(
15
+ string packageId,
16
+ NuGetVersion currentVersion,
17
+ NuGetContext nugetContext,
18
+ Logger logger,
19
+ CancellationToken cancellationToken)
20
+ {
21
+ var versionFilter = CreateVersionFilter(currentVersion);
22
+
23
+ return GetVersionsAsync(packageId, currentVersion, versionFilter, nugetContext, logger, cancellationToken);
24
+ }
25
+
26
+ public static Task<VersionResult> GetVersionsAsync(
27
+ DependencyInfo dependencyInfo,
28
+ NuGetContext nugetContext,
29
+ Logger logger,
30
+ CancellationToken cancellationToken)
31
+ {
32
+ var packageId = dependencyInfo.Name;
33
+ var versionRange = VersionRange.Parse(dependencyInfo.Version);
34
+ var currentVersion = versionRange.MinVersion!;
35
+ var versionFilter = CreateVersionFilter(dependencyInfo, versionRange);
36
+
37
+ return GetVersionsAsync(packageId, currentVersion, versionFilter, nugetContext, logger, cancellationToken);
38
+ }
39
+
40
+ public static async Task<VersionResult> GetVersionsAsync(
41
+ string packageId,
42
+ NuGetVersion currentVersion,
43
+ Func<NuGetVersion, bool> versionFilter,
44
+ NuGetContext nugetContext,
45
+ Logger logger,
46
+ CancellationToken cancellationToken)
47
+ {
48
+ var includePrerelease = currentVersion.IsPrerelease;
49
+ VersionResult result = new(currentVersion);
50
+
51
+ var sourceMapping = PackageSourceMapping.GetPackageSourceMapping(nugetContext.Settings);
52
+ var packageSources = sourceMapping.GetConfiguredPackageSources(packageId).ToHashSet();
53
+ var sources = packageSources.Count == 0
54
+ ? nugetContext.PackageSources
55
+ : nugetContext.PackageSources
56
+ .Where(p => packageSources.Contains(p.Name))
57
+ .ToImmutableArray();
58
+
59
+ foreach (var source in sources)
60
+ {
61
+ var sourceRepository = Repository.Factory.GetCoreV3(source);
62
+ var feed = await sourceRepository.GetResourceAsync<MetadataResource>();
63
+ if (feed is null)
64
+ {
65
+ logger.Log($"Failed to get MetadataResource for [{source.Source}]");
66
+ continue;
67
+ }
68
+
69
+ var existsInFeed = await feed.Exists(
70
+ packageId,
71
+ includePrerelease,
72
+ includeUnlisted: false,
73
+ nugetContext.SourceCacheContext,
74
+ NullLogger.Instance,
75
+ cancellationToken);
76
+ if (!existsInFeed)
77
+ {
78
+ continue;
79
+ }
80
+
81
+ var feedVersions = (await feed.GetVersions(
82
+ packageId,
83
+ includePrerelease,
84
+ includeUnlisted: false,
85
+ nugetContext.SourceCacheContext,
86
+ NullLogger.Instance,
87
+ CancellationToken.None)).ToHashSet();
88
+
89
+ if (feedVersions.Contains(currentVersion))
90
+ {
91
+ result.AddCurrentVersionSource(source);
92
+ }
93
+
94
+ result.AddRange(source, feedVersions.Where(versionFilter));
95
+ }
96
+
97
+ return result;
98
+ }
99
+
100
+ internal static Func<NuGetVersion, bool> CreateVersionFilter(DependencyInfo dependencyInfo, VersionRange versionRange)
101
+ {
102
+ // If we are floating to the absolute latest version, we should not filter pre-release versions at all.
103
+ var currentVersion = versionRange.Float?.FloatBehavior != NuGetVersionFloatBehavior.AbsoluteLatest
104
+ ? versionRange.MinVersion
105
+ : null;
106
+
107
+ return version => (currentVersion is null || version > currentVersion)
108
+ && versionRange.Satisfies(version)
109
+ && (currentVersion is null || !currentVersion.IsPrerelease || !version.IsPrerelease || version.Version == currentVersion.Version)
110
+ && !dependencyInfo.IgnoredVersions.Any(r => r.IsSatisfiedBy(version))
111
+ && !dependencyInfo.Vulnerabilities.Any(v => v.IsVulnerable(version));
112
+ }
113
+
114
+ internal static Func<NuGetVersion, bool> CreateVersionFilter(NuGetVersion currentVersion)
115
+ {
116
+ return version => version > currentVersion
117
+ && (currentVersion is null || !currentVersion.IsPrerelease || !version.IsPrerelease || version.Version == currentVersion.Version);
118
+ }
119
+
120
+ public static async Task<bool> DoVersionsExistAsync(
121
+ IEnumerable<string> packageIds,
122
+ NuGetVersion version,
123
+ NuGetContext nugetContext,
124
+ Logger logger,
125
+ CancellationToken cancellationToken)
126
+ {
127
+ foreach (var packageId in packageIds)
128
+ {
129
+ if (!await DoesVersionExistAsync(packageId, version, nugetContext, logger, cancellationToken))
130
+ {
131
+ return false;
132
+ }
133
+ }
134
+
135
+ return true;
136
+ }
137
+
138
+ public static async Task<bool> DoesVersionExistAsync(
139
+ string packageId,
140
+ NuGetVersion version,
141
+ NuGetContext nugetContext,
142
+ Logger logger,
143
+ CancellationToken cancellationToken)
144
+ {
145
+ var includePrerelease = version.IsPrerelease;
146
+
147
+ var sourceMapping = PackageSourceMapping.GetPackageSourceMapping(nugetContext.Settings);
148
+ var packageSources = sourceMapping.GetConfiguredPackageSources(packageId).ToHashSet();
149
+ var sources = packageSources.Count == 0
150
+ ? nugetContext.PackageSources
151
+ : nugetContext.PackageSources
152
+ .Where(p => packageSources.Contains(p.Name))
153
+ .ToImmutableArray();
154
+
155
+ foreach (var source in sources)
156
+ {
157
+ var sourceRepository = Repository.Factory.GetCoreV3(source);
158
+ var feed = await sourceRepository.GetResourceAsync<MetadataResource>();
159
+ if (feed is null)
160
+ {
161
+ logger.Log($"Failed to get MetadataResource for [{source.Source}]");
162
+ continue;
163
+ }
164
+
165
+ var existsInFeed = await feed.Exists(
166
+ new PackageIdentity(packageId, version),
167
+ includeUnlisted: false,
168
+ nugetContext.SourceCacheContext,
169
+ NullLogger.Instance,
170
+ cancellationToken);
171
+ if (existsInFeed)
172
+ {
173
+ return true;
174
+ }
175
+ }
176
+
177
+ return false;
178
+ }
179
+ }
@@ -0,0 +1,54 @@
1
+ using System.Collections.Immutable;
2
+
3
+ using NuGet.Configuration;
4
+ using NuGet.Versioning;
5
+
6
+ namespace NuGetUpdater.Core.Analyze;
7
+
8
+ internal class VersionResult
9
+ {
10
+ private readonly Dictionary<NuGetVersion, List<PackageSource>> _versions = [];
11
+ private readonly List<PackageSource> _currentVersionSources = [];
12
+
13
+ public NuGetVersion CurrentVersion { get; }
14
+
15
+ public VersionResult(NuGetVersion currentVersion)
16
+ {
17
+ CurrentVersion = currentVersion;
18
+ }
19
+
20
+ public void AddCurrentVersionSource(PackageSource source)
21
+ {
22
+ _currentVersionSources.Add(source);
23
+ }
24
+
25
+ public void AddRange(PackageSource source, IEnumerable<NuGetVersion> versions)
26
+ {
27
+ foreach (var version in versions)
28
+ {
29
+ if (_versions.ContainsKey(version))
30
+ {
31
+ _versions[version].Add(source);
32
+ }
33
+ else
34
+ {
35
+ _versions.Add(version, [source]);
36
+ }
37
+ }
38
+ }
39
+
40
+ public ImmutableArray<PackageSource> GetPackageSources(NuGetVersion version)
41
+ {
42
+ if (version == CurrentVersion)
43
+ {
44
+ return [.. _currentVersionSources];
45
+ }
46
+
47
+ return [.. _versions[version]];
48
+ }
49
+
50
+ public ImmutableArray<NuGetVersion> GetVersions()
51
+ {
52
+ return [.. _versions.Keys];
53
+ }
54
+ }
@@ -14,7 +14,8 @@ public sealed record Dependency(
14
14
  bool IsDirect = false,
15
15
  bool IsTransitive = false,
16
16
  bool IsOverride = false,
17
- bool IsUpdate = false) : IEquatable<Dependency>
17
+ bool IsUpdate = false,
18
+ string? InfoUrl = null) : IEquatable<Dependency>
18
19
  {
19
20
  public bool Equals(Dependency? other)
20
21
  {
@@ -37,7 +38,8 @@ public sealed record Dependency(
37
38
  IsDirect == other.IsDirect &&
38
39
  IsTransitive == other.IsTransitive &&
39
40
  IsOverride == other.IsOverride &&
40
- IsUpdate == other.IsUpdate;
41
+ IsUpdate == other.IsUpdate &&
42
+ InfoUrl == other.InfoUrl;
41
43
  }
42
44
 
43
45
  public override int GetHashCode()
@@ -53,6 +55,7 @@ public sealed record Dependency(
53
55
  hash.Add(IsTransitive);
54
56
  hash.Add(IsOverride);
55
57
  hash.Add(IsUpdate);
58
+ hash.Add(InfoUrl);
56
59
  return hash.ToHashCode();
57
60
  }
58
61
  }
@@ -39,6 +39,7 @@ public partial class DiscoveryWorker
39
39
  workspacePath = workspacePath[1..];
40
40
  }
41
41
 
42
+ string initialWorkspacePath = workspacePath;
42
43
  workspacePath = Path.Combine(repoRootPath, workspacePath);
43
44
 
44
45
  DotNetToolsJsonDiscoveryResult? dotNetToolsJsonDiscovery = null;
@@ -75,7 +76,7 @@ public partial class DiscoveryWorker
75
76
 
76
77
  var result = new WorkspaceDiscoveryResult
77
78
  {
78
- FilePath = repoRootPath != workspacePath ? Path.GetRelativePath(repoRootPath, workspacePath) : string.Empty,
79
+ Path = initialWorkspacePath,
79
80
  DotNetToolsJson = dotNetToolsJsonDiscovery,
80
81
  GlobalJson = globalJsonDiscovery,
81
82
  DirectoryPackagesProps = directoryPackagesPropsDiscovery,
@@ -2,9 +2,9 @@ using System.Collections.Immutable;
2
2
 
3
3
  namespace NuGetUpdater.Core.Discover;
4
4
 
5
- public sealed record WorkspaceDiscoveryResult : IDiscoveryResult
5
+ public sealed record WorkspaceDiscoveryResult
6
6
  {
7
- public required string FilePath { get; init; }
7
+ public required string Path { get; init; }
8
8
  public bool IsSuccess { get; init; } = true;
9
9
  public ImmutableArray<ProjectDiscoveryResult> Projects { get; init; }
10
10
  public DirectoryPackagesPropsDiscoveryResult? DirectoryPackagesProps { get; init; }
@@ -1,5 +1,3 @@
1
- using System.Linq;
2
-
3
1
  using NuGet.Frameworks;
4
2
 
5
3
  namespace NuGetUpdater.Core.FrameworkChecker;
@@ -1,9 +1,6 @@
1
1
  // Copyright (c) .NET Foundation. All rights reserved.
2
2
  // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3
3
 
4
- using System;
5
- using System.Collections.Generic;
6
-
7
4
  using NuGet.Frameworks;
8
5
 
9
6
  using NuGetGallery.Frameworks;
@@ -1,9 +1,6 @@
1
1
  // Copyright (c) .NET Foundation. All rights reserved.
2
2
  // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3
3
 
4
- using System;
5
- using System.Collections.Generic;
6
-
7
4
  using NuGet.Frameworks;
8
5
 
9
6
  using static NuGet.Frameworks.FrameworkConstants;
@@ -1,10 +1,5 @@
1
1
  extern alias CoreV2;
2
2
 
3
- using System;
4
- using System.Collections.Generic;
5
- using System.IO;
6
- using System.Linq;
7
- using System.Threading.Tasks;
8
3
  using System.Xml.Linq;
9
4
 
10
5
  using CoreV2::NuGet.Runtime;
@@ -1,10 +1,6 @@
1
1
  extern alias CoreV2;
2
2
 
3
- using System;
4
- using System.Collections.Generic;
5
3
  using System.Diagnostics.CodeAnalysis;
6
- using System.IO;
7
- using System.Linq;
8
4
  using System.Reflection;
9
5
  using System.Text.RegularExpressions;
10
6