dependabot-nuget 0.247.0 → 0.249.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Update.cs +57 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/SdkPackageUpdater.cs +1 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +25 -5
- data/lib/dependabot/nuget/file_fetcher/import_paths_finder.rb +1 -0
- data/lib/dependabot/nuget/file_fetcher/sln_project_paths_finder.rb +2 -0
- data/lib/dependabot/nuget/file_fetcher.rb +12 -8
- data/lib/dependabot/nuget/file_parser/dotnet_tools_json_parser.rb +1 -0
- data/lib/dependabot/nuget/file_parser/global_json_parser.rb +1 -0
- data/lib/dependabot/nuget/file_parser/packages_config_parser.rb +1 -0
- data/lib/dependabot/nuget/file_parser/project_file_parser.rb +1 -0
- data/lib/dependabot/nuget/file_parser/property_value_finder.rb +2 -0
- data/lib/dependabot/nuget/file_parser.rb +42 -11
- data/lib/dependabot/nuget/file_updater/property_value_updater.rb +1 -0
- data/lib/dependabot/nuget/nuget_config_credential_helpers.rb +10 -1
- data/lib/dependabot/nuget/requirement.rb +17 -8
- data/lib/dependabot/nuget/update_checker/compatibility_checker.rb +28 -7
- data/lib/dependabot/nuget/update_checker/dependency_finder.rb +70 -19
- data/lib/dependabot/nuget/update_checker/nupkg_fetcher.rb +76 -8
- data/lib/dependabot/nuget/update_checker/nuspec_fetcher.rb +25 -3
- data/lib/dependabot/nuget/update_checker/property_updater.rb +108 -44
- data/lib/dependabot/nuget/update_checker/repository_finder.rb +90 -18
- data/lib/dependabot/nuget/update_checker/requirements_updater.rb +32 -9
- data/lib/dependabot/nuget/update_checker/tfm_comparer.rb +8 -3
- data/lib/dependabot/nuget/update_checker/tfm_finder.rb +51 -13
- data/lib/dependabot/nuget/update_checker/version_finder.rb +167 -62
- data/lib/dependabot/nuget/update_checker.rb +73 -29
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c23304dabfd9e4506478efc6188332716cc7ff097f8042b45c67bfda9cf1b6ae
|
4
|
+
data.tar.gz: 74f8d095b039666ca96e098913667e8c40552df5963187cf4ae66a86ac429ae1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 904dae0561b8288c0cc8b319269a6352cdfa1d4d443e01b052e99bf3294277523f1a227944a7787024a216ad1661a21647330e116f6e5a9c66d95cbd630d7f47
|
7
|
+
data.tar.gz: 75cff9a26706bc60d8f5423d8e096fe4b1a1ccf20c3bb3fff5a5c6428ac6cfdd3b58e0a9ce490a9fb6577e852bd59417917bde19ad3697ed8d76aa789bdb4591
|
@@ -3,6 +3,8 @@ using System.IO;
|
|
3
3
|
using System.Text;
|
4
4
|
using System.Threading.Tasks;
|
5
5
|
|
6
|
+
using NuGetUpdater.Core;
|
7
|
+
using NuGetUpdater.Core.Test;
|
6
8
|
using NuGetUpdater.Core.Test.Update;
|
7
9
|
|
8
10
|
using Xunit;
|
@@ -291,6 +293,61 @@ public partial class EntryPointTests
|
|
291
293
|
);
|
292
294
|
}
|
293
295
|
|
296
|
+
[Fact]
|
297
|
+
public async Task UpdaterDoesNotUseRepoGlobalJsonForMSBuildTasks()
|
298
|
+
{
|
299
|
+
// This is a _very_ specific scenario where the `NuGetUpdater.Cli` tool might pick up a `global.json` from
|
300
|
+
// the root of the repo under test and use it's `sdk` property when trying to locate MSBuild. To properly
|
301
|
+
// test this, it must be tested in a new process where MSBuild has not been loaded yet and the runner tool
|
302
|
+
// must be started with its working directory at the test repo's root.
|
303
|
+
using var tempDir = new TemporaryDirectory();
|
304
|
+
await File.WriteAllTextAsync(Path.Join(tempDir.DirectoryPath, "global.json"), """
|
305
|
+
{
|
306
|
+
"sdk": {
|
307
|
+
"version": "99.99.99"
|
308
|
+
}
|
309
|
+
}
|
310
|
+
""");
|
311
|
+
await File.WriteAllTextAsync(Path.Join(tempDir.DirectoryPath, "project.csproj"), """
|
312
|
+
<Project Sdk="Microsoft.NET.Sdk">
|
313
|
+
<PropertyGroup>
|
314
|
+
<TargetFramework>net8.0</TargetFramework>
|
315
|
+
</PropertyGroup>
|
316
|
+
<ItemGroup>
|
317
|
+
<PackageReference Include="Newtonsoft.Json" Version="7.0.1" />
|
318
|
+
</ItemGroup>
|
319
|
+
</Project>
|
320
|
+
""");
|
321
|
+
var executableName = $"NuGetUpdater.Cli{(Environment.OSVersion.Platform == PlatformID.Win32NT ? ".exe" : "")}";
|
322
|
+
var executableArgs = string.Join(" ",
|
323
|
+
[
|
324
|
+
"update",
|
325
|
+
"--repo-root",
|
326
|
+
tempDir.DirectoryPath,
|
327
|
+
"--solution-or-project",
|
328
|
+
Path.Join(tempDir.DirectoryPath, "project.csproj"),
|
329
|
+
"--dependency",
|
330
|
+
"Newtonsoft.Json",
|
331
|
+
"--new-version",
|
332
|
+
"13.0.1",
|
333
|
+
"--previous-version",
|
334
|
+
"7.0.1",
|
335
|
+
"--verbose"
|
336
|
+
]);
|
337
|
+
|
338
|
+
// verify base run
|
339
|
+
var (exitCode, output, error) = await ProcessEx.RunAsync(executableName, executableArgs, workingDirectory: tempDir.DirectoryPath);
|
340
|
+
Assert.True(exitCode == 0, $"Error running update on unsupported SDK.\nSTDOUT:\n{output}\nSTDERR:\n{error}");
|
341
|
+
|
342
|
+
// verify project update
|
343
|
+
var updatedProjectContents = await File.ReadAllTextAsync(Path.Join(tempDir.DirectoryPath, "project.csproj"));
|
344
|
+
Assert.Contains("13.0.1", updatedProjectContents);
|
345
|
+
|
346
|
+
// verify `global.json` untouched
|
347
|
+
var updatedGlobalJsonContents = await File.ReadAllTextAsync(Path.Join(tempDir.DirectoryPath, "global.json"));
|
348
|
+
Assert.Contains("99.99.99", updatedGlobalJsonContents);
|
349
|
+
}
|
350
|
+
|
294
351
|
private static async Task Run(Func<string, string[]> getArgs, (string Path, string Content)[] initialFiles, (string, string)[] expectedFiles)
|
295
352
|
{
|
296
353
|
var actualFiles = await RunUpdate(initialFiles, async path =>
|
@@ -231,7 +231,7 @@ internal static class SdkPackageUpdater
|
|
231
231
|
logger.Log($" Adding [{dependencyName}/{newDependencyVersion}] as a top-level package reference.");
|
232
232
|
|
233
233
|
// see https://learn.microsoft.com/nuget/consume-packages/install-use-packages-dotnet-cli
|
234
|
-
var (exitCode, _, _) = await ProcessEx.RunAsync("dotnet", $"add {projectPath} package {dependencyName} --version {newDependencyVersion}");
|
234
|
+
var (exitCode, _, _) = await ProcessEx.RunAsync("dotnet", $"add {projectPath} package {dependencyName} --version {newDependencyVersion}", workingDirectory: Path.GetDirectoryName(projectPath));
|
235
235
|
if (exitCode != 0)
|
236
236
|
{
|
237
237
|
logger.Log($" Transitive dependency [{dependencyName}/{newDependencyVersion}] was not added.");
|
@@ -41,9 +41,29 @@ internal static partial class MSBuildHelper
|
|
41
41
|
// Ensure MSBuild types are registered before calling a method that loads the types
|
42
42
|
if (!IsMSBuildRegistered)
|
43
43
|
{
|
44
|
-
var
|
45
|
-
|
46
|
-
|
44
|
+
var globalJsonPath = "global.json";
|
45
|
+
var tempGlobalJsonPath = globalJsonPath + Guid.NewGuid().ToString();
|
46
|
+
var globalJsonExists = File.Exists(globalJsonPath);
|
47
|
+
try
|
48
|
+
{
|
49
|
+
if (globalJsonExists)
|
50
|
+
{
|
51
|
+
Console.WriteLine("Temporarily removing `global.json` for MSBuild detection.");
|
52
|
+
File.Move(globalJsonPath, tempGlobalJsonPath);
|
53
|
+
}
|
54
|
+
|
55
|
+
var defaultInstance = MSBuildLocator.QueryVisualStudioInstances().First();
|
56
|
+
MSBuildPath = defaultInstance.MSBuildPath;
|
57
|
+
MSBuildLocator.RegisterInstance(defaultInstance);
|
58
|
+
}
|
59
|
+
finally
|
60
|
+
{
|
61
|
+
if (globalJsonExists)
|
62
|
+
{
|
63
|
+
Console.WriteLine("Restoring `global.json` after MSBuild detection.");
|
64
|
+
File.Move(tempGlobalJsonPath, globalJsonPath);
|
65
|
+
}
|
66
|
+
}
|
47
67
|
}
|
48
68
|
}
|
49
69
|
|
@@ -311,7 +331,7 @@ internal static partial class MSBuildHelper
|
|
311
331
|
try
|
312
332
|
{
|
313
333
|
var tempProjectPath = await CreateTempProjectAsync(tempDirectory, repoRoot, projectPath, targetFramework, packages);
|
314
|
-
var (exitCode, stdOut, stdErr) = await ProcessEx.RunAsync("dotnet", $"restore \"{tempProjectPath}\"");
|
334
|
+
var (exitCode, stdOut, stdErr) = await ProcessEx.RunAsync("dotnet", $"restore \"{tempProjectPath}\"", workingDirectory: tempDirectory.FullName);
|
315
335
|
|
316
336
|
// NU1608: Detected package version outside of dependency constraint
|
317
337
|
|
@@ -451,7 +471,7 @@ internal static partial class MSBuildHelper
|
|
451
471
|
{
|
452
472
|
var tempProjectPath = await CreateTempProjectAsync(tempDirectory, repoRoot, projectPath, targetFramework, packages);
|
453
473
|
|
454
|
-
var (exitCode, stdout, stderr) = await ProcessEx.RunAsync("dotnet", $"build \"{tempProjectPath}\" /t:_ReportDependencies");
|
474
|
+
var (exitCode, stdout, stderr) = await ProcessEx.RunAsync("dotnet", $"build \"{tempProjectPath}\" /t:_ReportDependencies", workingDirectory: tempDirectory.FullName);
|
455
475
|
|
456
476
|
if (exitCode == 0)
|
457
477
|
{
|
@@ -25,7 +25,7 @@ module Dependabot
|
|
25
25
|
return true if filenames.any? { |f| f.match?("^src$") }
|
26
26
|
return true if filenames.any? { |f| f.end_with?(".proj") }
|
27
27
|
|
28
|
-
filenames.any? { |name| name.match?(/\.
|
28
|
+
filenames.any? { |name| name.match?(/\.(cs|vb|fs)proj$/) }
|
29
29
|
end
|
30
30
|
|
31
31
|
sig { override.returns(String) }
|
@@ -53,7 +53,7 @@ module Dependabot
|
|
53
53
|
end
|
54
54
|
|
55
55
|
sig { override.returns(T::Array[DependencyFile]) }
|
56
|
-
def fetch_files
|
56
|
+
def fetch_files
|
57
57
|
fetched_files = []
|
58
58
|
fetched_files += project_files
|
59
59
|
fetched_files += directory_build_files
|
@@ -73,10 +73,7 @@ module Dependabot
|
|
73
73
|
if project_files.none? && packages_config_files.none?
|
74
74
|
raise T.must(@missing_sln_project_file_errors.first) if @missing_sln_project_file_errors&.any?
|
75
75
|
|
76
|
-
|
77
|
-
Dependabot::DependencyFileNotFound,
|
78
|
-
File.join(directory, "<anything>.(cs|vb|fs)proj")
|
79
|
-
)
|
76
|
+
raise_dependency_file_not_found
|
80
77
|
end
|
81
78
|
|
82
79
|
fetched_files
|
@@ -102,9 +99,16 @@ module Dependabot
|
|
102
99
|
project_files
|
103
100
|
end
|
104
101
|
rescue Octokit::NotFound, Gitlab::Error::NotFound
|
102
|
+
raise_dependency_file_not_found
|
103
|
+
end
|
104
|
+
|
105
|
+
sig { returns(T.noreturn) }
|
106
|
+
def raise_dependency_file_not_found
|
105
107
|
raise(
|
106
|
-
Dependabot::DependencyFileNotFound
|
107
|
-
|
108
|
+
Dependabot::DependencyFileNotFound.new(
|
109
|
+
File.join(directory, "*.(sln|csproj|vbproj|fsproj|proj)"),
|
110
|
+
"Unable to find `*.sln`, `*.(cs|vb|fs)proj`, or `*.proj` in directory `#{directory}`"
|
111
|
+
)
|
108
112
|
)
|
109
113
|
end
|
110
114
|
|
@@ -44,6 +44,16 @@ module Dependabot
|
|
44
44
|
Dependabot.logger.warn "Dependency '#{d.name}' excluded due to unparsable version: #{d.version}"
|
45
45
|
end
|
46
46
|
|
47
|
+
dependency_info = dependencies.map do |d|
|
48
|
+
requirements_info = d.requirements.filter_map { |r| " file: #{r[:file]}, metadata: #{r[:metadata]}" }
|
49
|
+
.join("\n")
|
50
|
+
" name: #{d.name}, version: #{d.version}\n#{requirements_info}"
|
51
|
+
end.join("\n")
|
52
|
+
|
53
|
+
if dependencies.length.positive?
|
54
|
+
Dependabot.logger.info "The following dependencies were found:\n#{dependency_info}"
|
55
|
+
end
|
56
|
+
|
47
57
|
dependencies
|
48
58
|
end
|
49
59
|
|
@@ -53,9 +63,18 @@ module Dependabot
|
|
53
63
|
def project_file_dependencies
|
54
64
|
dependency_set = DependencySet.new
|
55
65
|
|
56
|
-
|
57
|
-
|
58
|
-
|
66
|
+
project_files.each do |project_file|
|
67
|
+
tfms = project_file_parser.target_frameworks(project_file: project_file)
|
68
|
+
unless tfms.any?
|
69
|
+
Dependabot.logger.warn "Excluding project file '#{project_file.name}' due to unresolvable target framework"
|
70
|
+
next
|
71
|
+
end
|
72
|
+
|
73
|
+
dependency_set += project_file_parser.dependency_set(project_file: project_file)
|
74
|
+
end
|
75
|
+
|
76
|
+
proj_files.each do |proj_file|
|
77
|
+
dependency_set += project_file_parser.dependency_set(project_file: proj_file)
|
59
78
|
end
|
60
79
|
|
61
80
|
dependency_set
|
@@ -99,14 +118,21 @@ module Dependabot
|
|
99
118
|
)
|
100
119
|
end
|
101
120
|
|
121
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
122
|
+
def proj_files
|
123
|
+
projfile = /\.proj$/
|
124
|
+
|
125
|
+
dependency_files.select do |df|
|
126
|
+
df.name.match?(projfile)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
102
130
|
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
103
131
|
def project_files
|
104
|
-
|
105
|
-
packageprops = /[Dd]irectory.[Pp]ackages.props/
|
132
|
+
projectfile = /\.(cs|vb|fs)proj$/
|
106
133
|
|
107
134
|
dependency_files.select do |df|
|
108
|
-
df.name.match?(
|
109
|
-
df.name.match?(packageprops)
|
135
|
+
df.name.match?(projectfile)
|
110
136
|
end
|
111
137
|
end
|
112
138
|
|
@@ -134,19 +160,24 @@ module Dependabot
|
|
134
160
|
|
135
161
|
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
136
162
|
def global_json
|
137
|
-
dependency_files.find { |f| f.name.casecmp("global.json")
|
163
|
+
dependency_files.find { |f| f.name.casecmp?("global.json") }
|
138
164
|
end
|
139
165
|
|
140
166
|
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
141
167
|
def dotnet_tools_json
|
142
|
-
dependency_files.find { |f| f.name.casecmp(".config/dotnet-tools.json")
|
168
|
+
dependency_files.find { |f| f.name.casecmp?(".config/dotnet-tools.json") }
|
143
169
|
end
|
144
170
|
|
145
171
|
sig { override.void }
|
146
172
|
def check_required_files
|
147
|
-
|
173
|
+
if project_files.any? || proj_files.any? || packages_config_files.any? || global_json || dotnet_tools_json
|
174
|
+
return
|
175
|
+
end
|
148
176
|
|
149
|
-
raise
|
177
|
+
raise Dependabot::DependencyFileNotFound.new(
|
178
|
+
"*.(cs|vb|fs)proj, *.proj, packages.config, global.json, dotnet-tools.json",
|
179
|
+
"No project file, *.proj, packages.config, global.json, or dotnet-tools.json!"
|
180
|
+
)
|
150
181
|
end
|
151
182
|
end
|
152
183
|
end
|
@@ -1,18 +1,25 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strong
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
+
require "sorbet-runtime"
|
5
|
+
|
4
6
|
module Dependabot
|
5
7
|
module Nuget
|
6
8
|
module NuGetConfigCredentialHelpers
|
9
|
+
extend T::Sig
|
10
|
+
|
11
|
+
sig { returns(String) }
|
7
12
|
def self.user_nuget_config_path
|
8
13
|
home_directory = Dir.home
|
9
14
|
File.join(home_directory, ".nuget", "NuGet", "NuGet.Config")
|
10
15
|
end
|
11
16
|
|
17
|
+
sig { returns(String) }
|
12
18
|
def self.temporary_nuget_config_path
|
13
19
|
user_nuget_config_path + "_ORIGINAL"
|
14
20
|
end
|
15
21
|
|
22
|
+
sig { params(credentials: T::Array[Dependabot::Credential]).void }
|
16
23
|
def self.add_credentials_to_nuget_config(credentials)
|
17
24
|
return unless File.exist?(user_nuget_config_path)
|
18
25
|
|
@@ -48,6 +55,7 @@ module Dependabot
|
|
48
55
|
File.write(user_nuget_config_path, nuget_config)
|
49
56
|
end
|
50
57
|
|
58
|
+
sig { void }
|
51
59
|
def self.restore_user_nuget_config
|
52
60
|
return unless File.exist?(temporary_nuget_config_path)
|
53
61
|
|
@@ -55,6 +63,7 @@ module Dependabot
|
|
55
63
|
File.rename(temporary_nuget_config_path, user_nuget_config_path)
|
56
64
|
end
|
57
65
|
|
66
|
+
sig { params(credentials: T::Array[Dependabot::Credential], _block: T.proc.void).void }
|
58
67
|
def self.patch_nuget_config_for_action(credentials, &_block)
|
59
68
|
add_credentials_to_nuget_config(credentials)
|
60
69
|
begin
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "sorbet-runtime"
|
@@ -12,6 +12,9 @@ require "dependabot/nuget/version"
|
|
12
12
|
module Dependabot
|
13
13
|
module Nuget
|
14
14
|
class Requirement < Dependabot::Requirement
|
15
|
+
extend T::Sig
|
16
|
+
|
17
|
+
sig { override.params(obj: T.any(Gem::Version, String)).returns([String, Gem::Version]) }
|
15
18
|
def self.parse(obj)
|
16
19
|
return ["=", Nuget::Version.new(obj.to_s)] if obj.is_a?(Gem::Version)
|
17
20
|
|
@@ -28,11 +31,12 @@ module Dependabot
|
|
28
31
|
# For consistency with other languages, we define a requirements array.
|
29
32
|
# Dotnet doesn't have an `OR` separator for requirements, so it always
|
30
33
|
# contains a single element.
|
31
|
-
sig { override.params(requirement_string: T.nilable(String)).returns(T::Array[Requirement]) }
|
34
|
+
sig { override.params(requirement_string: T.nilable(String)).returns(T::Array[Dependabot::Requirement]) }
|
32
35
|
def self.requirements_array(requirement_string)
|
33
36
|
[new(requirement_string)]
|
34
37
|
end
|
35
38
|
|
39
|
+
sig { params(requirements: T.any(T.nilable(String), T::Array[T.nilable(String)])).void }
|
36
40
|
def initialize(*requirements)
|
37
41
|
requirements = requirements.flatten.flat_map do |req_string|
|
38
42
|
convert_dotnet_constraint_to_ruby_constraint(req_string)
|
@@ -41,6 +45,7 @@ module Dependabot
|
|
41
45
|
super(requirements)
|
42
46
|
end
|
43
47
|
|
48
|
+
sig { override.params(version: Gem::Version).returns(T::Boolean) }
|
44
49
|
def satisfied_by?(version)
|
45
50
|
version = Nuget::Version.new(version.to_s)
|
46
51
|
super
|
@@ -48,10 +53,11 @@ module Dependabot
|
|
48
53
|
|
49
54
|
private
|
50
55
|
|
56
|
+
sig { params(req_string: T.nilable(String)).returns(T.nilable(T.any(String, T::Array[String]))) }
|
51
57
|
def convert_dotnet_constraint_to_ruby_constraint(req_string)
|
52
58
|
return unless req_string
|
53
59
|
|
54
|
-
return convert_dotnet_range_to_ruby_range(req_string) if req_string
|
60
|
+
return convert_dotnet_range_to_ruby_range(req_string) if req_string.start_with?("(", "[")
|
55
61
|
|
56
62
|
return req_string.split(",").map(&:strip) if req_string.include?(",")
|
57
63
|
|
@@ -61,6 +67,7 @@ module Dependabot
|
|
61
67
|
end
|
62
68
|
|
63
69
|
# rubocop:disable Metrics/PerceivedComplexity
|
70
|
+
sig { params(req_string: String).returns(T::Array[String]) }
|
64
71
|
def convert_dotnet_range_to_ruby_range(req_string)
|
65
72
|
lower_b, upper_b = req_string.split(",").map(&:strip).map do |bound|
|
66
73
|
next convert_range_wildcard_req(bound) if bound.include?("*")
|
@@ -70,9 +77,9 @@ module Dependabot
|
|
70
77
|
|
71
78
|
lower_b =
|
72
79
|
if ["(", "["].include?(lower_b) then nil
|
73
|
-
elsif lower_b.start_with?("(") then "> #{lower_b.sub(/\(\s*/, '')}"
|
80
|
+
elsif T.must(lower_b).start_with?("(") then "> #{T.must(lower_b).sub(/\(\s*/, '')}"
|
74
81
|
else
|
75
|
-
">= #{lower_b.sub(/\[\s*/, '').strip}"
|
82
|
+
">= #{T.must(lower_b).sub(/\[\s*/, '').strip}"
|
76
83
|
end
|
77
84
|
|
78
85
|
upper_b =
|
@@ -87,20 +94,22 @@ module Dependabot
|
|
87
94
|
end
|
88
95
|
# rubocop:enable Metrics/PerceivedComplexity
|
89
96
|
|
97
|
+
sig { params(req_string: String).returns(String) }
|
90
98
|
def convert_range_wildcard_req(req_string)
|
91
|
-
range_end = req_string[-1]
|
92
|
-
defined_part = req_string.split("*").first
|
99
|
+
range_end = T.must(req_string[-1])
|
100
|
+
defined_part = T.must(req_string.split("*").first)
|
93
101
|
version = defined_part + "0"
|
94
102
|
version += range_end if [")", "]"].include?(range_end)
|
95
103
|
version
|
96
104
|
end
|
97
105
|
|
106
|
+
sig { params(req_string: String).returns(String) }
|
98
107
|
def convert_wildcard_req(req_string)
|
99
108
|
return ">= 0-a" if req_string == "*-*"
|
100
109
|
|
101
110
|
return ">= 0" if req_string.start_with?("*")
|
102
111
|
|
103
|
-
defined_part = req_string.split("*").first
|
112
|
+
defined_part = T.must(req_string.split("*").first)
|
104
113
|
suffix = defined_part.end_with?(".") ? "0" : "a"
|
105
114
|
version = defined_part + suffix
|
106
115
|
"~> #{version}"
|
@@ -1,22 +1,34 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
+
require "sorbet-runtime"
|
5
|
+
|
4
6
|
require "dependabot/update_checkers/base"
|
5
7
|
|
6
8
|
module Dependabot
|
7
9
|
module Nuget
|
8
10
|
class CompatibilityChecker
|
11
|
+
extend T::Sig
|
12
|
+
|
9
13
|
require_relative "nuspec_fetcher"
|
10
14
|
require_relative "nupkg_fetcher"
|
11
15
|
require_relative "tfm_finder"
|
12
16
|
require_relative "tfm_comparer"
|
13
17
|
|
18
|
+
sig do
|
19
|
+
params(
|
20
|
+
dependency_urls: T::Array[T::Hash[Symbol, String]],
|
21
|
+
dependency: Dependabot::Dependency,
|
22
|
+
tfm_finder: Dependabot::Nuget::TfmFinder
|
23
|
+
).void
|
24
|
+
end
|
14
25
|
def initialize(dependency_urls:, dependency:, tfm_finder:)
|
15
26
|
@dependency_urls = dependency_urls
|
16
27
|
@dependency = dependency
|
17
28
|
@tfm_finder = tfm_finder
|
18
29
|
end
|
19
30
|
|
31
|
+
sig { params(version: String).returns(T::Boolean) }
|
20
32
|
def compatible?(version)
|
21
33
|
nuspec_xml = NuspecFetcher.fetch_nuspec(dependency_urls, dependency.name, version)
|
22
34
|
return false unless nuspec_xml
|
@@ -32,15 +44,23 @@ module Dependabot
|
|
32
44
|
return true if package_tfms.nil?
|
33
45
|
return false if package_tfms.empty?
|
34
46
|
|
35
|
-
return false if project_tfms.nil? || project_tfms
|
47
|
+
return false if project_tfms.nil? || project_tfms&.empty?
|
36
48
|
|
37
|
-
TfmComparer.are_frameworks_compatible?(project_tfms, package_tfms)
|
49
|
+
TfmComparer.are_frameworks_compatible?(T.must(project_tfms), package_tfms)
|
38
50
|
end
|
39
51
|
|
40
52
|
private
|
41
53
|
|
42
|
-
|
54
|
+
sig { returns(T::Array[T::Hash[Symbol, String]]) }
|
55
|
+
attr_reader :dependency_urls
|
43
56
|
|
57
|
+
sig { returns(Dependabot::Dependency) }
|
58
|
+
attr_reader :dependency
|
59
|
+
|
60
|
+
sig { returns(Dependabot::Nuget::TfmFinder) }
|
61
|
+
attr_reader :tfm_finder
|
62
|
+
|
63
|
+
sig { params(nuspec_xml: Nokogiri::XML::Document).returns(T::Boolean) }
|
44
64
|
def pure_development_dependency?(nuspec_xml)
|
45
65
|
contents = nuspec_xml.at_xpath("package/metadata/developmentDependency")&.content&.strip
|
46
66
|
return false unless contents # no `developmentDependency` element
|
@@ -55,16 +75,17 @@ module Dependabot
|
|
55
75
|
dependency_groups_with_target_framework.to_a.empty?
|
56
76
|
end
|
57
77
|
|
78
|
+
sig { params(nuspec_xml: Nokogiri::XML::Document).returns(T::Array[String]) }
|
58
79
|
def parse_package_tfms(nuspec_xml)
|
59
80
|
nuspec_xml.xpath("//dependencies/group").filter_map { |group| group.attribute("targetFramework") }
|
60
81
|
end
|
61
82
|
|
83
|
+
sig { returns(T.nilable(T::Array[String])) }
|
62
84
|
def project_tfms
|
63
|
-
|
64
|
-
|
65
|
-
@project_tfms = tfm_finder.frameworks(dependency)
|
85
|
+
@project_tfms ||= T.let(tfm_finder.frameworks(dependency), T.nilable(T::Array[String]))
|
66
86
|
end
|
67
87
|
|
88
|
+
sig { params(dependency_version: String).returns(T.nilable(T::Array[String])) }
|
68
89
|
def fetch_package_tfms(dependency_version)
|
69
90
|
cache = CacheManager.cache("compatibility_checker_tfms_cache")
|
70
91
|
key = "#{dependency.name}::#{dependency_version}"
|