dependabot-nuget 0.246.0 → 0.248.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.Core/Utilities/MSBuildHelper.cs +40 -6
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/PathHelper.cs +27 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.Sdk.cs +18 -0
- data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/MSBuildHelperTests.cs +110 -0
- 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 +89 -37
- 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 -41
- data/lib/dependabot/nuget/file_parser/property_value_finder.rb +57 -5
- data/lib/dependabot/nuget/file_parser.rb +13 -3
- 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 +6 -1
- data/lib/dependabot/nuget/metadata_finder.rb +27 -3
- data/lib/dependabot/nuget/nuget_client.rb +23 -0
- data/lib/dependabot/nuget/nuget_config_credential_helpers.rb +10 -1
- data/lib/dependabot/nuget/requirement.rb +21 -9
- data/lib/dependabot/nuget/update_checker/compatibility_checker.rb +26 -15
- data/lib/dependabot/nuget/update_checker/nupkg_fetcher.rb +87 -21
- data/lib/dependabot/nuget/update_checker/nuspec_fetcher.rb +25 -3
- data/lib/dependabot/nuget/update_checker/repository_finder.rb +25 -3
- data/lib/dependabot/nuget/update_checker/requirements_updater.rb +32 -9
- data/lib/dependabot/nuget/update_checker/tfm_finder.rb +2 -2
- data/lib/dependabot/nuget/update_checker/version_finder.rb +178 -64
- data/lib/dependabot/nuget/update_checker.rb +76 -32
- data/lib/dependabot/nuget/version.rb +7 -2
- metadata +19 -5
@@ -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,28 +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
|
-
|
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
|
413
498
|
end
|
414
|
-
package_versions
|
415
499
|
end
|
500
|
+
package_versions
|
416
501
|
end
|
417
502
|
|
503
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
418
504
|
def directory_packages_props_files
|
419
505
|
dependency_files.select { |df| df.name.match?(/[Dd]irectory.[Pp]ackages.props/) }
|
420
506
|
end
|
421
507
|
|
508
|
+
sig { params(dependency_node: Nokogiri::XML::Node, project_file: DependencyFile).returns(T.nilable(String)) }
|
422
509
|
def dependency_version(dependency_node, project_file)
|
423
510
|
requirement = dependency_requirement(dependency_node, project_file)
|
424
511
|
return unless requirement
|
@@ -434,22 +521,24 @@ module Dependabot
|
|
434
521
|
version
|
435
522
|
end
|
436
523
|
|
524
|
+
sig { params(dependency_node: Nokogiri::XML::Node).returns(T.nilable(String)) }
|
437
525
|
def req_property_name(dependency_node)
|
438
526
|
raw_requirement = get_node_version_value(dependency_node)
|
439
527
|
return unless raw_requirement
|
440
528
|
|
441
529
|
return unless raw_requirement.match?(PROPERTY_REGEX)
|
442
530
|
|
443
|
-
raw_requirement
|
444
|
-
|
445
|
-
.named_captures.fetch("property")
|
531
|
+
T.must(raw_requirement.match(PROPERTY_REGEX))
|
532
|
+
.named_captures.fetch("property")
|
446
533
|
end
|
447
534
|
|
535
|
+
sig { params(node: Nokogiri::XML::Node).returns(T.nilable(String)) }
|
448
536
|
def get_node_version_value(node)
|
449
537
|
get_attribute_value(node, "Version") || get_attribute_value(node, "VersionOverride")
|
450
538
|
end
|
451
539
|
|
452
540
|
# rubocop:disable Metrics/PerceivedComplexity
|
541
|
+
sig { params(node: Nokogiri::XML::Node, attribute: String).returns(T.nilable(String)) }
|
453
542
|
def get_attribute_value(node, attribute)
|
454
543
|
value =
|
455
544
|
node.attribute(attribute)&.value&.strip ||
|
@@ -461,20 +550,24 @@ module Dependabot
|
|
461
550
|
end
|
462
551
|
# rubocop:enable Metrics/PerceivedComplexity
|
463
552
|
|
553
|
+
sig { params(value: String, project_file: Dependabot::DependencyFile).returns(String) }
|
464
554
|
def evaluated_value(value, project_file)
|
465
555
|
return value unless value.match?(PROPERTY_REGEX)
|
466
556
|
|
467
|
-
property_name = value.match(PROPERTY_REGEX)
|
468
|
-
.named_captures.fetch("property")
|
557
|
+
property_name = T.must(value.match(PROPERTY_REGEX)&.named_captures&.fetch("property"))
|
469
558
|
property_details = details_for_property(property_name, project_file)
|
470
559
|
|
471
560
|
# Don't halt parsing for a missing property value until we're
|
472
561
|
# confident we're fetching property values correctly
|
473
562
|
return value unless property_details&.fetch(:value)
|
474
563
|
|
475
|
-
value.gsub(PROPERTY_REGEX, property_details
|
564
|
+
value.gsub(PROPERTY_REGEX, property_details.fetch(:value))
|
476
565
|
end
|
477
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
|
478
571
|
def details_for_property(property_name, project_file)
|
479
572
|
property_value_finder
|
480
573
|
.property_details(
|
@@ -483,11 +576,13 @@ module Dependabot
|
|
483
576
|
)
|
484
577
|
end
|
485
578
|
|
579
|
+
sig { returns(PropertyValueFinder) }
|
486
580
|
def property_value_finder
|
487
581
|
@property_value_finder ||=
|
488
|
-
PropertyValueFinder.new(dependency_files: dependency_files)
|
582
|
+
T.let(PropertyValueFinder.new(dependency_files: dependency_files), T.nilable(PropertyValueFinder))
|
489
583
|
end
|
490
584
|
|
585
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
491
586
|
def project_import_files
|
492
587
|
dependency_files -
|
493
588
|
project_files -
|
@@ -497,22 +592,26 @@ module Dependabot
|
|
497
592
|
[dotnet_tools_json]
|
498
593
|
end
|
499
594
|
|
595
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
500
596
|
def project_files
|
501
597
|
dependency_files.select { |f| f.name.match?(/\.[a-z]{2}proj$/) }
|
502
598
|
end
|
503
599
|
|
600
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
504
601
|
def packages_config_files
|
505
602
|
dependency_files.select do |f|
|
506
|
-
f.name.split("/").last
|
603
|
+
f.name.split("/").last&.casecmp("packages.config")&.zero?
|
507
604
|
end
|
508
605
|
end
|
509
606
|
|
607
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
510
608
|
def global_json
|
511
|
-
dependency_files.find { |f| f.name.casecmp("global.json")
|
609
|
+
dependency_files.find { |f| f.name.casecmp("global.json")&.zero? }
|
512
610
|
end
|
513
611
|
|
612
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
514
613
|
def dotnet_tools_json
|
515
|
-
dependency_files.find { |f| f.name.casecmp(".config/dotnet-tools.json")
|
614
|
+
dependency_files.find { |f| f.name.casecmp(".config/dotnet-tools.json")&.zero? }
|
516
615
|
end
|
517
616
|
end
|
518
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,
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strong
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "nokogiri"
|
@@ -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
|
|
@@ -77,14 +87,14 @@ module Dependabot
|
|
77
87
|
def global_json_dependencies
|
78
88
|
return DependencySet.new unless global_json
|
79
89
|
|
80
|
-
GlobalJsonParser.new(global_json: global_json).dependency_set
|
90
|
+
GlobalJsonParser.new(global_json: T.must(global_json)).dependency_set
|
81
91
|
end
|
82
92
|
|
83
93
|
sig { returns(Dependabot::FileParsers::Base::DependencySet) }
|
84
94
|
def dotnet_tools_json_dependencies
|
85
95
|
return DependencySet.new unless dotnet_tools_json
|
86
96
|
|
87
|
-
DotNetToolsJsonParser.new(dotnet_tools_json: dotnet_tools_json).dependency_set
|
97
|
+
DotNetToolsJsonParser.new(dotnet_tools_json: T.must(dotnet_tools_json)).dependency_set
|
88
98
|
end
|
89
99
|
|
90
100
|
sig { returns(Dependabot::Nuget::FileParser::ProjectFileParser) }
|