dependabot-nuget 0.246.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/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 +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 -41
- data/lib/dependabot/nuget/file_parser/property_value_finder.rb +57 -5
- data/lib/dependabot/nuget/file_parser.rb +3 -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/requirement.rb +4 -1
- data/lib/dependabot/nuget/update_checker/compatibility_checker.rb +26 -15
- data/lib/dependabot/nuget/update_checker/nupkg_fetcher.rb +11 -13
- data/lib/dependabot/nuget/update_checker/repository_finder.rb +25 -3
- 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 +4 -4
- 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 "dependabot/file_fetchers"
|
@@ -18,6 +18,7 @@ module Dependabot
|
|
18
18
|
|
19
19
|
BUILD_FILE_NAMES = /^Directory\.Build\.(props|targets)$/i # Directory.Build.props, Directory.Build.targets
|
20
20
|
|
21
|
+
sig { override.params(filenames: T::Array[String]).returns(T::Boolean) }
|
21
22
|
def self.required_files_in?(filenames)
|
22
23
|
return true if filenames.any? { |f| f.match?(/^packages\.config$/i) }
|
23
24
|
return true if filenames.any? { |f| f.end_with?(".sln") }
|
@@ -27,12 +28,32 @@ module Dependabot
|
|
27
28
|
filenames.any? { |name| name.match?(/\.[a-z]{2}proj$/) }
|
28
29
|
end
|
29
30
|
|
31
|
+
sig { override.returns(String) }
|
30
32
|
def self.required_files_message
|
31
33
|
"Repo must contain a .proj file, .(cs|vb|fs)proj file, or a packages.config."
|
32
34
|
end
|
33
35
|
|
36
|
+
sig do
|
37
|
+
params(
|
38
|
+
source: Dependabot::Source,
|
39
|
+
credentials: T::Array[Credential],
|
40
|
+
repo_contents_path: T.nilable(String),
|
41
|
+
options: T::Hash[String, String]
|
42
|
+
).void
|
43
|
+
end
|
44
|
+
def initialize(source:, credentials:, repo_contents_path: nil, options: {})
|
45
|
+
super(source: source, credentials: credentials, repo_contents_path: repo_contents_path, options: options)
|
46
|
+
|
47
|
+
@sln_files = T.let(nil, T.nilable(T::Array[Dependabot::DependencyFile]))
|
48
|
+
@sln_project_files = T.let(nil, T.nilable(T::Array[Dependabot::DependencyFile]))
|
49
|
+
@project_files = T.let([], T::Array[Dependabot::DependencyFile])
|
50
|
+
@fetched_files = T.let({}, T::Hash[String, T::Array[Dependabot::DependencyFile]])
|
51
|
+
@nuget_config_files = T.let(nil, T.nilable(T::Array[Dependabot::DependencyFile]))
|
52
|
+
@packages_config_files = T.let(nil, T.nilable(T::Array[Dependabot::DependencyFile]))
|
53
|
+
end
|
54
|
+
|
34
55
|
sig { override.returns(T::Array[DependencyFile]) }
|
35
|
-
def fetch_files
|
56
|
+
def fetch_files # rubocop:disable Metrics/AbcSize
|
36
57
|
fetched_files = []
|
37
58
|
fetched_files += project_files
|
38
59
|
fetched_files += directory_build_files
|
@@ -50,7 +71,7 @@ module Dependabot
|
|
50
71
|
end
|
51
72
|
|
52
73
|
if project_files.none? && packages_config_files.none?
|
53
|
-
raise @missing_sln_project_file_errors.first if @missing_sln_project_file_errors&.any?
|
74
|
+
raise T.must(@missing_sln_project_file_errors.first) if @missing_sln_project_file_errors&.any?
|
54
75
|
|
55
76
|
raise(
|
56
77
|
Dependabot::DependencyFileNotFound,
|
@@ -63,8 +84,11 @@ module Dependabot
|
|
63
84
|
|
64
85
|
private
|
65
86
|
|
87
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
66
88
|
def project_files
|
67
|
-
@project_files
|
89
|
+
return @project_files if @project_files.any?
|
90
|
+
|
91
|
+
@project_files =
|
68
92
|
begin
|
69
93
|
project_files = []
|
70
94
|
project_files += csproj_file
|
@@ -84,13 +108,14 @@ module Dependabot
|
|
84
108
|
)
|
85
109
|
end
|
86
110
|
|
111
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
87
112
|
def packages_config_files
|
88
113
|
return @packages_config_files if @packages_config_files
|
89
114
|
|
90
115
|
candidate_paths =
|
91
116
|
[*project_files.map { |f| File.dirname(f.name) }, "."].uniq
|
92
117
|
|
93
|
-
@packages_config_files
|
118
|
+
@packages_config_files =
|
94
119
|
candidate_paths.filter_map do |dir|
|
95
120
|
file = repo_contents(dir: dir)
|
96
121
|
.find { |f| f.name.casecmp("packages.config").zero? }
|
@@ -99,6 +124,7 @@ module Dependabot
|
|
99
124
|
end
|
100
125
|
|
101
126
|
# rubocop:disable Metrics/PerceivedComplexity
|
127
|
+
sig { returns(T.nilable(T::Array[T.untyped])) }
|
102
128
|
def sln_file_names
|
103
129
|
sln_files = repo_contents.select { |f| f.name.end_with?(".sln") }
|
104
130
|
src_dir = repo_contents.any? { |f| f.name == "src" && f.type == "dir" }
|
@@ -117,13 +143,15 @@ module Dependabot
|
|
117
143
|
end
|
118
144
|
# rubocop:enable Metrics/PerceivedComplexity
|
119
145
|
|
146
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
120
147
|
def directory_build_files
|
121
|
-
@directory_build_files ||= fetch_directory_build_files
|
148
|
+
@directory_build_files ||= T.let(fetch_directory_build_files, T.nilable(T::Array[Dependabot::DependencyFile]))
|
122
149
|
end
|
123
150
|
|
151
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
124
152
|
def fetch_directory_build_files
|
125
|
-
attempted_dirs = []
|
126
|
-
directory_build_files = []
|
153
|
+
attempted_dirs = T.let([], T::Array[Pathname])
|
154
|
+
directory_build_files = T.let([], T::Array[Dependabot::DependencyFile])
|
127
155
|
directory_path = Pathname.new(directory)
|
128
156
|
|
129
157
|
# find all build files (Directory.Build.props/.targets) relative to the given project file
|
@@ -145,12 +173,13 @@ module Dependabot
|
|
145
173
|
directory_build_files
|
146
174
|
end
|
147
175
|
|
176
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
148
177
|
def sln_project_files
|
149
178
|
return [] unless sln_files
|
150
179
|
|
151
180
|
@sln_project_files ||=
|
152
181
|
begin
|
153
|
-
paths = sln_files.flat_map do |sln_file|
|
182
|
+
paths = T.must(sln_files).flat_map do |sln_file|
|
154
183
|
SlnProjectPathsFinder
|
155
184
|
.new(sln_file: sln_file)
|
156
185
|
.project_paths
|
@@ -159,7 +188,7 @@ module Dependabot
|
|
159
188
|
paths.filter_map do |path|
|
160
189
|
fetch_file_from_host(path)
|
161
190
|
rescue Dependabot::DependencyFileNotFound => e
|
162
|
-
@missing_sln_project_file_errors ||= []
|
191
|
+
@missing_sln_project_file_errors ||= T.let([], T.nilable(T::Array[Dependabot::DependencyFileNotFound]))
|
163
192
|
@missing_sln_project_file_errors << e
|
164
193
|
# Don't worry about missing files too much for now (at least
|
165
194
|
# until we start resolving properties)
|
@@ -168,35 +197,42 @@ module Dependabot
|
|
168
197
|
end
|
169
198
|
end
|
170
199
|
|
200
|
+
sig { returns(T.nilable(T::Array[Dependabot::DependencyFile])) }
|
171
201
|
def sln_files
|
172
202
|
return unless sln_file_names
|
173
203
|
|
174
204
|
@sln_files ||=
|
175
205
|
sln_file_names
|
176
|
-
|
177
|
-
|
206
|
+
&.map { |sln_file_name| fetch_file_from_host(sln_file_name) }
|
207
|
+
&.select { |file| file.content&.valid_encoding? }
|
178
208
|
end
|
179
209
|
|
210
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
180
211
|
def csproj_file
|
181
|
-
@csproj_file ||= find_and_fetch_with_suffix(".csproj")
|
212
|
+
@csproj_file ||= T.let(find_and_fetch_with_suffix(".csproj"), T.nilable(T::Array[Dependabot::DependencyFile]))
|
182
213
|
end
|
183
214
|
|
215
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
184
216
|
def vbproj_file
|
185
|
-
@vbproj_file ||= find_and_fetch_with_suffix(".vbproj")
|
217
|
+
@vbproj_file ||= T.let(find_and_fetch_with_suffix(".vbproj"), T.nilable(T::Array[Dependabot::DependencyFile]))
|
186
218
|
end
|
187
219
|
|
220
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
188
221
|
def fsproj_file
|
189
|
-
@fsproj_file ||= find_and_fetch_with_suffix(".fsproj")
|
222
|
+
@fsproj_file ||= T.let(find_and_fetch_with_suffix(".fsproj"), T.nilable(T::Array[Dependabot::DependencyFile]))
|
190
223
|
end
|
191
224
|
|
225
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
192
226
|
def proj_files
|
193
|
-
@proj_files ||= find_and_fetch_with_suffix(".proj")
|
227
|
+
@proj_files ||= T.let(find_and_fetch_with_suffix(".proj"), T.nilable(T::Array[Dependabot::DependencyFile]))
|
194
228
|
end
|
195
229
|
|
230
|
+
sig { params(suffix: String).returns(T::Array[Dependabot::DependencyFile]) }
|
196
231
|
def find_and_fetch_with_suffix(suffix)
|
197
232
|
repo_contents.select { |f| f.name.end_with?(suffix) }.map { |f| fetch_file_from_host(f.name) }
|
198
233
|
end
|
199
234
|
|
235
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
200
236
|
def nuget_config_files
|
201
237
|
return @nuget_config_files if @nuget_config_files
|
202
238
|
|
@@ -206,8 +242,15 @@ module Dependabot
|
|
206
242
|
@nuget_config_files
|
207
243
|
end
|
208
244
|
|
245
|
+
sig do
|
246
|
+
params(
|
247
|
+
project_file: Dependabot::DependencyFile,
|
248
|
+
expected_file_name: String
|
249
|
+
)
|
250
|
+
.returns(T.nilable(Dependabot::DependencyFile))
|
251
|
+
end
|
209
252
|
def named_file_up_tree_from_project_file(project_file, expected_file_name)
|
210
|
-
found_expected_file = nil
|
253
|
+
found_expected_file = T.let(nil, T.nilable(Dependabot::DependencyFile))
|
211
254
|
directory_path = Pathname.new(directory)
|
212
255
|
full_project_dir = Pathname.new(project_file.directory).join(project_file.name).dirname
|
213
256
|
full_project_dir.ascend.each do |base|
|
@@ -228,26 +271,25 @@ module Dependabot
|
|
228
271
|
found_expected_file
|
229
272
|
end
|
230
273
|
|
274
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
231
275
|
def global_json
|
232
|
-
|
233
|
-
|
234
|
-
@global_json = fetch_file_if_present("global.json")
|
276
|
+
@global_json ||= T.let(fetch_file_if_present("global.json"), T.nilable(Dependabot::DependencyFile))
|
235
277
|
end
|
236
278
|
|
279
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
237
280
|
def dotnet_tools_json
|
238
|
-
|
239
|
-
|
240
|
-
@dotnet_tools_json = fetch_file_if_present(".config/dotnet-tools.json")
|
281
|
+
@dotnet_tools_json ||= T.let(fetch_file_if_present(".config/dotnet-tools.json"),
|
282
|
+
T.nilable(Dependabot::DependencyFile))
|
241
283
|
end
|
242
284
|
|
285
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
243
286
|
def packages_props
|
244
|
-
|
245
|
-
|
246
|
-
@packages_props = fetch_file_if_present("Packages.props")
|
287
|
+
@packages_props ||= T.let(fetch_file_if_present("Packages.props"), T.nilable(Dependabot::DependencyFile))
|
247
288
|
end
|
248
289
|
|
290
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
249
291
|
def imported_property_files
|
250
|
-
imported_property_files = []
|
292
|
+
imported_property_files = T.let([], T::Array[Dependabot::DependencyFile])
|
251
293
|
|
252
294
|
files = [*project_files, *directory_build_files]
|
253
295
|
|
@@ -263,18 +305,24 @@ module Dependabot
|
|
263
305
|
imported_property_files
|
264
306
|
end
|
265
307
|
|
308
|
+
sig do
|
309
|
+
params(
|
310
|
+
file: Dependabot::DependencyFile,
|
311
|
+
previously_fetched_files: T::Array[Dependabot::DependencyFile]
|
312
|
+
)
|
313
|
+
.returns(T::Array[Dependabot::DependencyFile])
|
314
|
+
end
|
266
315
|
def fetch_imported_property_files(file:, previously_fetched_files:)
|
267
316
|
file_id = file.directory + "/" + file.name
|
268
|
-
@fetched_files ||= {}
|
269
317
|
if @fetched_files[file_id]
|
270
|
-
@fetched_files[file_id]
|
318
|
+
T.must(@fetched_files[file_id])
|
271
319
|
else
|
272
320
|
paths =
|
273
321
|
ImportPathsFinder.new(project_file: file).import_paths +
|
274
322
|
ImportPathsFinder.new(project_file: file).project_reference_paths +
|
275
323
|
ImportPathsFinder.new(project_file: file).project_file_paths
|
276
324
|
|
277
|
-
paths.
|
325
|
+
paths.filter_map do |path|
|
278
326
|
next if previously_fetched_files.map(&:name).include?(path)
|
279
327
|
next if file.name == path
|
280
328
|
next if path.include?("$(")
|
@@ -290,7 +338,7 @@ module Dependabot
|
|
290
338
|
# Don't worry about missing files too much for now (at least
|
291
339
|
# until we start resolving properties)
|
292
340
|
nil
|
293
|
-
end.
|
341
|
+
end.flatten
|
294
342
|
end
|
295
343
|
end
|
296
344
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "json"
|
@@ -12,12 +12,17 @@ module Dependabot
|
|
12
12
|
module Nuget
|
13
13
|
class FileParser
|
14
14
|
class DotNetToolsJsonParser
|
15
|
+
extend T::Sig
|
16
|
+
|
15
17
|
require "dependabot/file_parsers/base/dependency_set"
|
16
18
|
|
19
|
+
sig { params(dotnet_tools_json: Dependabot::DependencyFile).void }
|
17
20
|
def initialize(dotnet_tools_json:)
|
18
21
|
@dotnet_tools_json = dotnet_tools_json
|
22
|
+
@parsed_dotnet_tools_json = T.let(nil, T.nilable(T::Hash[String, T.untyped]))
|
19
23
|
end
|
20
24
|
|
25
|
+
sig { returns(Dependabot::FileParsers::Base::DependencySet) }
|
21
26
|
def dependency_set
|
22
27
|
dependency_set = Dependabot::FileParsers::Base::DependencySet.new
|
23
28
|
|
@@ -48,11 +53,14 @@ module Dependabot
|
|
48
53
|
|
49
54
|
private
|
50
55
|
|
56
|
+
sig { returns(Dependabot::DependencyFile) }
|
51
57
|
attr_reader :dotnet_tools_json
|
52
58
|
|
59
|
+
sig { returns(T::Hash[String, T.untyped]) }
|
53
60
|
def parsed_dotnet_tools_json
|
54
61
|
# Remove BOM if present as JSON should be UTF-8
|
55
|
-
|
62
|
+
content = T.must(dotnet_tools_json.content)
|
63
|
+
@parsed_dotnet_tools_json ||= JSON.parse(content.delete_prefix("\uFEFF"))
|
56
64
|
rescue JSON::ParserError
|
57
65
|
raise Dependabot::DependencyFileNotParseable, dotnet_tools_json.path
|
58
66
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "json"
|
@@ -12,12 +12,17 @@ module Dependabot
|
|
12
12
|
module Nuget
|
13
13
|
class FileParser
|
14
14
|
class GlobalJsonParser
|
15
|
+
extend T::Sig
|
16
|
+
|
15
17
|
require "dependabot/file_parsers/base/dependency_set"
|
16
18
|
|
19
|
+
sig { params(global_json: Dependabot::DependencyFile).void }
|
17
20
|
def initialize(global_json:)
|
18
21
|
@global_json = global_json
|
22
|
+
@parsed_global_json = T.let(nil, T.nilable(T::Hash[String, T.untyped]))
|
19
23
|
end
|
20
24
|
|
25
|
+
sig { returns(Dependabot::FileParsers::Base::DependencySet) }
|
21
26
|
def dependency_set
|
22
27
|
dependency_set = Dependabot::FileParsers::Base::DependencySet.new
|
23
28
|
|
@@ -45,11 +50,14 @@ module Dependabot
|
|
45
50
|
|
46
51
|
private
|
47
52
|
|
53
|
+
sig { returns(Dependabot::DependencyFile) }
|
48
54
|
attr_reader :global_json
|
49
55
|
|
56
|
+
sig { returns(T::Hash[String, T.untyped]) }
|
50
57
|
def parsed_global_json
|
51
58
|
# Remove BOM if present as JSON should be UTF-8
|
52
|
-
|
59
|
+
content = T.must(global_json.content)
|
60
|
+
@parsed_global_json ||= JSON.parse(content.delete_prefix("\uFEFF"))
|
53
61
|
rescue JSON::ParserError
|
54
62
|
raise Dependabot::DependencyFileNotParseable, global_json.path
|
55
63
|
end
|
@@ -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
|