dependabot-nuget 0.245.0 → 0.247.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.Core/Updater/PackagesConfigUpdater.cs +42 -7
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/SdkPackageUpdater.cs +164 -90
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/UpdaterWorker.cs +38 -2
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +92 -18
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/NuGetHelper.cs +1 -1
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/PathHelper.cs +27 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTestBase.cs +115 -14
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/{UpdateWorker.DirsProj.cs → UpdateWorkerTests.DirsProj.cs} +22 -24
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackagesConfig.cs +66 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.Sdk.cs +373 -83
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/MSBuildHelperTests.cs +117 -4
- data/lib/dependabot/nuget/cache_manager.rb +9 -3
- data/lib/dependabot/nuget/file_fetcher/import_paths_finder.rb +15 -12
- data/lib/dependabot/nuget/file_fetcher/sln_project_paths_finder.rb +13 -3
- data/lib/dependabot/nuget/file_fetcher.rb +79 -31
- data/lib/dependabot/nuget/file_parser/dotnet_tools_json_parser.rb +10 -2
- data/lib/dependabot/nuget/file_parser/global_json_parser.rb +10 -2
- data/lib/dependabot/nuget/file_parser/packages_config_parser.rb +11 -2
- data/lib/dependabot/nuget/file_parser/project_file_parser.rb +140 -45
- data/lib/dependabot/nuget/file_parser/property_value_finder.rb +57 -5
- data/lib/dependabot/nuget/file_parser.rb +18 -4
- data/lib/dependabot/nuget/file_updater/property_value_updater.rb +25 -8
- data/lib/dependabot/nuget/file_updater.rb +74 -38
- data/lib/dependabot/nuget/http_response_helpers.rb +19 -0
- data/lib/dependabot/nuget/metadata_finder.rb +32 -4
- data/lib/dependabot/nuget/nuget_client.rb +31 -13
- data/lib/dependabot/nuget/requirement.rb +4 -1
- data/lib/dependabot/nuget/update_checker/compatibility_checker.rb +26 -15
- data/lib/dependabot/nuget/update_checker/dependency_finder.rb +23 -13
- data/lib/dependabot/nuget/update_checker/nupkg_fetcher.rb +83 -21
- data/lib/dependabot/nuget/update_checker/repository_finder.rb +29 -13
- data/lib/dependabot/nuget/update_checker/tfm_finder.rb +2 -2
- data/lib/dependabot/nuget/update_checker/version_finder.rb +15 -6
- data/lib/dependabot/nuget/update_checker.rb +6 -7
- data/lib/dependabot/nuget/version.rb +7 -2
- metadata +21 -7
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/SdkPackageUpdaterTests.cs +0 -317
@@ -1,4 +1,4 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "nokogiri"
|
@@ -13,18 +13,22 @@ module Dependabot
|
|
13
13
|
module Nuget
|
14
14
|
class FileParser
|
15
15
|
class PackagesConfigParser
|
16
|
+
extend T::Sig
|
16
17
|
require "dependabot/file_parsers/base/dependency_set"
|
17
18
|
|
18
19
|
DEPENDENCY_SELECTOR = "packages > package"
|
19
20
|
|
21
|
+
sig { returns(T::Hash[String, Dependabot::FileParsers::Base::DependencySet]) }
|
20
22
|
def self.dependency_set_cache
|
21
23
|
CacheManager.cache("packages_config_dependency_set")
|
22
24
|
end
|
23
25
|
|
26
|
+
sig { params(packages_config: Dependabot::DependencyFile).void }
|
24
27
|
def initialize(packages_config:)
|
25
28
|
@packages_config = packages_config
|
26
29
|
end
|
27
30
|
|
31
|
+
sig { returns(Dependabot::FileParsers::Base::DependencySet) }
|
28
32
|
def dependency_set
|
29
33
|
key = "#{packages_config.name.downcase}::#{packages_config.content.hash}"
|
30
34
|
cache = PackagesConfigParser.dependency_set_cache
|
@@ -34,8 +38,10 @@ module Dependabot
|
|
34
38
|
|
35
39
|
private
|
36
40
|
|
41
|
+
sig { returns(Dependabot::DependencyFile) }
|
37
42
|
attr_reader :packages_config
|
38
43
|
|
44
|
+
sig { returns(Dependabot::FileParsers::Base::DependencySet) }
|
39
45
|
def parse_dependencies
|
40
46
|
dependency_set = Dependabot::FileParsers::Base::DependencySet.new
|
41
47
|
|
@@ -44,7 +50,7 @@ module Dependabot
|
|
44
50
|
doc.css(DEPENDENCY_SELECTOR).each do |dependency_node|
|
45
51
|
dependency_set <<
|
46
52
|
Dependency.new(
|
47
|
-
name: dependency_name(dependency_node),
|
53
|
+
name: T.must(dependency_name(dependency_node)),
|
48
54
|
version: dependency_version(dependency_node),
|
49
55
|
package_manager: "nuget",
|
50
56
|
requirements: [{
|
@@ -59,11 +65,13 @@ module Dependabot
|
|
59
65
|
dependency_set
|
60
66
|
end
|
61
67
|
|
68
|
+
sig { params(dependency_node: Nokogiri::XML::Node).returns(T.nilable(String)) }
|
62
69
|
def dependency_name(dependency_node)
|
63
70
|
dependency_node.attribute("id")&.value&.strip ||
|
64
71
|
dependency_node.at_xpath("./id")&.content&.strip
|
65
72
|
end
|
66
73
|
|
74
|
+
sig { params(dependency_node: Nokogiri::XML::Node).returns(T.nilable(String)) }
|
67
75
|
def dependency_version(dependency_node)
|
68
76
|
# Ranges and wildcards aren't allowed in a packages.config - the
|
69
77
|
# specified requirement is always an exact version.
|
@@ -71,6 +79,7 @@ module Dependabot
|
|
71
79
|
dependency_node.at_xpath("./version")&.content&.strip
|
72
80
|
end
|
73
81
|
|
82
|
+
sig { params(dependency_node: Nokogiri::XML::Node).returns(String) }
|
74
83
|
def dependency_type(dependency_node)
|
75
84
|
val = dependency_node.attribute("developmentDependency")&.value&.strip ||
|
76
85
|
dependency_node.at_xpath("./developmentDependency")&.content&.strip
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "nokogiri"
|
@@ -40,20 +40,31 @@ module Dependabot
|
|
40
40
|
PROPERTY_REGEX = /\$\((?<property>.*?)\)/
|
41
41
|
ITEM_REGEX = /\@\((?<property>.*?)\)/
|
42
42
|
|
43
|
+
sig { returns(T::Hash[String, Dependabot::FileParsers::Base::DependencySet]) }
|
43
44
|
def self.dependency_set_cache
|
44
45
|
CacheManager.cache("project_file_dependency_set")
|
45
46
|
end
|
46
47
|
|
48
|
+
sig { returns(T::Hash[String, T.untyped]) }
|
47
49
|
def self.dependency_url_search_cache
|
48
50
|
CacheManager.cache("dependency_url_search_cache")
|
49
51
|
end
|
50
52
|
|
53
|
+
sig do
|
54
|
+
params(dependency_files: T::Array[DependencyFile],
|
55
|
+
credentials: T::Array[Credential],
|
56
|
+
repo_contents_path: T.nilable(String)).void
|
57
|
+
end
|
51
58
|
def initialize(dependency_files:, credentials:, repo_contents_path:)
|
52
59
|
@dependency_files = dependency_files
|
53
60
|
@credentials = credentials
|
54
61
|
@repo_contents_path = repo_contents_path
|
55
62
|
end
|
56
63
|
|
64
|
+
sig do
|
65
|
+
params(project_file: DependencyFile, visited_project_files: T::Set[String])
|
66
|
+
.returns(Dependabot::FileParsers::Base::DependencySet)
|
67
|
+
end
|
57
68
|
def dependency_set(project_file:, visited_project_files: Set.new)
|
58
69
|
key = "#{project_file.name.downcase}::#{project_file.content.hash}"
|
59
70
|
cache = ProjectFileParser.dependency_set_cache
|
@@ -64,8 +75,9 @@ module Dependabot
|
|
64
75
|
cache[key] ||= parse_dependencies(project_file, visited_project_files)
|
65
76
|
end
|
66
77
|
|
78
|
+
sig { params(project_file: DependencyFile).returns(T::Set[String]) }
|
67
79
|
def downstream_file_references(project_file:)
|
68
|
-
file_set = Set.new
|
80
|
+
file_set = T.let(Set.new, T::Set[String])
|
69
81
|
|
70
82
|
doc = Nokogiri::XML(project_file.content)
|
71
83
|
doc.remove_namespaces!
|
@@ -74,9 +86,11 @@ module Dependabot
|
|
74
86
|
ref_nodes = proj_refs + proj_files
|
75
87
|
ref_nodes.each do |project_reference_node|
|
76
88
|
dep_file = get_attribute_value(project_reference_node, "Include")
|
89
|
+
next unless dep_file
|
90
|
+
|
77
91
|
full_project_path = full_path(project_file, dep_file)
|
78
92
|
full_project_path = full_project_path[1..-1] if full_project_path.start_with?("/")
|
79
|
-
full_project_paths = expand_wildcards_in_project_reference_path(full_project_path)
|
93
|
+
full_project_paths = expand_wildcards_in_project_reference_path(T.must(full_project_path))
|
80
94
|
full_project_paths.each do |full_project_path_expanded|
|
81
95
|
file_set << full_project_path_expanded if full_project_path_expanded
|
82
96
|
end
|
@@ -85,30 +99,37 @@ module Dependabot
|
|
85
99
|
file_set
|
86
100
|
end
|
87
101
|
|
102
|
+
sig { params(project_file: DependencyFile).returns(T::Array[String]) }
|
88
103
|
def target_frameworks(project_file:)
|
89
104
|
target_framework = details_for_property("TargetFramework", project_file)
|
90
|
-
return [target_framework
|
105
|
+
return [target_framework.fetch(:value)] if target_framework
|
91
106
|
|
92
107
|
target_frameworks = details_for_property("TargetFrameworks", project_file)
|
93
|
-
return target_frameworks
|
108
|
+
return target_frameworks.fetch(:value)&.split(";") if target_frameworks
|
94
109
|
|
95
110
|
target_framework = details_for_property("TargetFrameworkVersion", project_file)
|
96
111
|
return [] unless target_framework
|
97
112
|
|
98
113
|
# TargetFrameworkVersion is a string like "v4.7.2"
|
99
|
-
value = target_framework
|
114
|
+
value = target_framework.fetch(:value)
|
100
115
|
# convert it to a string like "net472"
|
101
116
|
["net#{value[1..-1].delete('.')}"]
|
102
117
|
end
|
103
118
|
|
119
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
104
120
|
def nuget_configs
|
105
121
|
dependency_files.select { |f| f.name.match?(%r{(^|/)nuget\.config$}i) }
|
106
122
|
end
|
107
123
|
|
108
124
|
private
|
109
125
|
|
110
|
-
|
126
|
+
sig { returns(T::Array[DependencyFile]) }
|
127
|
+
attr_reader :dependency_files
|
128
|
+
|
129
|
+
sig { returns(T::Array[Credential]) }
|
130
|
+
attr_reader :credentials
|
111
131
|
|
132
|
+
sig { params(project_file: DependencyFile, ref_path: String).returns(String) }
|
112
133
|
def full_path(project_file, ref_path)
|
113
134
|
project_file_directory = File.dirname(project_file.name)
|
114
135
|
is_rooted = project_file_directory.start_with?("/")
|
@@ -121,9 +142,13 @@ module Dependabot
|
|
121
142
|
relative_path = File.join(project_file_directory, relative_path)
|
122
143
|
result = File.expand_path(relative_path)
|
123
144
|
result = result[1..-1] unless is_rooted
|
124
|
-
result
|
145
|
+
T.must(result)
|
125
146
|
end
|
126
147
|
|
148
|
+
sig do
|
149
|
+
params(project_file: DependencyFile, visited_project_files: T.untyped)
|
150
|
+
.returns(Dependabot::FileParsers::Base::DependencySet)
|
151
|
+
end
|
127
152
|
def parse_dependencies(project_file, visited_project_files)
|
128
153
|
dependency_set = Dependabot::FileParsers::Base::DependencySet.new
|
129
154
|
|
@@ -152,6 +177,7 @@ module Dependabot
|
|
152
177
|
dependency_set
|
153
178
|
end
|
154
179
|
|
180
|
+
sig { params(dependency_set: Dependabot::FileParsers::Base::DependencySet).void }
|
155
181
|
def add_global_package_references(dependency_set)
|
156
182
|
project_import_files.each do |file|
|
157
183
|
doc = Nokogiri::XML(file.content)
|
@@ -169,11 +195,25 @@ module Dependabot
|
|
169
195
|
end
|
170
196
|
end
|
171
197
|
|
198
|
+
sig do
|
199
|
+
params(project_file: DependencyFile,
|
200
|
+
doc: Nokogiri::XML::Document,
|
201
|
+
dependency_set: Dependabot::FileParsers::Base::DependencySet,
|
202
|
+
visited_project_files: T::Set[String])
|
203
|
+
.void
|
204
|
+
end
|
172
205
|
def add_transitive_dependencies(project_file, doc, dependency_set, visited_project_files)
|
173
206
|
add_transitive_dependencies_from_packages(dependency_set)
|
174
207
|
add_transitive_dependencies_from_project_references(project_file, doc, dependency_set, visited_project_files)
|
175
208
|
end
|
176
209
|
|
210
|
+
sig do
|
211
|
+
params(project_file: DependencyFile,
|
212
|
+
doc: Nokogiri::XML::Document,
|
213
|
+
dependency_set: Dependabot::FileParsers::Base::DependencySet,
|
214
|
+
visited_project_files: T::Set[String])
|
215
|
+
.void
|
216
|
+
end
|
177
217
|
def add_transitive_dependencies_from_project_references(project_file, doc, dependency_set,
|
178
218
|
visited_project_files)
|
179
219
|
|
@@ -216,31 +256,33 @@ module Dependabot
|
|
216
256
|
end
|
217
257
|
end
|
218
258
|
|
219
|
-
sig { params(full_path:
|
259
|
+
sig { params(full_path: String).returns(T::Array[T.nilable(String)]) }
|
220
260
|
def expand_wildcards_in_project_reference_path(full_path)
|
221
|
-
full_path =
|
222
|
-
expanded_wildcard = Dir.glob(T.must(full_path))
|
223
|
-
|
224
|
-
filtered_paths = []
|
261
|
+
full_path = File.join(@repo_contents_path, full_path)
|
225
262
|
|
226
263
|
# For each expanded path, remove the @repo_contents_path prefix and leading slash
|
227
|
-
|
264
|
+
filtered_paths = Dir.glob(full_path).map do |path|
|
228
265
|
# Remove @repo_contents_path prefix
|
229
|
-
path = path.sub(@repo_contents_path, "")
|
266
|
+
path = path.sub(@repo_contents_path, "") if @repo_contents_path
|
230
267
|
# Remove leading slash
|
231
268
|
path = path[1..-1] if path.start_with?("/")
|
232
|
-
filtered_paths << path
|
233
269
|
path # Return the modified path
|
234
270
|
end
|
235
271
|
|
272
|
+
return filtered_paths if filtered_paths.any?
|
273
|
+
|
236
274
|
# If the wildcard didn't match anything, strip the @repo_contents_path prefix and return the original path.
|
237
|
-
|
275
|
+
full_path = full_path.sub(@repo_contents_path, "") if @repo_contents_path
|
276
|
+
full_path = full_path[1..-1] if full_path.start_with?("/")
|
277
|
+
[full_path]
|
238
278
|
end
|
239
279
|
|
280
|
+
sig { params(dependency_set: Dependabot::FileParsers::Base::DependencySet).void }
|
240
281
|
def add_transitive_dependencies_from_packages(dependency_set)
|
241
282
|
transitive_dependencies_from_packages(dependency_set.dependencies).each { |dep| dependency_set << dep }
|
242
283
|
end
|
243
284
|
|
285
|
+
sig { params(dependencies: T::Array[Dependency]).returns(T::Array[Dependency]) }
|
244
286
|
def transitive_dependencies_from_packages(dependencies)
|
245
287
|
transitive_dependencies = {}
|
246
288
|
|
@@ -261,6 +303,11 @@ module Dependabot
|
|
261
303
|
transitive_dependencies.values
|
262
304
|
end
|
263
305
|
|
306
|
+
sig do
|
307
|
+
params(doc: Nokogiri::XML::Document,
|
308
|
+
dependency_set: Dependabot::FileParsers::Base::DependencySet,
|
309
|
+
project_file: DependencyFile).void
|
310
|
+
end
|
264
311
|
def add_sdk_references(doc, dependency_set, project_file)
|
265
312
|
# These come in 3 flavours:
|
266
313
|
# - <Project Sdk="Name/Version">
|
@@ -273,8 +320,13 @@ module Dependabot
|
|
273
320
|
add_sdk_refs_from_import_tags(doc, dependency_set, project_file)
|
274
321
|
end
|
275
322
|
|
323
|
+
sig do
|
324
|
+
params(sdk_references: String,
|
325
|
+
dependency_set: Dependabot::FileParsers::Base::DependencySet,
|
326
|
+
project_file: DependencyFile).void
|
327
|
+
end
|
276
328
|
def add_sdk_ref_from_project(sdk_references, dependency_set, project_file)
|
277
|
-
sdk_references.split(";")
|
329
|
+
sdk_references.split(";").each do |sdk_reference|
|
278
330
|
m = sdk_reference.match(PROJECT_SDK_REGEX)
|
279
331
|
if m
|
280
332
|
dependency = build_dependency(m[1], m[2], m[2], nil, project_file)
|
@@ -283,6 +335,11 @@ module Dependabot
|
|
283
335
|
end
|
284
336
|
end
|
285
337
|
|
338
|
+
sig do
|
339
|
+
params(doc: Nokogiri::XML::Document,
|
340
|
+
dependency_set: Dependabot::FileParsers::Base::DependencySet,
|
341
|
+
project_file: DependencyFile).void
|
342
|
+
end
|
286
343
|
def add_sdk_refs_from_import_tags(doc, dependency_set, project_file)
|
287
344
|
doc.xpath("/Project/Import").each do |import_node|
|
288
345
|
next unless import_node.attribute("Sdk") && import_node.attribute("Version")
|
@@ -295,6 +352,11 @@ module Dependabot
|
|
295
352
|
end
|
296
353
|
end
|
297
354
|
|
355
|
+
sig do
|
356
|
+
params(doc: Nokogiri::XML::Document,
|
357
|
+
dependency_set: Dependabot::FileParsers::Base::DependencySet,
|
358
|
+
project_file: DependencyFile).void
|
359
|
+
end
|
298
360
|
def add_sdk_refs_from_project(doc, dependency_set, project_file)
|
299
361
|
doc.xpath("/Project").each do |project_node|
|
300
362
|
sdk_references = project_node.attribute("Sdk")&.value&.strip
|
@@ -304,6 +366,11 @@ module Dependabot
|
|
304
366
|
end
|
305
367
|
end
|
306
368
|
|
369
|
+
sig do
|
370
|
+
params(doc: Nokogiri::XML::Document,
|
371
|
+
dependency_set: Dependabot::FileParsers::Base::DependencySet,
|
372
|
+
project_file: DependencyFile).void
|
373
|
+
end
|
307
374
|
def add_sdk_refs_from_sdk_tags(doc, dependency_set, project_file)
|
308
375
|
doc.xpath("/Project/Sdk").each do |sdk_node|
|
309
376
|
next unless sdk_node.attribute("Version")
|
@@ -316,6 +383,15 @@ module Dependabot
|
|
316
383
|
end
|
317
384
|
end
|
318
385
|
|
386
|
+
sig do
|
387
|
+
params(name: T.nilable(String),
|
388
|
+
req: T.nilable(String),
|
389
|
+
version: T.nilable(String),
|
390
|
+
prop_name: T.nilable(String),
|
391
|
+
project_file: Dependabot::DependencyFile,
|
392
|
+
dev: T.untyped)
|
393
|
+
.returns(T.nilable(Dependabot::Dependency))
|
394
|
+
end
|
319
395
|
def build_dependency(name, req, version, prop_name, project_file, dev: false)
|
320
396
|
return unless name
|
321
397
|
|
@@ -350,6 +426,7 @@ module Dependabot
|
|
350
426
|
dependency
|
351
427
|
end
|
352
428
|
|
429
|
+
sig { params(dependency: Dependency).returns(T::Boolean) }
|
353
430
|
def dependency_has_search_results?(dependency)
|
354
431
|
dependency_urls = RepositoryFinder.new(
|
355
432
|
dependency: dependency,
|
@@ -362,11 +439,13 @@ module Dependabot
|
|
362
439
|
end
|
363
440
|
end
|
364
441
|
|
442
|
+
sig { params(dependency_name: String, dependency_url: T::Hash[Symbol, String]).returns(T.nilable(T::Boolean)) }
|
365
443
|
def dependency_url_has_matching_result?(dependency_name, dependency_url)
|
366
444
|
versions = NugetClient.get_package_versions(dependency_name, dependency_url)
|
367
445
|
versions&.any?
|
368
446
|
end
|
369
447
|
|
448
|
+
sig { params(dependency_node: Nokogiri::XML::Node, project_file: DependencyFile).returns(T.nilable(String)) }
|
370
449
|
def dependency_name(dependency_node, project_file)
|
371
450
|
raw_name = get_attribute_value(dependency_node, "Include") ||
|
372
451
|
get_attribute_value(dependency_node, "Update")
|
@@ -379,6 +458,7 @@ module Dependabot
|
|
379
458
|
evaluated_value(raw_name, project_file)
|
380
459
|
end
|
381
460
|
|
461
|
+
sig { params(dependency_node: Nokogiri::XML::Node, project_file: DependencyFile).returns(T.nilable(String)) }
|
382
462
|
def dependency_requirement(dependency_node, project_file)
|
383
463
|
raw_requirement = get_node_version_value(dependency_node) ||
|
384
464
|
find_package_version(dependency_node, project_file)
|
@@ -387,6 +467,7 @@ module Dependabot
|
|
387
467
|
evaluated_value(raw_requirement, project_file)
|
388
468
|
end
|
389
469
|
|
470
|
+
sig { params(dependency_node: Nokogiri::XML::Node, project_file: DependencyFile).returns(T.nilable(String)) }
|
390
471
|
def find_package_version(dependency_node, project_file)
|
391
472
|
name = dependency_name(dependency_node, project_file)
|
392
473
|
return unless name
|
@@ -397,32 +478,34 @@ module Dependabot
|
|
397
478
|
package_version_string
|
398
479
|
end
|
399
480
|
|
481
|
+
sig { returns(T::Hash[String, String]) }
|
400
482
|
def package_versions
|
401
|
-
@package_versions ||=
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
end
|
483
|
+
@package_versions ||= T.let(parse_package_versions, T.nilable(T::Hash[String, String]))
|
484
|
+
end
|
485
|
+
|
486
|
+
sig { returns(T::Hash[String, String]) }
|
487
|
+
def parse_package_versions
|
488
|
+
package_versions = T.let({}, T::Hash[String, String])
|
489
|
+
directory_packages_props_files.each do |file|
|
490
|
+
doc = Nokogiri::XML(file.content)
|
491
|
+
doc.remove_namespaces!
|
492
|
+
doc.css(PACKAGE_VERSION_SELECTOR).each do |package_node|
|
493
|
+
name = dependency_name(package_node, file)
|
494
|
+
version = dependency_version(package_node, file)
|
495
|
+
next unless name && version
|
496
|
+
|
497
|
+
package_versions[name] = version
|
417
498
|
end
|
418
|
-
package_versions
|
419
499
|
end
|
500
|
+
package_versions
|
420
501
|
end
|
421
502
|
|
503
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
422
504
|
def directory_packages_props_files
|
423
505
|
dependency_files.select { |df| df.name.match?(/[Dd]irectory.[Pp]ackages.props/) }
|
424
506
|
end
|
425
507
|
|
508
|
+
sig { params(dependency_node: Nokogiri::XML::Node, project_file: DependencyFile).returns(T.nilable(String)) }
|
426
509
|
def dependency_version(dependency_node, project_file)
|
427
510
|
requirement = dependency_requirement(dependency_node, project_file)
|
428
511
|
return unless requirement
|
@@ -438,22 +521,24 @@ module Dependabot
|
|
438
521
|
version
|
439
522
|
end
|
440
523
|
|
524
|
+
sig { params(dependency_node: Nokogiri::XML::Node).returns(T.nilable(String)) }
|
441
525
|
def req_property_name(dependency_node)
|
442
526
|
raw_requirement = get_node_version_value(dependency_node)
|
443
527
|
return unless raw_requirement
|
444
528
|
|
445
529
|
return unless raw_requirement.match?(PROPERTY_REGEX)
|
446
530
|
|
447
|
-
raw_requirement
|
448
|
-
|
449
|
-
.named_captures.fetch("property")
|
531
|
+
T.must(raw_requirement.match(PROPERTY_REGEX))
|
532
|
+
.named_captures.fetch("property")
|
450
533
|
end
|
451
534
|
|
535
|
+
sig { params(node: Nokogiri::XML::Node).returns(T.nilable(String)) }
|
452
536
|
def get_node_version_value(node)
|
453
537
|
get_attribute_value(node, "Version") || get_attribute_value(node, "VersionOverride")
|
454
538
|
end
|
455
539
|
|
456
540
|
# rubocop:disable Metrics/PerceivedComplexity
|
541
|
+
sig { params(node: Nokogiri::XML::Node, attribute: String).returns(T.nilable(String)) }
|
457
542
|
def get_attribute_value(node, attribute)
|
458
543
|
value =
|
459
544
|
node.attribute(attribute)&.value&.strip ||
|
@@ -465,20 +550,24 @@ module Dependabot
|
|
465
550
|
end
|
466
551
|
# rubocop:enable Metrics/PerceivedComplexity
|
467
552
|
|
553
|
+
sig { params(value: String, project_file: Dependabot::DependencyFile).returns(String) }
|
468
554
|
def evaluated_value(value, project_file)
|
469
555
|
return value unless value.match?(PROPERTY_REGEX)
|
470
556
|
|
471
|
-
property_name = value.match(PROPERTY_REGEX)
|
472
|
-
.named_captures.fetch("property")
|
557
|
+
property_name = T.must(value.match(PROPERTY_REGEX)&.named_captures&.fetch("property"))
|
473
558
|
property_details = details_for_property(property_name, project_file)
|
474
559
|
|
475
560
|
# Don't halt parsing for a missing property value until we're
|
476
561
|
# confident we're fetching property values correctly
|
477
562
|
return value unless property_details&.fetch(:value)
|
478
563
|
|
479
|
-
value.gsub(PROPERTY_REGEX, property_details
|
564
|
+
value.gsub(PROPERTY_REGEX, property_details.fetch(:value))
|
480
565
|
end
|
481
566
|
|
567
|
+
sig do
|
568
|
+
params(property_name: String, project_file: Dependabot::DependencyFile)
|
569
|
+
.returns(T.nilable(T::Hash[T.untyped, T.untyped]))
|
570
|
+
end
|
482
571
|
def details_for_property(property_name, project_file)
|
483
572
|
property_value_finder
|
484
573
|
.property_details(
|
@@ -487,11 +576,13 @@ module Dependabot
|
|
487
576
|
)
|
488
577
|
end
|
489
578
|
|
579
|
+
sig { returns(PropertyValueFinder) }
|
490
580
|
def property_value_finder
|
491
581
|
@property_value_finder ||=
|
492
|
-
PropertyValueFinder.new(dependency_files: dependency_files)
|
582
|
+
T.let(PropertyValueFinder.new(dependency_files: dependency_files), T.nilable(PropertyValueFinder))
|
493
583
|
end
|
494
584
|
|
585
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
495
586
|
def project_import_files
|
496
587
|
dependency_files -
|
497
588
|
project_files -
|
@@ -501,22 +592,26 @@ module Dependabot
|
|
501
592
|
[dotnet_tools_json]
|
502
593
|
end
|
503
594
|
|
595
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
504
596
|
def project_files
|
505
597
|
dependency_files.select { |f| f.name.match?(/\.[a-z]{2}proj$/) }
|
506
598
|
end
|
507
599
|
|
600
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
508
601
|
def packages_config_files
|
509
602
|
dependency_files.select do |f|
|
510
|
-
f.name.split("/").last
|
603
|
+
f.name.split("/").last&.casecmp("packages.config")&.zero?
|
511
604
|
end
|
512
605
|
end
|
513
606
|
|
607
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
514
608
|
def global_json
|
515
|
-
dependency_files.find { |f| f.name.casecmp("global.json")
|
609
|
+
dependency_files.find { |f| f.name.casecmp("global.json")&.zero? }
|
516
610
|
end
|
517
611
|
|
612
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
518
613
|
def dotnet_tools_json
|
519
|
-
dependency_files.find { |f| f.name.casecmp(".config/dotnet-tools.json")
|
614
|
+
dependency_files.find { |f| f.name.casecmp(".config/dotnet-tools.json")&.zero? }
|
520
615
|
end
|
521
616
|
end
|
522
617
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "dependabot/nuget/file_fetcher/import_paths_finder"
|
@@ -11,12 +11,21 @@ module Dependabot
|
|
11
11
|
module Nuget
|
12
12
|
class FileParser
|
13
13
|
class PropertyValueFinder
|
14
|
+
extend T::Sig
|
15
|
+
|
14
16
|
PROPERTY_REGEX = /\$\((?<property>.*?)\)/
|
15
17
|
|
18
|
+
sig { params(dependency_files: T::Array[Dependabot::DependencyFile]).void }
|
16
19
|
def initialize(dependency_files:)
|
17
20
|
@dependency_files = dependency_files
|
18
21
|
end
|
19
22
|
|
23
|
+
sig do
|
24
|
+
params(property_name: String,
|
25
|
+
callsite_file: Dependabot::DependencyFile,
|
26
|
+
stack: T::Array[[String, String]])
|
27
|
+
.returns(T.nilable(T::Hash[T.untyped, T.untyped]))
|
28
|
+
end
|
20
29
|
def property_details(property_name:, callsite_file:, stack: [])
|
21
30
|
stack += [[property_name, callsite_file.name]]
|
22
31
|
return if property_name.include?("(")
|
@@ -53,12 +62,18 @@ module Dependabot
|
|
53
62
|
check_next_level_of_stack(node_details, stack)
|
54
63
|
end
|
55
64
|
|
65
|
+
sig do
|
66
|
+
params(node_details: T.untyped,
|
67
|
+
stack: T::Array[[String, String]])
|
68
|
+
.returns(T.nilable(T::Hash[T.untyped, T.untyped]))
|
69
|
+
end
|
56
70
|
def check_next_level_of_stack(node_details, stack)
|
57
71
|
property_name = node_details.fetch(:value)
|
58
72
|
.match(PROPERTY_REGEX)
|
59
73
|
.named_captures.fetch("property")
|
60
74
|
callsite_file = dependency_files
|
61
75
|
.find { |f| f.name == node_details.fetch(:file) }
|
76
|
+
return unless callsite_file
|
62
77
|
|
63
78
|
raise "Circular reference!" if stack.include?([property_name, callsite_file.name])
|
64
79
|
|
@@ -71,8 +86,14 @@ module Dependabot
|
|
71
86
|
|
72
87
|
private
|
73
88
|
|
89
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
74
90
|
attr_reader :dependency_files
|
75
91
|
|
92
|
+
sig do
|
93
|
+
params(property: String,
|
94
|
+
file: Dependabot::DependencyFile)
|
95
|
+
.returns(T.nilable(T::Hash[Symbol, T.untyped]))
|
96
|
+
end
|
76
97
|
def deep_find_prop_node(property:, file:)
|
77
98
|
doc = Nokogiri::XML(file.content)
|
78
99
|
doc.remove_namespaces!
|
@@ -100,21 +121,34 @@ module Dependabot
|
|
100
121
|
deep_find_prop_node(property: property, file: file)
|
101
122
|
end
|
102
123
|
|
124
|
+
sig do
|
125
|
+
params(property: String, callsite_file: Dependabot::DependencyFile)
|
126
|
+
.returns(T.nilable(T::Hash[Symbol, T.untyped]))
|
127
|
+
end
|
103
128
|
def find_property_in_directory_build_targets(property:, callsite_file:)
|
104
129
|
find_property_in_up_tree_files(property: property, callsite_file: callsite_file,
|
105
130
|
expected_file_name: "Directory.Build.targets")
|
106
131
|
end
|
107
132
|
|
133
|
+
sig do
|
134
|
+
params(property: String, callsite_file: Dependabot::DependencyFile)
|
135
|
+
.returns(T.nilable(T::Hash[Symbol, T.untyped]))
|
136
|
+
end
|
108
137
|
def find_property_in_directory_build_props(property:, callsite_file:)
|
109
138
|
find_property_in_up_tree_files(property: property, callsite_file: callsite_file,
|
110
139
|
expected_file_name: "Directory.Build.props")
|
111
140
|
end
|
112
141
|
|
142
|
+
sig do
|
143
|
+
params(property: String, callsite_file: Dependabot::DependencyFile)
|
144
|
+
.returns(T.nilable(T::Hash[Symbol, T.untyped]))
|
145
|
+
end
|
113
146
|
def find_property_in_directory_packages_props(property:, callsite_file:)
|
114
147
|
find_property_in_up_tree_files(property: property, callsite_file: callsite_file,
|
115
148
|
expected_file_name: "Directory.Packages.props")
|
116
149
|
end
|
117
150
|
|
151
|
+
sig { params(property: String).returns(T.nilable(T::Hash[Symbol, T.untyped])) }
|
118
152
|
def find_property_in_packages_props(property:)
|
119
153
|
file = packages_props_file
|
120
154
|
return unless file
|
@@ -122,15 +156,25 @@ module Dependabot
|
|
122
156
|
deep_find_prop_node(property: property, file: file)
|
123
157
|
end
|
124
158
|
|
159
|
+
sig do
|
160
|
+
params(property: String,
|
161
|
+
callsite_file: Dependabot::DependencyFile,
|
162
|
+
expected_file_name: String)
|
163
|
+
.returns(T.untyped)
|
164
|
+
end
|
125
165
|
def find_property_in_up_tree_files(property:, callsite_file:, expected_file_name:)
|
126
166
|
files = up_tree_files_for_project(callsite_file, expected_file_name)
|
127
|
-
return unless files
|
128
167
|
return if files.empty?
|
129
168
|
|
130
169
|
# first file where we were able to find the node
|
131
|
-
files.reduce(nil)
|
170
|
+
files.reduce(T.let(nil, T.nilable(String))) do |acc, file|
|
171
|
+
acc || deep_find_prop_node(property: property, file: file)
|
172
|
+
end
|
132
173
|
end
|
133
174
|
|
175
|
+
sig do
|
176
|
+
params(project_file: DependencyFile, expected_file_name: String).returns(T::Array[Dependabot::DependencyFile])
|
177
|
+
end
|
134
178
|
def up_tree_files_for_project(project_file, expected_file_name)
|
135
179
|
dir = File.dirname(project_file.name)
|
136
180
|
|
@@ -142,21 +186,29 @@ module Dependabot
|
|
142
186
|
|
143
187
|
paths =
|
144
188
|
possible_paths.uniq
|
145
|
-
.select { |p| dependency_files.find { |f| f.name.casecmp(p)
|
189
|
+
.select { |p| dependency_files.find { |f| f.name.casecmp(p)&.zero? } }
|
146
190
|
|
147
191
|
dependency_files.select { |f| paths.include?(f.name) }
|
148
192
|
end
|
149
193
|
|
194
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
150
195
|
def packages_props_file
|
151
|
-
dependency_files.find { |f| f.name.casecmp("Packages.props")
|
196
|
+
dependency_files.find { |f| f.name.casecmp("Packages.props")&.zero? }
|
152
197
|
end
|
153
198
|
|
199
|
+
sig { params(property_name: String).returns(String) }
|
154
200
|
def property_xpath(property_name)
|
155
201
|
# only return properties that don't have a `Condition` attribute or the `Condition` attribute is checking for
|
156
202
|
# an empty string, e.g., Condition="$(SomeProperty) == ''"
|
157
203
|
%{/Project/PropertyGroup/#{property_name}[not(@Condition) or @Condition="$(#{property_name}) == ''"]}
|
158
204
|
end
|
159
205
|
|
206
|
+
sig do
|
207
|
+
params(file: DependencyFile,
|
208
|
+
node: Nokogiri::XML::Node,
|
209
|
+
property: String)
|
210
|
+
.returns(T::Hash[Symbol, T.untyped])
|
211
|
+
end
|
160
212
|
def node_details(file:, node:, property:)
|
161
213
|
{
|
162
214
|
file: file.name,
|