dependabot-bun 0.304.0 → 0.305.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/lib/dependabot/bun/file_updater/bun_lockfile_updater.rb +1 -1
- data/lib/dependabot/bun/file_updater/npmrc_builder.rb +2 -2
- data/lib/dependabot/bun/metadata_finder.rb +2 -2
- data/lib/dependabot/bun/package/package_details_fetcher.rb +440 -0
- data/lib/dependabot/bun/package/registry_finder.rb +413 -0
- data/lib/dependabot/bun/update_checker/latest_version_finder.rb +532 -32
- data/lib/dependabot/bun/update_checker/library_detector.rb +1 -1
- data/lib/dependabot/bun/update_checker.rb +6 -3
- metadata +7 -6
- data/lib/dependabot/bun/update_checker/registry_finder.rb +0 -279
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ce7041993b9d64b4a4189d5ca9a4a03a77dff0a4976ba90320edc0ae5a38b9e3
|
4
|
+
data.tar.gz: 78474ef6d6bc726d9275a7817c3ff3d7666d4c5692319567060f045aee579440
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 952dc4d6c311dab15d48d7992ea857e2f18c0f2df16fb73804f8b2c3d8c3eb6334abf86d10ab455da85d6745d3c8ca7082433d9f8302374bb5d29d7b0f04e758
|
7
|
+
data.tar.gz: 786c6d9069871bf87518bfc1bdb464ad50fb7818f4ded374e6568c40cf7f31eeef01d692b8a41f07e57d2ca0783824d155cc44238a691a8aad49dd7649ec9cce
|
@@ -10,7 +10,7 @@ module Dependabot
|
|
10
10
|
class FileUpdater < Dependabot::FileUpdaters::Base
|
11
11
|
# Build a .npmrc file from the lockfile content, credentials, and any
|
12
12
|
# committed .npmrc
|
13
|
-
# We should refactor this to use
|
13
|
+
# We should refactor this to use Package::RegistryFinder
|
14
14
|
class NpmrcBuilder
|
15
15
|
extend T::Sig
|
16
16
|
|
@@ -121,7 +121,7 @@ module Dependabot
|
|
121
121
|
|
122
122
|
if dependencies.any?
|
123
123
|
@dependency_urls = dependencies.map do |dependency|
|
124
|
-
|
124
|
+
Package::RegistryFinder.new(
|
125
125
|
dependency: dependency,
|
126
126
|
credentials: credentials,
|
127
127
|
npmrc_file: npmrc_file
|
@@ -8,7 +8,7 @@ require "time"
|
|
8
8
|
require "dependabot/metadata_finders"
|
9
9
|
require "dependabot/metadata_finders/base"
|
10
10
|
require "dependabot/registry_client"
|
11
|
-
require "dependabot/bun/
|
11
|
+
require "dependabot/bun/package/registry_finder"
|
12
12
|
require "dependabot/bun/version"
|
13
13
|
|
14
14
|
module Dependabot
|
@@ -105,7 +105,7 @@ module Dependabot
|
|
105
105
|
def new_source
|
106
106
|
sources = dependency.requirements
|
107
107
|
.map { |r| r.fetch(:source) }.uniq.compact
|
108
|
-
.sort_by { |source|
|
108
|
+
.sort_by { |source| Package::RegistryFinder.central_registry?(source[:url]) ? 1 : 0 }
|
109
109
|
|
110
110
|
sources.first
|
111
111
|
end
|
@@ -0,0 +1,440 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "json"
|
5
|
+
require "excon"
|
6
|
+
require "time"
|
7
|
+
require "dependabot/package/package_release"
|
8
|
+
require "dependabot/package/package_details"
|
9
|
+
require "dependabot/bun/package/registry_finder"
|
10
|
+
|
11
|
+
module Dependabot
|
12
|
+
module Bun
|
13
|
+
module Package
|
14
|
+
class PackageDetailsFetcher
|
15
|
+
extend T::Sig
|
16
|
+
|
17
|
+
GLOBAL_REGISTRY = "registry.npmjs.org"
|
18
|
+
NPM_OFFICIAL_WEBSITE = "https://www.npmjs.com"
|
19
|
+
|
20
|
+
API_AUTHORIZATION_KEY = "Authorization"
|
21
|
+
API_AUTHORIZATION_VALUE_BASIC_PREFIX = "Basic"
|
22
|
+
API_RESPONSE_STATUS_SUCCESS_PREFIX = "2"
|
23
|
+
|
24
|
+
RELEASE_TIME_KEY = "time"
|
25
|
+
RELEASE_VERSIONS_KEY = "versions"
|
26
|
+
RELEASE_DIST_TAGS_KEY = "dist-tags"
|
27
|
+
RELEASE_DIST_TAGS_LATEST_KEY = "latest"
|
28
|
+
RELEASE_ENGINES_KEY = "engines"
|
29
|
+
RELEASE_LANGUAGE_KEY = "node"
|
30
|
+
RELEASE_DEPRECATION_KEY = "deprecated"
|
31
|
+
RELEASE_REPOSITORY_KEY = "repository"
|
32
|
+
RELEASE_PACKAGE_TYPE_KEY = "type"
|
33
|
+
RELEASE_PACKAGE_TYPE_GIT = "git"
|
34
|
+
RELEASE_PACKAGE_TYPE_NPM = "npm"
|
35
|
+
|
36
|
+
REGISTRY_FILE_NPMRC = ".npmrc"
|
37
|
+
REGISTRY_FILE_YARNRC = ".yarnrc"
|
38
|
+
REGISTRY_FILE_YARNRC_YML = ".yarnrc.yml"
|
39
|
+
|
40
|
+
sig do
|
41
|
+
params(
|
42
|
+
dependency: Dependabot::Dependency,
|
43
|
+
dependency_files: T::Array[Dependabot::DependencyFile],
|
44
|
+
credentials: T::Array[Dependabot::Credential]
|
45
|
+
).void
|
46
|
+
end
|
47
|
+
def initialize(
|
48
|
+
dependency:,
|
49
|
+
dependency_files:,
|
50
|
+
credentials:
|
51
|
+
)
|
52
|
+
@dependency = T.let(dependency, Dependabot::Dependency)
|
53
|
+
@dependency_files = T.let(dependency_files, T::Array[Dependabot::DependencyFile])
|
54
|
+
@credentials = T.let(credentials, T::Array[Dependabot::Credential])
|
55
|
+
|
56
|
+
@npm_details = T.let(nil, T.nilable(T::Hash[String, T.untyped]))
|
57
|
+
@dist_tags = T.let(nil, T.nilable(T::Hash[String, String]))
|
58
|
+
@registry_finder = T.let(nil, T.nilable(Package::RegistryFinder))
|
59
|
+
@version_endpoint_working = T.let(nil, T.nilable(T::Boolean))
|
60
|
+
@yanked = T.let({}, T::Hash[Gem::Version, T.nilable(T::Boolean)])
|
61
|
+
end
|
62
|
+
|
63
|
+
sig { returns(Dependabot::Dependency) }
|
64
|
+
attr_reader :dependency
|
65
|
+
|
66
|
+
sig { returns(T::Array[Dependabot::Credential]) }
|
67
|
+
attr_reader :credentials
|
68
|
+
|
69
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
70
|
+
attr_reader :dependency_files
|
71
|
+
|
72
|
+
sig { returns(T.nilable(Dependabot::Package::PackageDetails)) }
|
73
|
+
def fetch
|
74
|
+
package_data = npm_details
|
75
|
+
Dependabot::Package::PackageDetails.new(
|
76
|
+
dependency: @dependency,
|
77
|
+
releases: package_data ? parse_versions(package_data) : [],
|
78
|
+
dist_tags: dist_tags
|
79
|
+
)
|
80
|
+
end
|
81
|
+
|
82
|
+
sig { returns(T::Boolean) }
|
83
|
+
def valid_npm_details?
|
84
|
+
!dist_tags.nil?
|
85
|
+
end
|
86
|
+
|
87
|
+
sig { returns(T.nilable(T::Hash[String, T.untyped])) }
|
88
|
+
def npm_details
|
89
|
+
@npm_details ||= fetch_npm_details
|
90
|
+
end
|
91
|
+
|
92
|
+
sig { returns(T::Boolean) }
|
93
|
+
def custom_registry?
|
94
|
+
registry_finder.custom_registry?
|
95
|
+
end
|
96
|
+
|
97
|
+
sig { returns(String) }
|
98
|
+
def dependency_url
|
99
|
+
registry_finder.dependency_url
|
100
|
+
end
|
101
|
+
|
102
|
+
sig { params(version: Gem::Version).returns(T::Boolean) }
|
103
|
+
def yanked?(version)
|
104
|
+
return @yanked[version] || false if @yanked.key?(version)
|
105
|
+
|
106
|
+
@yanked[version] =
|
107
|
+
begin
|
108
|
+
if dependency_registry == GLOBAL_REGISTRY
|
109
|
+
status = Dependabot::RegistryClient.head(
|
110
|
+
url: registry_finder.tarball_url(version),
|
111
|
+
headers: registry_auth_headers
|
112
|
+
).status
|
113
|
+
else
|
114
|
+
status = Dependabot::RegistryClient.get(
|
115
|
+
url: dependency_url + "/#{version}",
|
116
|
+
headers: registry_auth_headers
|
117
|
+
).status
|
118
|
+
|
119
|
+
if status == 404
|
120
|
+
# Some registries don't handle escaped package names properly
|
121
|
+
status = Dependabot::RegistryClient.get(
|
122
|
+
url: dependency_url.gsub("%2F", "/") + "/#{version}",
|
123
|
+
headers: registry_auth_headers
|
124
|
+
).status
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
version_not_found = status == 404
|
129
|
+
version_not_found && version_endpoint_working?
|
130
|
+
rescue Excon::Error::Timeout, Excon::Error::Socket
|
131
|
+
# Give the benefit of the doubt if the registry is playing up
|
132
|
+
false
|
133
|
+
end
|
134
|
+
|
135
|
+
@yanked[version] || false
|
136
|
+
end
|
137
|
+
|
138
|
+
private
|
139
|
+
|
140
|
+
sig { returns(T.nilable(T::Boolean)) }
|
141
|
+
def version_endpoint_working?
|
142
|
+
return true if dependency_registry == GLOBAL_REGISTRY
|
143
|
+
|
144
|
+
return @version_endpoint_working if @version_endpoint_working
|
145
|
+
|
146
|
+
@version_endpoint_working =
|
147
|
+
begin
|
148
|
+
Dependabot::RegistryClient.get(
|
149
|
+
url: dependency_url + "/#{RELEASE_DIST_TAGS_LATEST_KEY}",
|
150
|
+
headers: registry_auth_headers
|
151
|
+
).status < 400
|
152
|
+
rescue Excon::Error::Timeout, Excon::Error::Socket
|
153
|
+
# Give the benefit of the doubt if the registry is playing up
|
154
|
+
true
|
155
|
+
end
|
156
|
+
@version_endpoint_working
|
157
|
+
end
|
158
|
+
|
159
|
+
sig do
|
160
|
+
params(
|
161
|
+
npm_data: T::Hash[String, T.untyped]
|
162
|
+
).returns(T::Array[Dependabot::Package::PackageRelease])
|
163
|
+
end
|
164
|
+
def parse_versions(npm_data)
|
165
|
+
time_data = fetch_value_from_hash(npm_data, RELEASE_TIME_KEY) || {}
|
166
|
+
versions_data = fetch_value_from_hash(npm_data, RELEASE_VERSIONS_KEY) || {}
|
167
|
+
|
168
|
+
dist_tags = fetch_value_from_hash(npm_data, RELEASE_DIST_TAGS_KEY)
|
169
|
+
latest_version = fetch_value_from_hash(dist_tags, RELEASE_DIST_TAGS_LATEST_KEY)
|
170
|
+
|
171
|
+
versions_data.filter_map do |version, details|
|
172
|
+
next unless Dependabot::Bun::Version.correct?(version)
|
173
|
+
|
174
|
+
package_type = infer_package_type(details)
|
175
|
+
|
176
|
+
deprecated = fetch_value_from_hash(details, RELEASE_DEPRECATION_KEY)
|
177
|
+
|
178
|
+
Dependabot::Package::PackageRelease.new(
|
179
|
+
version: Version.new(version),
|
180
|
+
released_at: time_data[version] ? Time.parse(time_data[version]) : nil,
|
181
|
+
yanked: deprecated ? true : false,
|
182
|
+
yanked_reason: deprecated.is_a?(String) ? deprecated : nil,
|
183
|
+
downloads: nil,
|
184
|
+
latest: latest_version.to_s == version,
|
185
|
+
url: package_version_url(version),
|
186
|
+
package_type: package_type,
|
187
|
+
language: package_language(details),
|
188
|
+
details: details
|
189
|
+
)
|
190
|
+
end.sort_by(&:version).reverse
|
191
|
+
end
|
192
|
+
|
193
|
+
sig { params(version: String).returns(String) }
|
194
|
+
def package_version_url(version)
|
195
|
+
"#{dependency_registry}/#{@dependency.name}/v/#{version}"
|
196
|
+
end
|
197
|
+
|
198
|
+
sig do
|
199
|
+
params(version_details: T::Hash[String, T.untyped])
|
200
|
+
.returns(T.nilable(Dependabot::Package::PackageLanguage))
|
201
|
+
end
|
202
|
+
def package_language(version_details)
|
203
|
+
# Fetch the engines hash from the version details
|
204
|
+
engines = version_details.is_a?(Hash) ? version_details[RELEASE_ENGINES_KEY] : nil
|
205
|
+
# Check if engines is a hash and fetch the node requirement
|
206
|
+
node_requirement = engines.is_a?(Hash) ? engines.fetch(RELEASE_LANGUAGE_KEY, nil) : nil
|
207
|
+
|
208
|
+
return nil unless node_requirement
|
209
|
+
|
210
|
+
if node_requirement
|
211
|
+
Dependabot::Package::PackageLanguage.new(
|
212
|
+
name: RELEASE_LANGUAGE_KEY,
|
213
|
+
version: nil,
|
214
|
+
requirement: Requirement.new(node_requirement)
|
215
|
+
)
|
216
|
+
end
|
217
|
+
rescue Gem::Requirement::BadRequirementError
|
218
|
+
nil
|
219
|
+
end
|
220
|
+
|
221
|
+
sig { returns(T.nilable(T::Hash[String, String])) }
|
222
|
+
def dist_tags
|
223
|
+
@dist_tags ||= fetch_value_from_hash(npm_details, RELEASE_DIST_TAGS_KEY)
|
224
|
+
end
|
225
|
+
|
226
|
+
sig { returns(T.nilable(T::Hash[String, T.untyped])) }
|
227
|
+
def fetch_npm_details
|
228
|
+
npm_response = fetch_npm_response
|
229
|
+
check_npm_response(npm_response) if npm_response
|
230
|
+
JSON.parse(npm_response.body)
|
231
|
+
rescue JSON::ParserError, Excon::Error::Timeout, Excon::Error::Socket, RegistryError => e
|
232
|
+
if git_dependency?
|
233
|
+
nil
|
234
|
+
else
|
235
|
+
raise_npm_details_error(e)
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
sig { returns(Excon::Response) }
|
240
|
+
def fetch_npm_response
|
241
|
+
response = Dependabot::RegistryClient.get(
|
242
|
+
url: dependency_url,
|
243
|
+
headers: registry_auth_headers
|
244
|
+
)
|
245
|
+
|
246
|
+
# If response is successful, return it
|
247
|
+
return response if response.status.to_s.start_with?(API_RESPONSE_STATUS_SUCCESS_PREFIX)
|
248
|
+
|
249
|
+
# If the registry is public (not explicitly private) and the request fails, return the response as is
|
250
|
+
return response if dependency_registry == GLOBAL_REGISTRY
|
251
|
+
|
252
|
+
# If a private registry returns a 500 error, check authentication
|
253
|
+
return response unless response.status == 500
|
254
|
+
|
255
|
+
auth = fetch_value_from_hash(registry_auth_headers, API_AUTHORIZATION_KEY)
|
256
|
+
return response unless auth
|
257
|
+
|
258
|
+
return response unless auth&.start_with?(API_AUTHORIZATION_VALUE_BASIC_PREFIX)
|
259
|
+
|
260
|
+
decoded_token = Base64.decode64(auth.gsub("#{API_AUTHORIZATION_VALUE_BASIC_PREFIX} ", "")).strip
|
261
|
+
|
262
|
+
# Ensure decoded token is not empty and contains a colon
|
263
|
+
if decoded_token.empty? || !decoded_token.include?(":")
|
264
|
+
raise PrivateSourceAuthenticationFailure, "Malformed basic auth credentials for #{dependency_registry}"
|
265
|
+
end
|
266
|
+
|
267
|
+
username, password = decoded_token.split(":")
|
268
|
+
|
269
|
+
Dependabot::RegistryClient.get(
|
270
|
+
url: dependency_url,
|
271
|
+
options: {
|
272
|
+
user: username,
|
273
|
+
password: password
|
274
|
+
}
|
275
|
+
)
|
276
|
+
rescue URI::InvalidURIError => e
|
277
|
+
raise DependencyFileNotResolvable, e.message
|
278
|
+
end
|
279
|
+
|
280
|
+
sig do
|
281
|
+
params(
|
282
|
+
details: T::Hash[String, T.untyped],
|
283
|
+
git_dependency: T::Boolean
|
284
|
+
)
|
285
|
+
.returns(String)
|
286
|
+
end
|
287
|
+
def infer_package_type(details, git_dependency: false)
|
288
|
+
return RELEASE_PACKAGE_TYPE_GIT if git_dependency
|
289
|
+
|
290
|
+
repository = fetch_value_from_hash(details, RELEASE_REPOSITORY_KEY)
|
291
|
+
|
292
|
+
case repository
|
293
|
+
when String
|
294
|
+
return repository.start_with?("git+") ? RELEASE_PACKAGE_TYPE_GIT : RELEASE_PACKAGE_TYPE_NPM
|
295
|
+
when Hash
|
296
|
+
type = fetch_value_from_hash(repository, RELEASE_PACKAGE_TYPE_KEY)
|
297
|
+
return RELEASE_PACKAGE_TYPE_GIT if type == RELEASE_PACKAGE_TYPE_GIT
|
298
|
+
end
|
299
|
+
|
300
|
+
RELEASE_PACKAGE_TYPE_NPM
|
301
|
+
end
|
302
|
+
|
303
|
+
sig { params(npm_response: Excon::Response).void }
|
304
|
+
def check_npm_response(npm_response)
|
305
|
+
return if git_dependency?
|
306
|
+
|
307
|
+
if private_dependency_not_reachable?(npm_response)
|
308
|
+
raise PrivateSourceAuthenticationFailure, dependency_registry
|
309
|
+
end
|
310
|
+
|
311
|
+
# handles scenario when private registry returns a server error 5xx
|
312
|
+
if private_dependency_server_error?(npm_response)
|
313
|
+
msg = "Server error #{npm_response.status} returned while accessing registry" \
|
314
|
+
" #{dependency_registry}."
|
315
|
+
raise DependencyFileNotResolvable, msg
|
316
|
+
end
|
317
|
+
|
318
|
+
status = npm_response.status
|
319
|
+
|
320
|
+
# handles issue when status 200 is returned from registry but with an invalid JSON object
|
321
|
+
if status.to_s.start_with?(API_RESPONSE_STATUS_SUCCESS_PREFIX) && response_invalid_json?(npm_response)
|
322
|
+
msg = "Invalid JSON object returned from registry #{dependency_registry}."
|
323
|
+
Dependabot.logger.warn("#{msg} Response body (truncated) : #{npm_response.body[0..500]}...")
|
324
|
+
raise DependencyFileNotResolvable, msg
|
325
|
+
end
|
326
|
+
|
327
|
+
return if status.to_s.start_with?(API_RESPONSE_STATUS_SUCCESS_PREFIX)
|
328
|
+
|
329
|
+
# Ignore 404s from the registry for updates where a lockfile doesn't
|
330
|
+
# need to be generated. The 404 won't cause problems later.
|
331
|
+
return if status == 404 && dependency.version.nil?
|
332
|
+
|
333
|
+
msg = "Got #{status} response with body #{npm_response.body}"
|
334
|
+
raise RegistryError.new(status, msg)
|
335
|
+
end
|
336
|
+
|
337
|
+
sig { params(error: StandardError).void }
|
338
|
+
def raise_npm_details_error(error)
|
339
|
+
raise if dependency_registry == GLOBAL_REGISTRY
|
340
|
+
raise unless error.is_a?(Excon::Error::Timeout)
|
341
|
+
|
342
|
+
raise PrivateSourceTimedOut, dependency_registry
|
343
|
+
end
|
344
|
+
|
345
|
+
sig { params(npm_response: Excon::Response).returns(T::Boolean) }
|
346
|
+
def private_dependency_not_reachable?(npm_response)
|
347
|
+
return true if npm_response.body.start_with?(/user ".*?" is not a /)
|
348
|
+
return false unless [401, 402, 403, 404].include?(npm_response.status)
|
349
|
+
|
350
|
+
# Check whether this dependency is (likely to be) private
|
351
|
+
if dependency_registry == GLOBAL_REGISTRY
|
352
|
+
return false unless dependency.name.start_with?("@")
|
353
|
+
|
354
|
+
web_response = Dependabot::RegistryClient.get(url: "#{NPM_OFFICIAL_WEBSITE}/package/#{dependency.name}")
|
355
|
+
# NOTE: returns 429 when the login page is rate limited
|
356
|
+
return web_response.body.include?("Forgot password?") ||
|
357
|
+
web_response.status == 429
|
358
|
+
end
|
359
|
+
|
360
|
+
true
|
361
|
+
end
|
362
|
+
|
363
|
+
sig { params(npm_response: Excon::Response).returns(T::Boolean) }
|
364
|
+
def private_dependency_server_error?(npm_response)
|
365
|
+
if [500, 501, 502, 503].include?(npm_response.status)
|
366
|
+
Dependabot.logger.warn(
|
367
|
+
"#{dependency_registry} returned code #{npm_response.status} " \
|
368
|
+
"with body #{npm_response.body}."
|
369
|
+
)
|
370
|
+
return true
|
371
|
+
end
|
372
|
+
false
|
373
|
+
end
|
374
|
+
|
375
|
+
sig { params(npm_response: Excon::Response).returns(T::Boolean) }
|
376
|
+
def response_invalid_json?(npm_response)
|
377
|
+
result = JSON.parse(npm_response.body)
|
378
|
+
result.is_a?(Hash) || result.is_a?(Array)
|
379
|
+
false
|
380
|
+
rescue JSON::ParserError, TypeError
|
381
|
+
true
|
382
|
+
end
|
383
|
+
|
384
|
+
sig { returns(T::Hash[String, String]) }
|
385
|
+
def registry_auth_headers
|
386
|
+
registry_finder.auth_headers
|
387
|
+
end
|
388
|
+
|
389
|
+
sig { returns(String) }
|
390
|
+
def dependency_registry
|
391
|
+
registry_finder.registry
|
392
|
+
end
|
393
|
+
|
394
|
+
sig { returns(Package::RegistryFinder) }
|
395
|
+
def registry_finder
|
396
|
+
@registry_finder ||= Package::RegistryFinder.new(
|
397
|
+
dependency: dependency,
|
398
|
+
credentials: credentials,
|
399
|
+
npmrc_file: npmrc_file,
|
400
|
+
yarnrc_file: yarnrc_file,
|
401
|
+
yarnrc_yml_file: yarnrc_yml_file
|
402
|
+
)
|
403
|
+
end
|
404
|
+
|
405
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
406
|
+
def npmrc_file
|
407
|
+
dependency_files.find { |f| f.name.end_with?(REGISTRY_FILE_NPMRC) }
|
408
|
+
end
|
409
|
+
|
410
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
411
|
+
def yarnrc_file
|
412
|
+
dependency_files.find { |f| f.name.end_with?(REGISTRY_FILE_YARNRC) }
|
413
|
+
end
|
414
|
+
|
415
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
416
|
+
def yarnrc_yml_file
|
417
|
+
dependency_files.find { |f| f.name.end_with?(REGISTRY_FILE_YARNRC_YML) }
|
418
|
+
end
|
419
|
+
|
420
|
+
sig { returns(T::Boolean) }
|
421
|
+
def git_dependency?
|
422
|
+
# ignored_version/raise_on_ignored are irrelevant.
|
423
|
+
GitCommitChecker.new(
|
424
|
+
dependency: dependency,
|
425
|
+
credentials: credentials
|
426
|
+
).git_dependency?
|
427
|
+
end
|
428
|
+
|
429
|
+
# This function safely retrieves a value for a given key from a Hash.
|
430
|
+
# If the hash is valid and the key exists, it will return the value, otherwise nil.
|
431
|
+
sig { params(hash: T.untyped, key: T.untyped).returns(T.untyped) }
|
432
|
+
def fetch_value_from_hash(hash, key)
|
433
|
+
return nil unless hash.is_a?(Hash) # Return nil if the hash is not a Hash
|
434
|
+
|
435
|
+
hash.fetch(key, nil) # Fetch the value for the given key, defaulting to nil if not found
|
436
|
+
end
|
437
|
+
end
|
438
|
+
end
|
439
|
+
end
|
440
|
+
end
|