dependabot-nuget 0.242.1 → 0.243.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/.editorconfig +37 -28
- data/helpers/lib/NuGetUpdater/.gitignore +1 -0
- data/helpers/lib/NuGetUpdater/NuGetProjects/NuGet.CommandLine/AssemblyMetadataExtractor.cs +2 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Program.cs +2 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Update.cs +178 -176
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Files/JsonBuildFile.cs +2 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Files/PackagesConfigBuildFile.cs +1 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Files/ProjectBuildFile.cs +5 -4
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/FrameworkChecker/CompatabilityChecker.cs +1 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/FrameworkChecker/FrameworkCompatibilityService.cs +10 -5
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/FrameworkChecker/SupportedFrameworks.cs +16 -12
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/BindingRedirectManager.cs +18 -17
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/BindingRedirectResolver.cs +7 -7
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/DotNetToolsJsonUpdater.cs +13 -20
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/GlobalJsonUpdater.cs +9 -3
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackagesConfigUpdater.cs +32 -16
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/SdkPackageUpdater.cs +42 -22
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/UpdaterWorker.cs +32 -13
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/WebApplicationTargetsConditionPatcher.cs +47 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/XmlFilePreAndPostProcessor.cs +55 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/JsonHelper.cs +12 -9
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +49 -42
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/PathHelper.cs +16 -3
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/ProcessExtensions.cs +6 -6
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/XmlExtensions.cs +11 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Files/ProjectBuildFileTests.cs +18 -9
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/FrameworkChecker/CompatibilityCheckerFacts.cs +2 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/FrameworkChecker/FrameworkCompatibilityServiceFacts.cs +7 -7
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/FrameworkChecker/SupportedFrameworkFacts.cs +1 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/PackagesConfigUpdaterTests.cs +9 -9
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorker.DirsProj.cs +81 -80
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTestBase.cs +22 -9
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.DotNetTools.cs +140 -104
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.GlobalJson.cs +25 -25
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.Mixed.cs +8 -9
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackagesConfig.cs +198 -22
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.Sdk.cs +401 -399
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/JsonHelperTests.cs +17 -15
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/MSBuildHelperTests.cs +111 -42
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/SdkPackageUpdaterTests.cs +103 -87
- data/lib/dependabot/nuget/file_parser/project_file_parser.rb +45 -19
- data/lib/dependabot/nuget/file_parser.rb +21 -3
- data/lib/dependabot/nuget/file_updater.rb +42 -6
- data/lib/dependabot/nuget/native_helpers.rb +27 -8
- data/lib/dependabot/nuget/nuget_client.rb +66 -23
- data/lib/dependabot/nuget/nuget_config_credential_helpers.rb +7 -3
- data/lib/dependabot/nuget/update_checker/compatibility_checker.rb +63 -59
- data/lib/dependabot/nuget/update_checker/dependency_finder.rb +2 -2
- data/lib/dependabot/nuget/update_checker/nupkg_fetcher.rb +1 -1
- data/lib/dependabot/nuget/update_checker/nuspec_fetcher.rb +22 -17
- data/lib/dependabot/nuget/update_checker/repository_finder.rb +292 -270
- data/lib/dependabot/nuget/update_checker/tfm_comparer.rb +11 -13
- data/lib/dependabot/nuget/update_checker/tfm_finder.rb +80 -82
- data/lib/dependabot/nuget/update_checker/version_finder.rb +3 -2
- data/lib/dependabot/nuget/version.rb +18 -7
- data/lib/dependabot/nuget.rb +0 -2
- metadata +7 -5
@@ -27,6 +27,8 @@ module Dependabot
|
|
27
27
|
|
28
28
|
PROJECT_REFERENCE_SELECTOR = "ItemGroup > ProjectReference"
|
29
29
|
|
30
|
+
PROJECT_FILE_SELECTOR = "ItemGroup > ProjectFile"
|
31
|
+
|
30
32
|
PACKAGE_REFERENCE_SELECTOR = "ItemGroup > PackageReference, " \
|
31
33
|
"ItemGroup > GlobalPackageReference"
|
32
34
|
|
@@ -56,6 +58,24 @@ module Dependabot
|
|
56
58
|
cache[key] ||= parse_dependencies(project_file)
|
57
59
|
end
|
58
60
|
|
61
|
+
def downstream_file_references(project_file:)
|
62
|
+
file_set = Set.new
|
63
|
+
|
64
|
+
doc = Nokogiri::XML(project_file.content)
|
65
|
+
doc.remove_namespaces!
|
66
|
+
proj_refs = doc.css(PROJECT_REFERENCE_SELECTOR)
|
67
|
+
proj_files = doc.css(PROJECT_FILE_SELECTOR)
|
68
|
+
ref_nodes = proj_refs + proj_files
|
69
|
+
ref_nodes.each do |project_reference_node|
|
70
|
+
dep_file = get_attribute_value(project_reference_node, "Include")
|
71
|
+
full_project_path = full_path(project_file, dep_file)
|
72
|
+
full_project_path = full_project_path[1..-1] if full_project_path.start_with?("/")
|
73
|
+
file_set << full_project_path if full_project_path
|
74
|
+
end
|
75
|
+
|
76
|
+
file_set
|
77
|
+
end
|
78
|
+
|
59
79
|
def target_frameworks(project_file:)
|
60
80
|
target_framework = details_for_property("TargetFramework", project_file)
|
61
81
|
return [target_framework&.fetch(:value)] if target_framework
|
@@ -80,6 +100,21 @@ module Dependabot
|
|
80
100
|
|
81
101
|
attr_reader :dependency_files, :credentials
|
82
102
|
|
103
|
+
def full_path(project_file, ref_path)
|
104
|
+
project_file_directory = File.dirname(project_file.name)
|
105
|
+
is_rooted = project_file_directory.start_with?("/")
|
106
|
+
# Root the directory path to avoid expand_path prepending the working directory
|
107
|
+
project_file_directory = "/" + project_file_directory unless is_rooted
|
108
|
+
|
109
|
+
# normalize path separators
|
110
|
+
relative_path = ref_path.tr("\\", "/")
|
111
|
+
# path is relative to the project file directory
|
112
|
+
relative_path = File.join(project_file_directory, relative_path)
|
113
|
+
result = File.expand_path(relative_path)
|
114
|
+
result = result[1..-1] unless is_rooted
|
115
|
+
result
|
116
|
+
end
|
117
|
+
|
83
118
|
def parse_dependencies(project_file)
|
84
119
|
dependency_set = Dependabot::FileParsers::Base::DependencySet.new
|
85
120
|
|
@@ -131,27 +166,20 @@ module Dependabot
|
|
131
166
|
end
|
132
167
|
|
133
168
|
def add_transitive_dependencies_from_project_references(project_file, doc, dependency_set)
|
134
|
-
project_file_directory = File.dirname(project_file.name)
|
135
|
-
is_rooted = project_file_directory.start_with?("/")
|
136
|
-
# Root the directory path to avoid expand_path prepending the working directory
|
137
|
-
project_file_directory = "/" + project_file_directory unless is_rooted
|
138
|
-
|
139
169
|
# Look for regular project references
|
140
|
-
doc.css(PROJECT_REFERENCE_SELECTOR)
|
170
|
+
project_refs = doc.css(PROJECT_REFERENCE_SELECTOR)
|
171
|
+
# Look for ProjectFile references (dirs.proj)
|
172
|
+
project_files = doc.css(PROJECT_FILE_SELECTOR)
|
173
|
+
ref_nodes = project_refs + project_files
|
174
|
+
|
175
|
+
ref_nodes.each do |reference_node|
|
141
176
|
relative_path = dependency_name(reference_node, project_file)
|
142
177
|
# This could result from a <ProjectReference Remove="..." /> item.
|
143
178
|
next unless relative_path
|
144
179
|
|
145
|
-
|
146
|
-
relative_path = relative_path.tr("\\", "/")
|
147
|
-
# path is relative to the project file directory
|
148
|
-
relative_path = File.join(project_file_directory, relative_path)
|
180
|
+
full_project_path = full_path(project_file, relative_path)
|
149
181
|
|
150
|
-
|
151
|
-
full_path = File.expand_path(relative_path)
|
152
|
-
full_path = full_path[1..-1] unless is_rooted
|
153
|
-
|
154
|
-
referenced_file = dependency_files.find { |f| f.name == full_path }
|
182
|
+
referenced_file = dependency_files.find { |f| f.name == full_project_path }
|
155
183
|
next unless referenced_file
|
156
184
|
|
157
185
|
dependency_set(project_file: referenced_file).dependencies.each do |dep|
|
@@ -279,14 +307,12 @@ module Dependabot
|
|
279
307
|
end
|
280
308
|
|
281
309
|
def dependency_has_search_results?(dependency)
|
282
|
-
dependency_urls =
|
310
|
+
dependency_urls = RepositoryFinder.new(
|
283
311
|
dependency: dependency,
|
284
312
|
credentials: credentials,
|
285
313
|
config_files: nuget_configs
|
286
314
|
).dependency_urls
|
287
|
-
if dependency_urls.empty?
|
288
|
-
dependency_urls = [UpdateChecker::RepositoryFinder.get_default_repository_details(dependency.name)]
|
289
|
-
end
|
315
|
+
dependency_urls = [RepositoryFinder.get_default_repository_details(dependency.name)] if dependency_urls.empty?
|
290
316
|
dependency_urls.any? do |dependency_url|
|
291
317
|
dependency_url_has_matching_result?(dependency.name, dependency_url)
|
292
318
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "nokogiri"
|
@@ -6,12 +6,15 @@ require "nokogiri"
|
|
6
6
|
require "dependabot/dependency"
|
7
7
|
require "dependabot/file_parsers"
|
8
8
|
require "dependabot/file_parsers/base"
|
9
|
+
require "sorbet-runtime"
|
9
10
|
|
10
11
|
# For details on how dotnet handles version constraints, see:
|
11
12
|
# https://docs.microsoft.com/en-us/nuget/reference/package-versioning
|
12
13
|
module Dependabot
|
13
14
|
module Nuget
|
14
15
|
class FileParser < Dependabot::FileParsers::Base
|
16
|
+
extend T::Sig
|
17
|
+
|
15
18
|
require "dependabot/file_parsers/base/dependency_set"
|
16
19
|
require_relative "file_parser/project_file_parser"
|
17
20
|
require_relative "file_parser/packages_config_parser"
|
@@ -20,6 +23,7 @@ module Dependabot
|
|
20
23
|
|
21
24
|
PACKAGE_CONF_DEPENDENCY_SELECTOR = "packages > packages"
|
22
25
|
|
26
|
+
sig { override.returns(T::Array[Dependabot::Dependency]) }
|
23
27
|
def parse
|
24
28
|
dependency_set = DependencySet.new
|
25
29
|
dependency_set += project_file_dependencies
|
@@ -31,6 +35,7 @@ module Dependabot
|
|
31
35
|
|
32
36
|
private
|
33
37
|
|
38
|
+
sig { returns(Dependabot::FileParsers::Base::DependencySet) }
|
34
39
|
def project_file_dependencies
|
35
40
|
dependency_set = DependencySet.new
|
36
41
|
|
@@ -42,6 +47,7 @@ module Dependabot
|
|
42
47
|
dependency_set
|
43
48
|
end
|
44
49
|
|
50
|
+
sig { returns(Dependabot::FileParsers::Base::DependencySet) }
|
45
51
|
def packages_config_dependencies
|
46
52
|
dependency_set = DependencySet.new
|
47
53
|
|
@@ -53,26 +59,32 @@ module Dependabot
|
|
53
59
|
dependency_set
|
54
60
|
end
|
55
61
|
|
62
|
+
sig { returns(Dependabot::FileParsers::Base::DependencySet) }
|
56
63
|
def global_json_dependencies
|
57
64
|
return DependencySet.new unless global_json
|
58
65
|
|
59
66
|
GlobalJsonParser.new(global_json: global_json).dependency_set
|
60
67
|
end
|
61
68
|
|
69
|
+
sig { returns(Dependabot::FileParsers::Base::DependencySet) }
|
62
70
|
def dotnet_tools_json_dependencies
|
63
71
|
return DependencySet.new unless dotnet_tools_json
|
64
72
|
|
65
73
|
DotNetToolsJsonParser.new(dotnet_tools_json: dotnet_tools_json).dependency_set
|
66
74
|
end
|
67
75
|
|
76
|
+
sig { returns(Dependabot::Nuget::FileParser::ProjectFileParser) }
|
68
77
|
def project_file_parser
|
69
|
-
@project_file_parser ||=
|
78
|
+
@project_file_parser ||= T.let(
|
70
79
|
ProjectFileParser.new(
|
71
80
|
dependency_files: dependency_files,
|
72
81
|
credentials: credentials
|
73
|
-
)
|
82
|
+
),
|
83
|
+
T.nilable(Dependabot::Nuget::FileParser::ProjectFileParser)
|
84
|
+
)
|
74
85
|
end
|
75
86
|
|
87
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
76
88
|
def project_files
|
77
89
|
projfile = /\.([a-z]{2})?proj$/
|
78
90
|
packageprops = /[Dd]irectory.[Pp]ackages.props/
|
@@ -83,12 +95,14 @@ module Dependabot
|
|
83
95
|
end
|
84
96
|
end
|
85
97
|
|
98
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
86
99
|
def packages_config_files
|
87
100
|
dependency_files.select do |f|
|
88
101
|
f.name.split("/").last&.casecmp("packages.config")&.zero?
|
89
102
|
end
|
90
103
|
end
|
91
104
|
|
105
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
92
106
|
def project_import_files
|
93
107
|
dependency_files -
|
94
108
|
project_files -
|
@@ -98,18 +112,22 @@ module Dependabot
|
|
98
112
|
[dotnet_tools_json]
|
99
113
|
end
|
100
114
|
|
115
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
101
116
|
def nuget_configs
|
102
117
|
dependency_files.select { |f| f.name.match?(/nuget\.config$/i) }
|
103
118
|
end
|
104
119
|
|
120
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
105
121
|
def global_json
|
106
122
|
dependency_files.find { |f| f.name.casecmp("global.json")&.zero? }
|
107
123
|
end
|
108
124
|
|
125
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
109
126
|
def dotnet_tools_json
|
110
127
|
dependency_files.find { |f| f.name.casecmp(".config/dotnet-tools.json")&.zero? }
|
111
128
|
end
|
112
129
|
|
130
|
+
sig { override.void }
|
113
131
|
def check_required_files
|
114
132
|
return if project_files.any? || packages_config_files.any?
|
115
133
|
|
@@ -5,10 +5,13 @@ require "dependabot/dependency_file"
|
|
5
5
|
require "dependabot/file_updaters"
|
6
6
|
require "dependabot/file_updaters/base"
|
7
7
|
require "dependabot/nuget/native_helpers"
|
8
|
+
require "sorbet-runtime"
|
8
9
|
|
9
10
|
module Dependabot
|
10
11
|
module Nuget
|
11
12
|
class FileUpdater < Dependabot::FileUpdaters::Base
|
13
|
+
extend T::Sig
|
14
|
+
|
12
15
|
require_relative "file_updater/property_value_updater"
|
13
16
|
require_relative "file_parser/project_file_parser"
|
14
17
|
require_relative "file_parser/dotnet_tools_json_parser"
|
@@ -54,6 +57,7 @@ module Dependabot
|
|
54
57
|
|
55
58
|
def try_update_projects(dependency)
|
56
59
|
update_ran = T.let(false, T::Boolean)
|
60
|
+
checked_files = Set.new
|
57
61
|
|
58
62
|
# run update for each project file
|
59
63
|
project_files.each do |project_file|
|
@@ -62,9 +66,17 @@ module Dependabot
|
|
62
66
|
|
63
67
|
next unless project_dependencies.any? { |dep| dep.name.casecmp(dependency.name).zero? }
|
64
68
|
|
65
|
-
|
66
|
-
|
67
|
-
|
69
|
+
next unless repo_contents_path
|
70
|
+
|
71
|
+
checked_key = "#{project_file.name}-#{dependency.name}#{dependency.version}"
|
72
|
+
call_nuget_updater_tool(dependency, proj_path) unless checked_files.include?(checked_key)
|
73
|
+
|
74
|
+
checked_files.add(checked_key)
|
75
|
+
# We need to check the downstream references even though we're already evaluated the file
|
76
|
+
downstream_files = project_file_parser.downstream_file_references(project_file: project_file)
|
77
|
+
downstream_files.each do |downstream_file|
|
78
|
+
checked_files.add("#{downstream_file}-#{dependency.name}#{dependency.version}")
|
79
|
+
end
|
68
80
|
update_ran = true
|
69
81
|
end
|
70
82
|
|
@@ -79,15 +91,39 @@ module Dependabot
|
|
79
91
|
project_file = project_files.first
|
80
92
|
proj_path = dependency_file_path(project_file)
|
81
93
|
|
82
|
-
|
83
|
-
|
84
|
-
|
94
|
+
return false unless repo_contents_path
|
95
|
+
|
96
|
+
call_nuget_updater_tool(dependency, proj_path)
|
85
97
|
return true
|
86
98
|
end
|
87
99
|
|
88
100
|
false
|
89
101
|
end
|
90
102
|
|
103
|
+
sig { params(dependency: Dependency, proj_path: String).void }
|
104
|
+
def call_nuget_updater_tool(dependency, proj_path)
|
105
|
+
NativeHelpers.run_nuget_updater_tool(repo_root: T.must(repo_contents_path), proj_path: proj_path,
|
106
|
+
dependency: dependency, is_transitive: !dependency.top_level?,
|
107
|
+
credentials: credentials)
|
108
|
+
|
109
|
+
# Tests need to track how many times we call the tooling updater to ensure we don't recurse needlessly
|
110
|
+
# Ideally we should find a way to not run this code in prod
|
111
|
+
# (or a better way to track calls made to NativeHelpers)
|
112
|
+
@update_tooling_calls ||= {}
|
113
|
+
key = proj_path + dependency.name
|
114
|
+
if @update_tooling_calls[key]
|
115
|
+
@update_tooling_calls[key] += 1
|
116
|
+
else
|
117
|
+
@update_tooling_calls[key] = 1
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# Don't call this from outside tests, we're only checking that we aren't recursing needlessly
|
122
|
+
sig { returns(T.nilable(T::Hash[String, Integer])) }
|
123
|
+
def testonly_update_tooling_calls
|
124
|
+
@update_tooling_calls
|
125
|
+
end
|
126
|
+
|
91
127
|
def project_dependencies(project_file)
|
92
128
|
# Collect all dependencies from the project file and associated packages.config
|
93
129
|
dependencies = project_file_parser.dependency_set(project_file: project_file).dependencies
|
@@ -1,11 +1,17 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strong
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
+
require "shellwords"
|
5
|
+
require "sorbet-runtime"
|
6
|
+
|
4
7
|
require_relative "nuget_config_credential_helpers"
|
5
8
|
|
6
9
|
module Dependabot
|
7
10
|
module Nuget
|
8
11
|
module NativeHelpers
|
12
|
+
extend T::Sig
|
13
|
+
|
14
|
+
sig { returns(String) }
|
9
15
|
def self.native_helpers_root
|
10
16
|
helpers_root = ENV.fetch("DEPENDABOT_NATIVE_HELPERS_PATH", nil)
|
11
17
|
return File.join(helpers_root, "nuget") unless helpers_root.nil?
|
@@ -13,9 +19,10 @@ module Dependabot
|
|
13
19
|
File.expand_path("../../../helpers", __dir__)
|
14
20
|
end
|
15
21
|
|
22
|
+
sig { params(project_tfms: T::Array[String], package_tfms: T::Array[String]).returns(T::Boolean) }
|
16
23
|
def self.run_nuget_framework_check(project_tfms, package_tfms)
|
17
24
|
exe_path = File.join(native_helpers_root, "NuGetUpdater", "NuGetUpdater.Cli")
|
18
|
-
|
25
|
+
command_parts = [
|
19
26
|
exe_path,
|
20
27
|
"framework-check",
|
21
28
|
"--project-tfms",
|
@@ -23,7 +30,8 @@ module Dependabot
|
|
23
30
|
"--package-tfms",
|
24
31
|
*package_tfms,
|
25
32
|
"--verbose"
|
26
|
-
]
|
33
|
+
]
|
34
|
+
command = Shellwords.join(command_parts)
|
27
35
|
|
28
36
|
fingerprint = [
|
29
37
|
exe_path,
|
@@ -48,9 +56,18 @@ module Dependabot
|
|
48
56
|
end
|
49
57
|
|
50
58
|
# rubocop:disable Metrics/MethodLength
|
59
|
+
sig do
|
60
|
+
params(
|
61
|
+
repo_root: String,
|
62
|
+
proj_path: String,
|
63
|
+
dependency: Dependency,
|
64
|
+
is_transitive: T::Boolean,
|
65
|
+
credentials: T::Array[T.untyped]
|
66
|
+
).void
|
67
|
+
end
|
51
68
|
def self.run_nuget_updater_tool(repo_root:, proj_path:, dependency:, is_transitive:, credentials:)
|
52
69
|
exe_path = File.join(native_helpers_root, "NuGetUpdater", "NuGetUpdater.Cli")
|
53
|
-
|
70
|
+
command_parts = [
|
54
71
|
exe_path,
|
55
72
|
"update",
|
56
73
|
"--repo-root",
|
@@ -63,9 +80,11 @@ module Dependabot
|
|
63
80
|
dependency.version,
|
64
81
|
"--previous-version",
|
65
82
|
dependency.previous_version,
|
66
|
-
is_transitive ? "--transitive" :
|
83
|
+
is_transitive ? "--transitive" : nil,
|
67
84
|
"--verbose"
|
68
|
-
].
|
85
|
+
].compact
|
86
|
+
|
87
|
+
command = Shellwords.join(command_parts)
|
69
88
|
|
70
89
|
fingerprint = [
|
71
90
|
exe_path,
|
@@ -80,9 +99,9 @@ module Dependabot
|
|
80
99
|
"<new-version>",
|
81
100
|
"--previous-version",
|
82
101
|
"<previous-version>",
|
83
|
-
is_transitive ? "--transitive" :
|
102
|
+
is_transitive ? "--transitive" : nil,
|
84
103
|
"--verbose"
|
85
|
-
].join(" ")
|
104
|
+
].compact.join(" ")
|
86
105
|
|
87
106
|
puts "running NuGet updater:\n" + command
|
88
107
|
|
@@ -1,12 +1,19 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "dependabot/nuget/cache_manager"
|
5
5
|
require "dependabot/nuget/update_checker/repository_finder"
|
6
|
+
require "sorbet-runtime"
|
6
7
|
|
7
8
|
module Dependabot
|
8
9
|
module Nuget
|
9
10
|
class NugetClient
|
11
|
+
extend T::Sig
|
12
|
+
|
13
|
+
sig do
|
14
|
+
params(dependency_name: String, repository_details: T::Hash[Symbol, String])
|
15
|
+
.returns(T.nilable(T::Set[String]))
|
16
|
+
end
|
10
17
|
def self.get_package_versions(dependency_name, repository_details)
|
11
18
|
repository_type = repository_details.fetch(:repository_type)
|
12
19
|
if repository_type == "v3"
|
@@ -18,6 +25,10 @@ module Dependabot
|
|
18
25
|
end
|
19
26
|
end
|
20
27
|
|
28
|
+
sig do
|
29
|
+
params(dependency_name: String, repository_details: T::Hash[Symbol, String])
|
30
|
+
.returns(T.nilable(T::Set[String]))
|
31
|
+
end
|
21
32
|
private_class_method def self.get_package_versions_v3(dependency_name, repository_details)
|
22
33
|
# Use the registration URL if possible because it is fast and correct
|
23
34
|
if repository_details[:registration_url]
|
@@ -28,9 +39,15 @@ module Dependabot
|
|
28
39
|
# Otherwise, use the versions URL (fast but wrong because it includes unlisted versions)
|
29
40
|
elsif repository_details[:versions_url]
|
30
41
|
get_versions_from_versions_url_v3(repository_details)
|
42
|
+
else
|
43
|
+
raise "No version sources were available for #{dependency_name} in #{repository_details}"
|
31
44
|
end
|
32
45
|
end
|
33
46
|
|
47
|
+
sig do
|
48
|
+
params(dependency_name: String, repository_details: T::Hash[Symbol, String])
|
49
|
+
.returns(T.nilable(T::Set[String]))
|
50
|
+
end
|
34
51
|
private_class_method def self.get_package_versions_v2(dependency_name, repository_details)
|
35
52
|
doc = execute_xml_nuget_request(repository_details.fetch(:versions_url), repository_details)
|
36
53
|
return unless doc
|
@@ -49,34 +66,33 @@ module Dependabot
|
|
49
66
|
matching_versions
|
50
67
|
end
|
51
68
|
|
69
|
+
sig { params(repository_details: T::Hash[Symbol, String]).returns(T.nilable(T::Set[String])) }
|
52
70
|
private_class_method def self.get_versions_from_versions_url_v3(repository_details)
|
53
|
-
body = execute_json_nuget_request(repository_details
|
54
|
-
body&.fetch("versions")
|
71
|
+
body = execute_json_nuget_request(repository_details.fetch(:versions_url), repository_details)
|
72
|
+
ver_array = T.let(body&.fetch("versions"), T.nilable(T::Array[String]))
|
73
|
+
ver_array&.to_set
|
55
74
|
end
|
56
75
|
|
76
|
+
sig { params(repository_details: T::Hash[Symbol, String]).returns(T.nilable(T::Set[String])) }
|
57
77
|
private_class_method def self.get_versions_from_registration_v3(repository_details)
|
58
|
-
url = repository_details
|
78
|
+
url = repository_details.fetch(:registration_url)
|
59
79
|
body = execute_json_nuget_request(url, repository_details)
|
60
80
|
|
61
81
|
return unless body
|
62
82
|
|
63
83
|
pages = body.fetch("items")
|
64
|
-
versions = Set.new
|
84
|
+
versions = T.let(Set.new, T::Set[String])
|
65
85
|
pages.each do |page|
|
66
86
|
items = page["items"]
|
67
87
|
if items
|
68
88
|
# inlined entries
|
69
|
-
items
|
70
|
-
catalog_entry = item["catalogEntry"]
|
71
|
-
if catalog_entry["listed"] == true
|
72
|
-
vers = catalog_entry["version"]
|
73
|
-
versions << vers
|
74
|
-
end
|
75
|
-
end
|
89
|
+
get_versions_from_inline_page(items, versions)
|
76
90
|
else
|
77
91
|
# paged entries
|
78
92
|
page_url = page["@id"]
|
79
93
|
page_body = execute_json_nuget_request(page_url, repository_details)
|
94
|
+
next unless page_body
|
95
|
+
|
80
96
|
items = page_body.fetch("items")
|
81
97
|
items.each do |item|
|
82
98
|
catalog_entry = item.fetch("catalogEntry")
|
@@ -88,8 +104,27 @@ module Dependabot
|
|
88
104
|
versions
|
89
105
|
end
|
90
106
|
|
107
|
+
sig { params(items: T::Array[T::Hash[String, T.untyped]], versions: T::Set[String]).void }
|
108
|
+
private_class_method def self.get_versions_from_inline_page(items, versions)
|
109
|
+
items.each do |item|
|
110
|
+
catalog_entry = item["catalogEntry"]
|
111
|
+
|
112
|
+
# a package is considered listed if the `listed` property is either `true` or missing
|
113
|
+
listed_property = catalog_entry["listed"]
|
114
|
+
is_listed = listed_property.nil? || listed_property == true
|
115
|
+
if is_listed
|
116
|
+
vers = catalog_entry["version"]
|
117
|
+
versions << vers
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
sig do
|
123
|
+
params(repository_details: T::Hash[Symbol, String], dependency_name: String)
|
124
|
+
.returns(T.nilable(T::Set[String]))
|
125
|
+
end
|
91
126
|
private_class_method def self.get_versions_from_search_url_v3(repository_details, dependency_name)
|
92
|
-
search_url = repository_details
|
127
|
+
search_url = repository_details.fetch(:search_url)
|
93
128
|
body = execute_json_nuget_request(search_url, repository_details)
|
94
129
|
|
95
130
|
body&.fetch("data")
|
@@ -98,11 +133,14 @@ module Dependabot
|
|
98
133
|
&.map { |d| d.fetch("version") }
|
99
134
|
end
|
100
135
|
|
136
|
+
sig do
|
137
|
+
params(url: String, repository_details: T::Hash[Symbol, T.untyped]).returns(T.nilable(Nokogiri::XML::Document))
|
138
|
+
end
|
101
139
|
private_class_method def self.execute_xml_nuget_request(url, repository_details)
|
102
140
|
response = execute_nuget_request_internal(
|
103
141
|
url: url,
|
104
|
-
auth_header: repository_details
|
105
|
-
repository_url: repository_details
|
142
|
+
auth_header: repository_details.fetch(:auth_header),
|
143
|
+
repository_url: repository_details.fetch(:repository_url)
|
106
144
|
)
|
107
145
|
return unless response.status == 200
|
108
146
|
|
@@ -111,11 +149,16 @@ module Dependabot
|
|
111
149
|
doc
|
112
150
|
end
|
113
151
|
|
152
|
+
sig do
|
153
|
+
params(url: String,
|
154
|
+
repository_details: T::Hash[Symbol, T.untyped])
|
155
|
+
.returns(T.nilable(T::Hash[T.untyped, T.untyped]))
|
156
|
+
end
|
114
157
|
private_class_method def self.execute_json_nuget_request(url, repository_details)
|
115
158
|
response = execute_nuget_request_internal(
|
116
159
|
url: url,
|
117
|
-
auth_header: repository_details
|
118
|
-
repository_url: repository_details
|
160
|
+
auth_header: repository_details.fetch(:auth_header),
|
161
|
+
repository_url: repository_details.fetch(:repository_url)
|
119
162
|
)
|
120
163
|
return unless response.status == 200
|
121
164
|
|
@@ -123,11 +166,10 @@ module Dependabot
|
|
123
166
|
JSON.parse(body)
|
124
167
|
end
|
125
168
|
|
126
|
-
|
127
|
-
url: String,
|
128
|
-
|
129
|
-
|
130
|
-
)
|
169
|
+
sig do
|
170
|
+
params(url: String, auth_header: T::Hash[Symbol, T.untyped], repository_url: String).returns(Excon::Response)
|
171
|
+
end
|
172
|
+
private_class_method def self.execute_nuget_request_internal(url:, auth_header:, repository_url:)
|
131
173
|
cache = CacheManager.cache("dependency_url_search_cache")
|
132
174
|
if cache[url].nil?
|
133
175
|
response = Dependabot::RegistryClient.get(
|
@@ -147,11 +189,12 @@ module Dependabot
|
|
147
189
|
response
|
148
190
|
rescue Excon::Error::Timeout, Excon::Error::Socket
|
149
191
|
repo_url = repository_url
|
150
|
-
raise if repo_url == Dependabot::Nuget::
|
192
|
+
raise if repo_url == Dependabot::Nuget::RepositoryFinder::DEFAULT_REPOSITORY_URL
|
151
193
|
|
152
194
|
raise PrivateSourceTimedOut, repo_url
|
153
195
|
end
|
154
196
|
|
197
|
+
sig { params(string: String).returns(String) }
|
155
198
|
private_class_method def self.remove_wrapping_zero_width_chars(string)
|
156
199
|
string.force_encoding("UTF-8").encode
|
157
200
|
.gsub(/\A[\u200B-\u200D\uFEFF]/, "")
|
@@ -55,17 +55,21 @@ module Dependabot
|
|
55
55
|
File.rename(temporary_nuget_config_path, user_nuget_config_path)
|
56
56
|
end
|
57
57
|
|
58
|
-
# rubocop:disable Lint/SuppressedException
|
59
58
|
def self.patch_nuget_config_for_action(credentials, &_block)
|
60
59
|
add_credentials_to_nuget_config(credentials)
|
61
60
|
begin
|
62
61
|
yield
|
63
|
-
rescue StandardError
|
62
|
+
rescue StandardError => e
|
63
|
+
Dependabot.logger.error(
|
64
|
+
<<~LOG_MESSAGE
|
65
|
+
Block argument of NuGetConfigCredentialHelpers::patch_nuget_config_for_action causes an exception #{e}:
|
66
|
+
#{e.message}
|
67
|
+
LOG_MESSAGE
|
68
|
+
)
|
64
69
|
ensure
|
65
70
|
restore_user_nuget_config
|
66
71
|
end
|
67
72
|
end
|
68
|
-
# rubocop:enable Lint/SuppressedException
|
69
73
|
end
|
70
74
|
end
|
71
75
|
end
|