dependabot-npm_and_yarn 0.302.0 → 0.304.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/npm_and_yarn/file_updater/bun_lockfile_updater.rb +1 -1
- data/lib/dependabot/npm_and_yarn/file_updater/npm_lockfile_updater.rb +3 -3
- data/lib/dependabot/npm_and_yarn/file_updater/npmrc_builder.rb +3 -3
- data/lib/dependabot/npm_and_yarn/file_updater/pnpm_lockfile_updater.rb +2 -2
- data/lib/dependabot/npm_and_yarn/file_updater/pnpm_workspace_updater.rb +1 -1
- data/lib/dependabot/npm_and_yarn/file_updater/yarn_lockfile_updater.rb +5 -5
- data/lib/dependabot/npm_and_yarn/metadata_finder.rb +2 -2
- data/lib/dependabot/npm_and_yarn/package/package_details_fetcher.rb +440 -0
- data/lib/dependabot/npm_and_yarn/{update_checker → package}/registry_finder.rb +125 -34
- data/lib/dependabot/npm_and_yarn/update_checker/dependency_files_builder.rb +2 -2
- data/lib/dependabot/npm_and_yarn/update_checker/latest_version_finder.rb +532 -30
- data/lib/dependabot/npm_and_yarn/update_checker/library_detector.rb +1 -1
- data/lib/dependabot/npm_and_yarn/update_checker/version_resolver.rb +270 -61
- data/lib/dependabot/npm_and_yarn/update_checker.rb +140 -23
- metadata +9 -8
@@ -1,14 +1,19 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "excon"
|
5
5
|
require "dependabot/npm_and_yarn/update_checker"
|
6
6
|
require "dependabot/registry_client"
|
7
|
+
require "sorbet-runtime"
|
7
8
|
|
8
9
|
module Dependabot
|
9
10
|
module NpmAndYarn
|
10
|
-
|
11
|
+
module Package
|
11
12
|
class RegistryFinder
|
13
|
+
extend T::Sig
|
14
|
+
|
15
|
+
GLOBAL_NPM_REGISTRY = "https://registry.npmjs.org"
|
16
|
+
|
12
17
|
CENTRAL_REGISTRIES = %w(
|
13
18
|
https://registry.npmjs.org
|
14
19
|
http://registry.npmjs.org
|
@@ -21,6 +26,15 @@ module Dependabot
|
|
21
26
|
NPM_SCOPED_REGISTRY_REGEX = /^(?<scope>@[^:]+)\s*:registry\s*=\s*['"]?(?<registry>.*?)['"]?$/
|
22
27
|
YARN_SCOPED_REGISTRY_REGEX = /['"](?<scope>@[^:]+):registry['"]\s((['"](?<registry>.*)['"])|(?<registry>.*))/
|
23
28
|
|
29
|
+
sig do
|
30
|
+
params(
|
31
|
+
dependency: T.nilable(Dependabot::Dependency),
|
32
|
+
credentials: T::Array[Dependabot::Credential],
|
33
|
+
npmrc_file: T.nilable(Dependabot::DependencyFile),
|
34
|
+
yarnrc_file: T.nilable(Dependabot::DependencyFile),
|
35
|
+
yarnrc_yml_file: T.nilable(Dependabot::DependencyFile)
|
36
|
+
).void
|
37
|
+
end
|
24
38
|
def initialize(dependency:, credentials:, npmrc_file: nil,
|
25
39
|
yarnrc_file: nil, yarnrc_yml_file: nil)
|
26
40
|
@dependency = dependency
|
@@ -28,58 +42,91 @@ module Dependabot
|
|
28
42
|
@npmrc_file = npmrc_file
|
29
43
|
@yarnrc_file = yarnrc_file
|
30
44
|
@yarnrc_yml_file = yarnrc_yml_file
|
45
|
+
|
46
|
+
@registry = T.let(nil, T.nilable(String))
|
47
|
+
@first_registry_with_dependency_details = T.let(nil, T.nilable(String))
|
48
|
+
@known_registries = T.let([], T::Array[T::Hash[String, T.nilable(String)]])
|
49
|
+
@configured_global_registry = T.let(nil, T.nilable(String))
|
50
|
+
@global_registry = T.let(nil, T.nilable(String))
|
51
|
+
@parsed_yarnrc_yml = T.let(nil, T.nilable(T::Hash[String, T.untyped]))
|
31
52
|
end
|
32
53
|
|
54
|
+
sig { returns(String) }
|
33
55
|
def registry
|
34
|
-
@registry
|
56
|
+
return @registry if @registry
|
57
|
+
|
58
|
+
@registry = locked_registry || configured_registry || first_registry_with_dependency_details
|
59
|
+
T.must(@registry)
|
35
60
|
end
|
36
61
|
|
62
|
+
sig { returns(T::Hash[String, String]) }
|
37
63
|
def auth_headers
|
38
64
|
auth_header_for(auth_token)
|
39
65
|
end
|
40
66
|
|
67
|
+
sig { returns(String) }
|
41
68
|
def dependency_url
|
42
69
|
"#{registry_url}/#{escaped_dependency_name}"
|
43
70
|
end
|
44
71
|
|
72
|
+
sig { params(version: T.any(String, Gem::Version)).returns(String) }
|
45
73
|
def tarball_url(version)
|
46
74
|
version_without_build_metadata = version.to_s.gsub(/\+.*/, "")
|
47
75
|
|
48
76
|
# Dependency name needs to be unescaped since tarball URLs don't always work with escaped slashes
|
49
|
-
"#{registry_url}/#{dependency
|
77
|
+
"#{registry_url}/#{dependency&.name}/-/#{scopeless_name}-#{version_without_build_metadata}.tgz"
|
50
78
|
end
|
51
79
|
|
80
|
+
sig { params(registry: String).returns(T::Boolean) }
|
52
81
|
def self.central_registry?(registry)
|
53
82
|
CENTRAL_REGISTRIES.any? do |r|
|
54
83
|
r.include?(registry)
|
55
84
|
end
|
56
85
|
end
|
57
86
|
|
87
|
+
sig { params(dependency_name: String).returns(T.nilable(String)) }
|
58
88
|
def registry_from_rc(dependency_name)
|
59
89
|
explicit_registry_from_rc(dependency_name) || global_registry
|
60
90
|
end
|
61
91
|
|
92
|
+
sig { returns(T::Boolean) }
|
93
|
+
def custom_registry?
|
94
|
+
return false if CENTRAL_REGISTRIES.include?(registry_url)
|
95
|
+
|
96
|
+
!(registry_url || "").match?(/registry\.npmjs\.(org|com)/)
|
97
|
+
end
|
98
|
+
|
62
99
|
private
|
63
100
|
|
101
|
+
sig { returns(T.nilable(Dependabot::Dependency)) }
|
64
102
|
attr_reader :dependency
|
103
|
+
|
104
|
+
sig { returns(T::Array[Dependabot::Credential]) }
|
65
105
|
attr_reader :credentials
|
106
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
66
107
|
attr_reader :npmrc_file
|
108
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
67
109
|
attr_reader :yarnrc_file
|
110
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
68
111
|
attr_reader :yarnrc_yml_file
|
69
112
|
|
113
|
+
sig { params(dependency_name: T.nilable(String)).returns(T.nilable(String)) }
|
70
114
|
def explicit_registry_from_rc(dependency_name)
|
71
|
-
if dependency_name
|
115
|
+
if dependency_name&.start_with?("@") && dependency_name.include?("/")
|
72
116
|
scope = dependency_name.split("/").first
|
73
|
-
scoped_registry(scope) || configured_global_registry
|
117
|
+
scoped_registry(T.must(scope)) || configured_global_registry
|
74
118
|
else
|
75
119
|
configured_global_registry
|
76
120
|
end
|
77
121
|
end
|
78
122
|
|
123
|
+
sig { returns(T.nilable(String)) }
|
79
124
|
def first_registry_with_dependency_details
|
125
|
+
return @first_registry_with_dependency_details if @first_registry_with_dependency_details
|
126
|
+
|
80
127
|
@first_registry_with_dependency_details ||=
|
81
128
|
known_registries.find do |details|
|
82
|
-
url = "#{details['registry']
|
129
|
+
url = "#{details['registry']&.gsub(%r{/+$}, '')}/#{escaped_dependency_name}"
|
83
130
|
url = "https://#{url}" unless url.start_with?("http")
|
84
131
|
response = Dependabot::RegistryClient.get(
|
85
132
|
url: url,
|
@@ -97,6 +144,7 @@ module Dependabot
|
|
97
144
|
@first_registry_with_dependency_details ||= global_registry.sub(%r{/+$}, "").sub(%r{^.*?//}, "")
|
98
145
|
end
|
99
146
|
|
147
|
+
sig { returns(T.nilable(String)) }
|
100
148
|
def registry_url
|
101
149
|
url =
|
102
150
|
if registry.start_with?("http")
|
@@ -104,7 +152,7 @@ module Dependabot
|
|
104
152
|
else
|
105
153
|
protocol =
|
106
154
|
if registry_source_url
|
107
|
-
registry_source_url
|
155
|
+
registry_source_url&.split("://")&.first
|
108
156
|
else
|
109
157
|
"https"
|
110
158
|
end
|
@@ -115,6 +163,7 @@ module Dependabot
|
|
115
163
|
url.gsub(%r{/+$}, "")
|
116
164
|
end
|
117
165
|
|
166
|
+
sig { params(token: T.nilable(String)).returns(T::Hash[String, String]) }
|
118
167
|
def auth_header_for(token)
|
119
168
|
return {} unless token
|
120
169
|
|
@@ -129,36 +178,45 @@ module Dependabot
|
|
129
178
|
end
|
130
179
|
end
|
131
180
|
|
181
|
+
sig { returns(T.nilable(String)) }
|
132
182
|
def auth_token
|
133
183
|
known_registries
|
134
184
|
.find { |cred| cred["registry"] == registry }
|
135
185
|
&.fetch("token", nil)
|
136
186
|
end
|
137
187
|
|
188
|
+
sig { returns(T.nilable(String)) }
|
138
189
|
def locked_registry
|
139
190
|
return unless registry_source_url
|
140
191
|
|
141
192
|
lockfile_registry =
|
142
193
|
registry_source_url
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
194
|
+
&.gsub("https://", "")
|
195
|
+
&.gsub("http://", "")
|
196
|
+
|
197
|
+
if lockfile_registry
|
198
|
+
detailed_registry =
|
199
|
+
known_registries
|
200
|
+
.find { |h| h["registry"]&.include?(lockfile_registry) }
|
201
|
+
&.fetch("registry")
|
202
|
+
end
|
149
203
|
|
150
204
|
detailed_registry || lockfile_registry
|
151
205
|
end
|
152
206
|
|
207
|
+
sig { returns(T.nilable(String)) }
|
153
208
|
def configured_registry
|
154
|
-
configured_registry_url = explicit_registry_from_rc(dependency
|
209
|
+
configured_registry_url = explicit_registry_from_rc(dependency&.name)
|
155
210
|
return unless configured_registry_url
|
156
211
|
|
157
212
|
normalize_configured_registry(configured_registry_url)
|
158
213
|
end
|
159
214
|
|
215
|
+
sig { returns(T::Array[T::Hash[String, T.nilable(String)]]) }
|
160
216
|
def known_registries
|
161
|
-
@known_registries
|
217
|
+
return @known_registries if @known_registries.any?
|
218
|
+
|
219
|
+
@known_registries =
|
162
220
|
begin
|
163
221
|
registries = []
|
164
222
|
registries += credentials
|
@@ -169,13 +227,15 @@ module Dependabot
|
|
169
227
|
|
170
228
|
unique_registries(registries)
|
171
229
|
end
|
230
|
+
@known_registries
|
172
231
|
end
|
173
232
|
|
233
|
+
sig { returns(T::Array[T::Hash[String, T.nilable(String)]]) }
|
174
234
|
def npmrc_registries
|
175
235
|
return [] unless npmrc_file
|
176
236
|
|
177
237
|
registries = []
|
178
|
-
npmrc_file
|
238
|
+
npmrc_file&.content&.scan(NPM_AUTH_TOKEN_REGEX) do
|
179
239
|
next if Regexp.last_match&.[](:registry)&.include?("${")
|
180
240
|
|
181
241
|
registry = T.must(Regexp.last_match)[:registry]
|
@@ -191,12 +251,17 @@ module Dependabot
|
|
191
251
|
registries += npmrc_global_registries
|
192
252
|
end
|
193
253
|
|
254
|
+
sig { returns(T::Array[T::Hash[String, T.nilable(String)]]) }
|
194
255
|
def yarnrc_registries
|
195
256
|
return [] unless yarnrc_file
|
196
257
|
|
197
258
|
yarnrc_global_registries
|
198
259
|
end
|
199
260
|
|
261
|
+
sig do
|
262
|
+
params(registries: T::Array[T::Hash[String, T.nilable(String)]])
|
263
|
+
.returns(T::Array[T::Hash[String, T.nilable(String)]])
|
264
|
+
end
|
200
265
|
def unique_registries(registries)
|
201
266
|
registries.uniq.reject do |registry|
|
202
267
|
next if registry["token"]
|
@@ -208,28 +273,31 @@ module Dependabot
|
|
208
273
|
end
|
209
274
|
end
|
210
275
|
|
276
|
+
sig { returns(String) }
|
211
277
|
def global_registry
|
212
|
-
return @global_registry if
|
278
|
+
return @global_registry if @global_registry
|
213
279
|
|
214
|
-
@global_registry
|
280
|
+
@global_registry = configured_global_registry || GLOBAL_NPM_REGISTRY
|
281
|
+
@global_registry
|
215
282
|
end
|
216
283
|
|
217
284
|
# rubocop:disable Metrics/PerceivedComplexity
|
285
|
+
sig { returns(T.nilable(String)) }
|
218
286
|
def configured_global_registry
|
219
|
-
return @configured_global_registry if
|
287
|
+
return @configured_global_registry if @configured_global_registry
|
220
288
|
|
221
289
|
@configured_global_registry = (npmrc_file && npmrc_global_registries.first&.fetch("url")) ||
|
222
290
|
(yarnrc_file && yarnrc_global_registries.first&.fetch("url"))
|
223
291
|
return @configured_global_registry if @configured_global_registry
|
224
292
|
|
225
293
|
if parsed_yarnrc_yml&.key?("npmRegistryServer")
|
226
|
-
return @configured_global_registry = parsed_yarnrc_yml["npmRegistryServer"]
|
294
|
+
return @configured_global_registry = T.must(parsed_yarnrc_yml)["npmRegistryServer"]
|
227
295
|
end
|
228
296
|
|
229
297
|
replaces_base = credentials.find { |cred| cred["type"] == "npm_registry" && cred.replaces_base? }
|
230
298
|
if replaces_base
|
231
299
|
registry = replaces_base["registry"]
|
232
|
-
registry = "https://#{registry}" unless registry
|
300
|
+
registry = "https://#{registry}" unless registry&.start_with?("http")
|
233
301
|
return @configured_global_registry = registry
|
234
302
|
end
|
235
303
|
|
@@ -237,31 +305,40 @@ module Dependabot
|
|
237
305
|
end
|
238
306
|
# rubocop:enable Metrics/PerceivedComplexity
|
239
307
|
|
308
|
+
sig { returns(T::Array[T::Hash[String, String]]) }
|
240
309
|
def npmrc_global_registries
|
241
310
|
global_rc_registries(npmrc_file, syntax: NPM_GLOBAL_REGISTRY_REGEX)
|
242
311
|
end
|
243
312
|
|
313
|
+
sig { returns(T::Array[T::Hash[String, String]]) }
|
244
314
|
def yarnrc_global_registries
|
245
315
|
global_rc_registries(yarnrc_file, syntax: YARN_GLOBAL_REGISTRY_REGEX)
|
246
316
|
end
|
247
317
|
|
318
|
+
sig { params(scope: String).returns(T.nilable(String)) }
|
248
319
|
def scoped_registry(scope)
|
249
320
|
scoped_rc_registry = scoped_rc_registry(npmrc_file, syntax: NPM_SCOPED_REGISTRY_REGEX, scope: scope) ||
|
250
321
|
scoped_rc_registry(yarnrc_file, syntax: YARN_SCOPED_REGISTRY_REGEX, scope: scope)
|
251
322
|
return scoped_rc_registry if scoped_rc_registry
|
252
323
|
|
253
324
|
if parsed_yarnrc_yml
|
254
|
-
yarn_berry_registry = parsed_yarnrc_yml
|
325
|
+
yarn_berry_registry = parsed_yarnrc_yml&.dig("npmScopes", scope.delete_prefix("@"), "npmRegistryServer")
|
255
326
|
return yarn_berry_registry if yarn_berry_registry
|
256
327
|
end
|
257
328
|
|
258
329
|
nil
|
259
330
|
end
|
260
331
|
|
332
|
+
sig do
|
333
|
+
params(
|
334
|
+
file: T.nilable(Dependabot::DependencyFile),
|
335
|
+
syntax: T.any(String, Regexp)
|
336
|
+
).returns(T::Array[T::Hash[String, String]])
|
337
|
+
end
|
261
338
|
def global_rc_registries(file, syntax:)
|
262
339
|
registries = []
|
263
340
|
|
264
|
-
file
|
341
|
+
file&.content&.scan(syntax) do
|
265
342
|
next if Regexp.last_match&.[](:registry)&.include?("${")
|
266
343
|
|
267
344
|
url = T.must(T.must(Regexp.last_match)[:registry]).strip
|
@@ -277,6 +354,13 @@ module Dependabot
|
|
277
354
|
registries
|
278
355
|
end
|
279
356
|
|
357
|
+
sig do
|
358
|
+
params(
|
359
|
+
file: T.nilable(Dependabot::DependencyFile),
|
360
|
+
syntax: T.any(String, Regexp),
|
361
|
+
scope: String
|
362
|
+
).returns(T.nilable(String))
|
363
|
+
end
|
280
364
|
def scoped_rc_registry(file, syntax:, scope:)
|
281
365
|
file&.content.to_s.scan(syntax) do
|
282
366
|
next if Regexp.last_match&.[](:registry)&.include?("${") || Regexp.last_match&.[](:scope) != scope
|
@@ -288,29 +372,36 @@ module Dependabot
|
|
288
372
|
end
|
289
373
|
|
290
374
|
# npm registries expect slashes to be escaped
|
375
|
+
sig { returns(T.nilable(String)) }
|
291
376
|
def escaped_dependency_name
|
292
|
-
dependency
|
377
|
+
dependency&.name&.gsub("/", "%2F")
|
293
378
|
end
|
294
379
|
|
380
|
+
sig { returns(T.nilable(String)) }
|
295
381
|
def scopeless_name
|
296
|
-
dependency
|
382
|
+
dependency&.name&.split("/")&.last
|
297
383
|
end
|
298
384
|
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
385
|
+
sig { returns(T.nilable(String)) }
|
386
|
+
def registry_source_url # rubocop:disable Metrics/PerceivedComplexity
|
387
|
+
sources = dependency&.requirements
|
388
|
+
&.map { |r| r.fetch(:source) }&.uniq&.compact
|
389
|
+
&.sort_by { |source| self.class.central_registry?(source[:url]) ? 1 : 0 }
|
303
390
|
|
304
|
-
sources
|
391
|
+
sources&.find { |s| s[:type] == "registry" }&.fetch(:url)
|
305
392
|
end
|
306
393
|
|
394
|
+
sig { returns(T.nilable(T::Hash[String, T.untyped])) }
|
307
395
|
def parsed_yarnrc_yml
|
308
|
-
|
309
|
-
return
|
396
|
+
yarnrc_yml_file_content = yarnrc_yml_file&.content
|
397
|
+
return unless yarnrc_yml_file_content
|
398
|
+
return @parsed_yarnrc_yml if @parsed_yarnrc_yml
|
310
399
|
|
311
|
-
@parsed_yarnrc_yml = YAML.safe_load(
|
400
|
+
@parsed_yarnrc_yml = YAML.safe_load(yarnrc_yml_file_content)
|
401
|
+
@parsed_yarnrc_yml
|
312
402
|
end
|
313
403
|
|
404
|
+
sig { params(url: String).returns(String) }
|
314
405
|
def normalize_configured_registry(url)
|
315
406
|
url.sub(%r{/+$}, "")
|
316
407
|
.sub(%r{^.*?//}, "")
|
@@ -110,7 +110,7 @@ module Dependabot
|
|
110
110
|
def yarnrc_specifies_private_reg?
|
111
111
|
return false unless yarnrc_file
|
112
112
|
|
113
|
-
regex =
|
113
|
+
regex = Package::RegistryFinder::YARN_GLOBAL_REGISTRY_REGEX
|
114
114
|
yarnrc_global_registry =
|
115
115
|
yarnrc_file.content
|
116
116
|
.lines.find { |line| line.match?(regex) }
|
@@ -120,7 +120,7 @@ module Dependabot
|
|
120
120
|
|
121
121
|
return false unless yarnrc_global_registry
|
122
122
|
|
123
|
-
|
123
|
+
Package::RegistryFinder::CENTRAL_REGISTRIES.none? do |r|
|
124
124
|
r.include?(T.must(URI(yarnrc_global_registry).host))
|
125
125
|
end
|
126
126
|
end
|