dependabot-npm_and_yarn 0.287.0 → 0.289.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_fetcher.rb +28 -0
- data/lib/dependabot/npm_and_yarn/file_parser.rb +4 -2
- data/lib/dependabot/npm_and_yarn/helpers.rb +131 -17
- data/lib/dependabot/npm_and_yarn/package_manager.rb +248 -33
- data/lib/dependabot/npm_and_yarn/update_checker/subdependency_version_resolver.rb +4 -4
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3b61a2e379cb066af66a91f9cbfd25d89755129946f7093b2e1d5be7f3642133
|
4
|
+
data.tar.gz: 10995493f890b53c62af1c14f7d29a165ee32096b8b84a529f0822984d6f1480
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3fb1619f2f8ba90e8bbe7945c7b1e179abd09004b940c1d2514d9f4290a7679955ad0fcc0db26ebcb4b810c5903e4d66ddb3f3d0b3ca0a265c9636f34f272519
|
7
|
+
data.tar.gz: ae15d52f683156e2df0ddf213c254e0b9cd6e9324da8eda5367dae8d1c59629497739be437e3c76c6597ce9ccbbbd06509196ba9efad51240e3e98a9c99d91f4
|
@@ -107,6 +107,7 @@ module Dependabot
|
|
107
107
|
fetched_yarn_files << yarn_lock if yarn_lock
|
108
108
|
fetched_yarn_files << yarnrc if yarnrc
|
109
109
|
fetched_yarn_files << yarnrc_yml if yarnrc_yml
|
110
|
+
create_yarn_cache
|
110
111
|
fetched_yarn_files
|
111
112
|
end
|
112
113
|
|
@@ -244,6 +245,20 @@ module Dependabot
|
|
244
245
|
return @pnpm_lock if defined?(@pnpm_lock)
|
245
246
|
|
246
247
|
@pnpm_lock ||= T.let(fetch_file_if_present(PNPMPackageManager::LOCKFILE_NAME), T.nilable(DependencyFile))
|
248
|
+
|
249
|
+
return @pnpm_lock if @pnpm_lock || directory == "/"
|
250
|
+
|
251
|
+
# Loop through parent directories looking for a pnpm-lock
|
252
|
+
(1..directory.split("/").count).each do |i|
|
253
|
+
@pnpm_lock = fetch_file_from_host(("../" * i) + PNPMPackageManager::LOCKFILE_NAME)
|
254
|
+
.tap { |f| f.support_file = true }
|
255
|
+
break if @pnpm_lock
|
256
|
+
rescue Dependabot::DependencyFileNotFound
|
257
|
+
# Ignore errors (pnpm_lock.yaml may not be present)
|
258
|
+
nil
|
259
|
+
end
|
260
|
+
|
261
|
+
@pnpm_lock
|
247
262
|
end
|
248
263
|
|
249
264
|
sig { returns(T.nilable(DependencyFile)) }
|
@@ -655,6 +670,19 @@ module Dependabot
|
|
655
670
|
rescue JSON::ParserError
|
656
671
|
raise Dependabot::DependencyFileNotParseable, T.must(lerna_json).path
|
657
672
|
end
|
673
|
+
|
674
|
+
sig { void }
|
675
|
+
def create_yarn_cache
|
676
|
+
if repo_contents_path.nil?
|
677
|
+
Dependabot.logger.info("Repository contents path is nil")
|
678
|
+
elsif Dir.exist?(T.must(repo_contents_path))
|
679
|
+
Dir.chdir(T.must(repo_contents_path)) do
|
680
|
+
FileUtils.mkdir_p(".yarn/cache")
|
681
|
+
end
|
682
|
+
else
|
683
|
+
Dependabot.logger.info("Repository contents path does not exist")
|
684
|
+
end
|
685
|
+
end
|
658
686
|
end
|
659
687
|
end
|
660
688
|
end
|
@@ -11,6 +11,7 @@ require "dependabot/npm_and_yarn/helpers"
|
|
11
11
|
require "dependabot/npm_and_yarn/native_helpers"
|
12
12
|
require "dependabot/npm_and_yarn/version"
|
13
13
|
require "dependabot/npm_and_yarn/requirement"
|
14
|
+
require "dependabot/npm_and_yarn/package_manager"
|
14
15
|
require "dependabot/npm_and_yarn/registry_parser"
|
15
16
|
require "dependabot/git_metadata_fetcher"
|
16
17
|
require "dependabot/git_commit_checker"
|
@@ -83,7 +84,8 @@ module Dependabot
|
|
83
84
|
@ecosystem ||= T.let(
|
84
85
|
Ecosystem.new(
|
85
86
|
name: ECOSYSTEM,
|
86
|
-
package_manager: package_manager_helper.package_manager
|
87
|
+
package_manager: package_manager_helper.package_manager,
|
88
|
+
language: package_manager_helper.language
|
87
89
|
),
|
88
90
|
T.nilable(Ecosystem)
|
89
91
|
)
|
@@ -477,4 +479,4 @@ module Dependabot
|
|
477
479
|
end
|
478
480
|
|
479
481
|
Dependabot::FileParsers
|
480
|
-
.register(
|
482
|
+
.register(Dependabot::NpmAndYarn::ECOSYSTEM, Dependabot::NpmAndYarn::FileParser)
|
@@ -1,9 +1,10 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "dependabot/dependency"
|
5
5
|
require "dependabot/file_parsers"
|
6
6
|
require "dependabot/file_parsers/base"
|
7
|
+
require "dependabot/shared_helpers"
|
7
8
|
require "sorbet-runtime"
|
8
9
|
|
9
10
|
module Dependabot
|
@@ -15,6 +16,7 @@ module Dependabot
|
|
15
16
|
/^.*(?<error>The "yarn-path" option has been set \(in [^)]+\), but the specified location doesn't exist)/
|
16
17
|
|
17
18
|
# NPM Version Constants
|
19
|
+
NPM_V10 = 10
|
18
20
|
NPM_V8 = 8
|
19
21
|
NPM_V6 = 6
|
20
22
|
NPM_DEFAULT_VERSION = NPM_V8
|
@@ -39,6 +41,10 @@ module Dependabot
|
|
39
41
|
# Otherwise, we are going to use old versionining npm 6
|
40
42
|
sig { params(lockfile: T.nilable(DependencyFile)).returns(Integer) }
|
41
43
|
def self.npm_version_numeric(lockfile)
|
44
|
+
if Dependabot::Experiments.enabled?(:enable_corepack_for_npm_and_yarn)
|
45
|
+
return npm_version_numeric_latest(lockfile)
|
46
|
+
end
|
47
|
+
|
42
48
|
fallback_version_npm8 = Dependabot::Experiments.enabled?(:npm_fallback_version_above_v6)
|
43
49
|
|
44
50
|
return npm_version_numeric_npm8_or_higher(lockfile) if fallback_version_npm8
|
@@ -90,6 +96,36 @@ module Dependabot
|
|
90
96
|
NPM_DEFAULT_VERSION # Fallback to default npm version if parsing fails
|
91
97
|
end
|
92
98
|
|
99
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
100
|
+
sig { params(lockfile: T.nilable(DependencyFile)).returns(Integer) }
|
101
|
+
def self.npm_version_numeric_latest(lockfile)
|
102
|
+
lockfile_content = lockfile&.content
|
103
|
+
|
104
|
+
# Return npm 10 as the default if the lockfile is missing or empty
|
105
|
+
return NPM_V10 if lockfile_content.nil? || lockfile_content.strip.empty?
|
106
|
+
|
107
|
+
# Parse the lockfile content to extract the `lockfileVersion`
|
108
|
+
parsed_lockfile = JSON.parse(lockfile_content)
|
109
|
+
lockfile_version = parsed_lockfile["lockfileVersion"]&.to_i
|
110
|
+
|
111
|
+
# Determine the appropriate npm version based on `lockfileVersion`
|
112
|
+
if lockfile_version.nil?
|
113
|
+
NPM_V10 # Use npm 10 if `lockfileVersion` is missing or nil
|
114
|
+
elsif lockfile_version >= 3
|
115
|
+
NPM_V10 # Use npm 10 for lockfileVersion 3 or higher
|
116
|
+
elsif lockfile_version >= 2
|
117
|
+
NPM_V8 # Use npm 8 for lockfileVersion 2
|
118
|
+
elsif lockfile_version >= 1
|
119
|
+
# Use npm 8 if the fallback version flag is enabled, otherwise use npm 6
|
120
|
+
Dependabot::Experiments.enabled?(:npm_fallback_version_above_v6) ? NPM_V8 : NPM_V6
|
121
|
+
else
|
122
|
+
NPM_V10 # Default to npm 10 for unexpected or unsupported versions
|
123
|
+
end
|
124
|
+
rescue JSON::ParserError
|
125
|
+
NPM_V8 # Fallback to npm 8 if the lockfile content cannot be parsed
|
126
|
+
end
|
127
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
128
|
+
|
93
129
|
sig { params(yarn_lock: T.nilable(DependencyFile)).returns(Integer) }
|
94
130
|
def self.yarn_version_numeric(yarn_lock)
|
95
131
|
lockfile_content = yarn_lock&.content
|
@@ -110,9 +146,14 @@ module Dependabot
|
|
110
146
|
def self.pnpm_version_numeric(pnpm_lock)
|
111
147
|
lockfile_content = pnpm_lock&.content
|
112
148
|
|
113
|
-
return PNPM_DEFAULT_VERSION if lockfile_content
|
149
|
+
return PNPM_DEFAULT_VERSION if !lockfile_content || lockfile_content.strip.empty?
|
150
|
+
|
151
|
+
pnpm_lockfile_version_str = pnpm_lockfile_version(pnpm_lock)
|
152
|
+
|
153
|
+
return PNPM_FALLBACK_VERSION unless pnpm_lockfile_version_str
|
154
|
+
|
155
|
+
pnpm_lockfile_version = pnpm_lockfile_version_str.to_f
|
114
156
|
|
115
|
-
pnpm_lockfile_version = pnpm_lockfile_version(pnpm_lock).to_f
|
116
157
|
return PNPM_V9 if pnpm_lockfile_version >= 9.0
|
117
158
|
return PNPM_V8 if pnpm_lockfile_version >= 6.0
|
118
159
|
return PNPM_V7 if pnpm_lockfile_version >= 5.4
|
@@ -120,6 +161,7 @@ module Dependabot
|
|
120
161
|
PNPM_FALLBACK_VERSION
|
121
162
|
end
|
122
163
|
|
164
|
+
sig { params(key: String, default_value: String).returns(T.untyped) }
|
123
165
|
def self.fetch_yarnrc_yml_value(key, default_value)
|
124
166
|
if File.exist?(".yarnrc.yml") && (yarnrc = YAML.load_file(".yarnrc.yml"))
|
125
167
|
yarnrc.fetch(key, default_value)
|
@@ -132,6 +174,10 @@ module Dependabot
|
|
132
174
|
def self.npm8?(package_lock)
|
133
175
|
return true unless package_lock&.content
|
134
176
|
|
177
|
+
if Dependabot::Experiments.enabled?(:enable_corepack_for_npm_and_yarn)
|
178
|
+
return npm_version_numeric_latest(package_lock) >= NPM_V8
|
179
|
+
end
|
180
|
+
|
135
181
|
npm_version_numeric(package_lock) == NPM_V8
|
136
182
|
end
|
137
183
|
|
@@ -252,9 +298,12 @@ module Dependabot
|
|
252
298
|
# set to false. Yarn commands should _not_ be ran outside of this helper
|
253
299
|
# to ensure that postinstall scripts are never executed, as they could
|
254
300
|
# contain malicious code.
|
301
|
+
sig { params(commands: T::Array[String]).void }
|
255
302
|
def self.run_yarn_commands(*commands)
|
256
303
|
setup_yarn_berry
|
257
|
-
commands.each
|
304
|
+
commands.each do |cmd, fingerprint|
|
305
|
+
run_single_yarn_command(cmd, fingerprint: fingerprint) if cmd
|
306
|
+
end
|
258
307
|
end
|
259
308
|
|
260
309
|
# Run single npm command returning stdout/stderr.
|
@@ -267,10 +316,44 @@ module Dependabot
|
|
267
316
|
if Dependabot::Experiments.enabled?(:enable_corepack_for_npm_and_yarn)
|
268
317
|
package_manager_run_command(NpmPackageManager::NAME, command, fingerprint: fingerprint)
|
269
318
|
else
|
270
|
-
SharedHelpers.run_shell_command(
|
319
|
+
Dependabot::SharedHelpers.run_shell_command(
|
320
|
+
"corepack npm #{command}",
|
321
|
+
fingerprint: "corepack npm #{fingerprint}"
|
322
|
+
)
|
271
323
|
end
|
272
324
|
end
|
273
325
|
|
326
|
+
sig { returns(T.nilable(String)) }
|
327
|
+
def self.node_version
|
328
|
+
version = run_node_command("-v", fingerprint: "-v").strip
|
329
|
+
|
330
|
+
# Validate the output format (e.g., "v20.18.1" or "20.18.1")
|
331
|
+
if version.match?(/^v?\d+(\.\d+){2}$/)
|
332
|
+
version.strip.delete_prefix("v") # Remove the "v" prefix if present
|
333
|
+
end
|
334
|
+
rescue StandardError => e
|
335
|
+
puts "Error retrieving Node.js version: #{e.message}"
|
336
|
+
nil
|
337
|
+
end
|
338
|
+
|
339
|
+
sig { params(command: String, fingerprint: T.nilable(String)).returns(String) }
|
340
|
+
def self.run_node_command(command, fingerprint: nil)
|
341
|
+
full_command = "node #{command}"
|
342
|
+
|
343
|
+
Dependabot.logger.info("Running node command: #{full_command}")
|
344
|
+
|
345
|
+
result = Dependabot::SharedHelpers.run_shell_command(
|
346
|
+
full_command,
|
347
|
+
fingerprint: "node #{fingerprint || command}"
|
348
|
+
)
|
349
|
+
|
350
|
+
Dependabot.logger.info("Command executed successfully: #{full_command}")
|
351
|
+
result
|
352
|
+
rescue StandardError => e
|
353
|
+
Dependabot.logger.error("Error running node command: #{full_command}, Error: #{e.message}")
|
354
|
+
raise
|
355
|
+
end
|
356
|
+
|
274
357
|
# Setup yarn and run a single yarn command returning stdout/stderr
|
275
358
|
sig { params(command: String, fingerprint: T.nilable(String)).returns(String) }
|
276
359
|
def self.run_yarn_command(command, fingerprint: nil)
|
@@ -284,7 +367,10 @@ module Dependabot
|
|
284
367
|
if Dependabot::Experiments.enabled?(:enable_corepack_for_npm_and_yarn)
|
285
368
|
package_manager_run_command(PNPMPackageManager::NAME, command, fingerprint: fingerprint)
|
286
369
|
else
|
287
|
-
SharedHelpers.run_shell_command(
|
370
|
+
Dependabot::SharedHelpers.run_shell_command(
|
371
|
+
"pnpm #{command}",
|
372
|
+
fingerprint: "pnpm #{fingerprint || command}"
|
373
|
+
)
|
288
374
|
end
|
289
375
|
end
|
290
376
|
|
@@ -294,13 +380,16 @@ module Dependabot
|
|
294
380
|
if Dependabot::Experiments.enabled?(:enable_corepack_for_npm_and_yarn)
|
295
381
|
package_manager_run_command(YarnPackageManager::NAME, command, fingerprint: fingerprint)
|
296
382
|
else
|
297
|
-
SharedHelpers.run_shell_command(
|
383
|
+
Dependabot::SharedHelpers.run_shell_command(
|
384
|
+
"yarn #{command}",
|
385
|
+
fingerprint: "yarn #{fingerprint || command}"
|
386
|
+
)
|
298
387
|
end
|
299
388
|
end
|
300
389
|
|
301
390
|
# Install the package manager for specified version by using corepack
|
302
391
|
# and prepare it for use by using corepack
|
303
|
-
sig { params(name: String, version: String).
|
392
|
+
sig { params(name: String, version: String).returns(String) }
|
304
393
|
def self.install(name, version)
|
305
394
|
Dependabot.logger.info("Installing \"#{name}@#{version}\"")
|
306
395
|
|
@@ -309,30 +398,40 @@ module Dependabot
|
|
309
398
|
installed_version = package_manager_version(name)
|
310
399
|
|
311
400
|
Dependabot.logger.info("Installed version of #{name}: #{installed_version}")
|
401
|
+
|
402
|
+
installed_version
|
312
403
|
end
|
313
404
|
|
314
405
|
# Install the package manager for specified version by using corepack
|
315
406
|
sig { params(name: String, version: String).void }
|
316
407
|
def self.package_manager_install(name, version)
|
317
|
-
SharedHelpers.run_shell_command(
|
408
|
+
Dependabot::SharedHelpers.run_shell_command(
|
318
409
|
"corepack install #{name}@#{version} --global --cache-only",
|
319
410
|
fingerprint: "corepack install <name>@<version> --global --cache-only"
|
320
|
-
)
|
411
|
+
).strip
|
321
412
|
end
|
322
413
|
|
323
414
|
# Prepare the package manager for use by using corepack
|
324
415
|
sig { params(name: String, version: String).void }
|
325
416
|
def self.package_manager_activate(name, version)
|
326
|
-
SharedHelpers.run_shell_command(
|
417
|
+
Dependabot::SharedHelpers.run_shell_command(
|
327
418
|
"corepack prepare #{name}@#{version} --activate",
|
328
419
|
fingerprint: "corepack prepare --activate"
|
329
|
-
)
|
420
|
+
).strip
|
330
421
|
end
|
331
422
|
|
332
423
|
# Get the version of the package manager by using corepack
|
333
424
|
sig { params(name: String).returns(String) }
|
334
425
|
def self.package_manager_version(name)
|
335
|
-
|
426
|
+
Dependabot.logger.info("Fetching version for package manager: #{name}")
|
427
|
+
|
428
|
+
version = package_manager_run_command(name, "-v").strip
|
429
|
+
|
430
|
+
Dependabot.logger.info("Version for #{name}: #{version}")
|
431
|
+
version
|
432
|
+
rescue StandardError => e
|
433
|
+
Dependabot.logger.error("Error fetching version for package manager #{name}: #{e.message}")
|
434
|
+
raise
|
336
435
|
end
|
337
436
|
|
338
437
|
# Run single command on package manager returning stdout/stderr
|
@@ -344,15 +443,30 @@ module Dependabot
|
|
344
443
|
).returns(String)
|
345
444
|
end
|
346
445
|
def self.package_manager_run_command(name, command, fingerprint: nil)
|
347
|
-
|
348
|
-
|
446
|
+
full_command = "corepack #{name} #{command}"
|
447
|
+
|
448
|
+
Dependabot.logger.info("Running package manager command: #{full_command}")
|
449
|
+
|
450
|
+
result = Dependabot::SharedHelpers.run_shell_command(
|
451
|
+
full_command,
|
349
452
|
fingerprint: "corepack #{name} #{fingerprint || command}"
|
350
|
-
)
|
453
|
+
).strip
|
454
|
+
|
455
|
+
Dependabot.logger.info("Command executed successfully: #{full_command}")
|
456
|
+
result
|
457
|
+
rescue StandardError => e
|
458
|
+
Dependabot.logger.error("Error running package manager command: #{full_command}, Error: #{e.message}")
|
459
|
+
raise
|
351
460
|
end
|
461
|
+
|
352
462
|
private_class_method :run_single_yarn_command
|
353
463
|
|
464
|
+
sig { params(pnpm_lock: DependencyFile).returns(T.nilable(String)) }
|
354
465
|
def self.pnpm_lockfile_version(pnpm_lock)
|
355
|
-
pnpm_lock.content.match(/^lockfileVersion: ['"]?(?<version>[\d.]+)/)
|
466
|
+
match = T.must(pnpm_lock.content).match(/^lockfileVersion: ['"]?(?<version>[\d.]+)/)
|
467
|
+
return match[:version] if match
|
468
|
+
|
469
|
+
nil
|
356
470
|
end
|
357
471
|
|
358
472
|
sig { params(dependency_set: Dependabot::FileParsers::Base::DependencySet).returns(T::Array[Dependency]) }
|
@@ -1,8 +1,9 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "dependabot/shared_helpers"
|
5
5
|
require "dependabot/ecosystem"
|
6
|
+
require "dependabot/npm_and_yarn/requirement"
|
6
7
|
require "dependabot/npm_and_yarn/version_selector"
|
7
8
|
|
8
9
|
module Dependabot
|
@@ -10,6 +11,37 @@ module Dependabot
|
|
10
11
|
ECOSYSTEM = "npm_and_yarn"
|
11
12
|
MANIFEST_FILENAME = "package.json"
|
12
13
|
LERNA_JSON_FILENAME = "lerna.json"
|
14
|
+
PACKAGE_MANAGER_VERSION_REGEX = /
|
15
|
+
^ # Start of string
|
16
|
+
(?<major>\d+) # Major version (required, numeric)
|
17
|
+
\. # Separator between major and minor versions
|
18
|
+
(?<minor>\d+) # Minor version (required, numeric)
|
19
|
+
\. # Separator between minor and patch versions
|
20
|
+
(?<patch>\d+) # Patch version (required, numeric)
|
21
|
+
( # Start pre-release section
|
22
|
+
-(?<pre_release>[a-zA-Z0-9.]+) # Pre-release label (optional, alphanumeric or dot-separated)
|
23
|
+
)?
|
24
|
+
( # Start build metadata section
|
25
|
+
\+(?<build>[a-zA-Z0-9.]+) # Build metadata (optional, alphanumeric or dot-separated)
|
26
|
+
)?
|
27
|
+
$ # End of string
|
28
|
+
/x # Extended mode for readability
|
29
|
+
|
30
|
+
VALID_REQUIREMENT_CONSTRAINT = /
|
31
|
+
^ # Start of string
|
32
|
+
(?<operator>=|>|>=|<|<=|~>|\\^) # Allowed operators
|
33
|
+
\s* # Optional whitespace
|
34
|
+
(?<major>\d+) # Major version (required)
|
35
|
+
(\.(?<minor>\d+))? # Minor version (optional)
|
36
|
+
(\.(?<patch>\d+))? # Patch version (optional)
|
37
|
+
( # Start pre-release section
|
38
|
+
-(?<pre_release>[a-zA-Z0-9.]+) # Pre-release label (optional)
|
39
|
+
)?
|
40
|
+
( # Start build metadata section
|
41
|
+
\+(?<build>[a-zA-Z0-9.]+) # Build metadata (optional)
|
42
|
+
)?
|
43
|
+
$ # End of string
|
44
|
+
/x # Extended mode for readability
|
13
45
|
|
14
46
|
MANIFEST_PACKAGE_MANAGER_KEY = "packageManager"
|
15
47
|
MANIFEST_ENGINES_KEY = "engines"
|
@@ -25,24 +57,32 @@ module Dependabot
|
|
25
57
|
NPM_V7 = "7"
|
26
58
|
NPM_V8 = "8"
|
27
59
|
NPM_V9 = "9"
|
60
|
+
NPM_V10 = "10"
|
28
61
|
|
29
62
|
# Keep versions in ascending order
|
30
63
|
SUPPORTED_VERSIONS = T.let([
|
31
64
|
Version.new(NPM_V6),
|
32
65
|
Version.new(NPM_V7),
|
33
66
|
Version.new(NPM_V8),
|
34
|
-
Version.new(NPM_V9)
|
67
|
+
Version.new(NPM_V9),
|
68
|
+
Version.new(NPM_V10)
|
35
69
|
].freeze, T::Array[Dependabot::Version])
|
36
70
|
|
37
71
|
DEPRECATED_VERSIONS = T.let([].freeze, T::Array[Dependabot::Version])
|
38
72
|
|
39
|
-
sig
|
40
|
-
|
73
|
+
sig do
|
74
|
+
params(
|
75
|
+
raw_version: String,
|
76
|
+
requirement: T.nilable(Dependabot::NpmAndYarn::Requirement)
|
77
|
+
).void
|
78
|
+
end
|
79
|
+
def initialize(raw_version, requirement: nil)
|
41
80
|
super(
|
42
81
|
NAME,
|
43
82
|
Version.new(raw_version),
|
44
83
|
DEPRECATED_VERSIONS,
|
45
|
-
SUPPORTED_VERSIONS
|
84
|
+
SUPPORTED_VERSIONS,
|
85
|
+
requirement
|
46
86
|
)
|
47
87
|
end
|
48
88
|
|
@@ -76,13 +116,19 @@ module Dependabot
|
|
76
116
|
|
77
117
|
DEPRECATED_VERSIONS = T.let([].freeze, T::Array[Dependabot::Version])
|
78
118
|
|
79
|
-
sig
|
80
|
-
|
119
|
+
sig do
|
120
|
+
params(
|
121
|
+
raw_version: String,
|
122
|
+
requirement: T.nilable(Requirement)
|
123
|
+
).void
|
124
|
+
end
|
125
|
+
def initialize(raw_version, requirement: nil)
|
81
126
|
super(
|
82
127
|
NAME,
|
83
128
|
Version.new(raw_version),
|
84
129
|
DEPRECATED_VERSIONS,
|
85
|
-
SUPPORTED_VERSIONS
|
130
|
+
SUPPORTED_VERSIONS,
|
131
|
+
requirement
|
86
132
|
)
|
87
133
|
end
|
88
134
|
|
@@ -115,13 +161,19 @@ module Dependabot
|
|
115
161
|
|
116
162
|
DEPRECATED_VERSIONS = T.let([].freeze, T::Array[Dependabot::Version])
|
117
163
|
|
118
|
-
sig
|
119
|
-
|
164
|
+
sig do
|
165
|
+
params(
|
166
|
+
raw_version: String,
|
167
|
+
requirement: T.nilable(Requirement)
|
168
|
+
).void
|
169
|
+
end
|
170
|
+
def initialize(raw_version, requirement: nil)
|
120
171
|
super(
|
121
172
|
NAME,
|
122
173
|
Version.new(raw_version),
|
123
174
|
DEPRECATED_VERSIONS,
|
124
|
-
SUPPORTED_VERSIONS
|
175
|
+
SUPPORTED_VERSIONS,
|
176
|
+
requirement
|
125
177
|
)
|
126
178
|
end
|
127
179
|
|
@@ -138,11 +190,20 @@ module Dependabot
|
|
138
190
|
|
139
191
|
DEFAULT_PACKAGE_MANAGER = NpmPackageManager::NAME
|
140
192
|
|
141
|
-
|
193
|
+
# Define a type alias for the expected class interface
|
194
|
+
NpmAndYarnPackageManagerClassType = T.type_alias do
|
195
|
+
T.any(
|
196
|
+
T.class_of(Dependabot::NpmAndYarn::NpmPackageManager),
|
197
|
+
T.class_of(Dependabot::NpmAndYarn::YarnPackageManager),
|
198
|
+
T.class_of(Dependabot::NpmAndYarn::PNPMPackageManager)
|
199
|
+
)
|
200
|
+
end
|
201
|
+
|
202
|
+
PACKAGE_MANAGER_CLASSES = T.let({
|
142
203
|
NpmPackageManager::NAME => NpmPackageManager,
|
143
204
|
YarnPackageManager::NAME => YarnPackageManager,
|
144
205
|
PNPMPackageManager::NAME => PNPMPackageManager
|
145
|
-
}.freeze
|
206
|
+
}.freeze, T::Hash[String, NpmAndYarnPackageManagerClassType])
|
146
207
|
|
147
208
|
class PackageManagerDetector
|
148
209
|
extend T::Sig
|
@@ -151,21 +212,34 @@ module Dependabot
|
|
151
212
|
sig do
|
152
213
|
params(
|
153
214
|
lockfiles: T::Hash[Symbol, T.nilable(Dependabot::DependencyFile)],
|
154
|
-
package_json: T::Hash[String, T.untyped]
|
215
|
+
package_json: T.nilable(T::Hash[String, T.untyped])
|
155
216
|
).void
|
156
217
|
end
|
157
218
|
def initialize(lockfiles, package_json)
|
158
219
|
@lockfiles = lockfiles
|
159
220
|
@package_json = package_json
|
160
|
-
@manifest_package_manager = package_json
|
161
|
-
@engines = package_json
|
221
|
+
@manifest_package_manager = T.let(package_json&.fetch(MANIFEST_PACKAGE_MANAGER_KEY, nil), T.nilable(String))
|
222
|
+
@engines = T.let(package_json&.fetch(MANIFEST_ENGINES_KEY, {}), T::Hash[String, T.untyped])
|
162
223
|
end
|
163
224
|
|
164
225
|
# Returns npm, yarn, or pnpm based on the lockfiles, package.json, and engines
|
165
226
|
# Defaults to npm if no package manager is detected
|
166
227
|
sig { returns(String) }
|
167
228
|
def detect_package_manager
|
168
|
-
|
229
|
+
package_manager = name_from_lockfiles ||
|
230
|
+
name_from_package_manager_attr ||
|
231
|
+
name_from_engines
|
232
|
+
|
233
|
+
if package_manager
|
234
|
+
Dependabot.logger.info("Detected package manager: #{package_manager}")
|
235
|
+
else
|
236
|
+
package_manager = DEFAULT_PACKAGE_MANAGER
|
237
|
+
Dependabot.logger.info("Default package manager used: #{package_manager}")
|
238
|
+
end
|
239
|
+
package_manager
|
240
|
+
rescue StandardError => e
|
241
|
+
Dependabot.logger.error("Error detecting package manager: #{e.message}")
|
242
|
+
DEFAULT_PACKAGE_MANAGER
|
169
243
|
end
|
170
244
|
|
171
245
|
private
|
@@ -195,22 +269,62 @@ module Dependabot
|
|
195
269
|
end
|
196
270
|
end
|
197
271
|
|
272
|
+
class Language < Ecosystem::VersionManager
|
273
|
+
extend T::Sig
|
274
|
+
NAME = "node"
|
275
|
+
|
276
|
+
SUPPORTED_VERSIONS = T.let([].freeze, T::Array[Dependabot::Version])
|
277
|
+
|
278
|
+
DEPRECATED_VERSIONS = T.let([].freeze, T::Array[Dependabot::Version])
|
279
|
+
|
280
|
+
sig do
|
281
|
+
params(
|
282
|
+
raw_version: T.nilable(String),
|
283
|
+
requirement: T.nilable(Requirement)
|
284
|
+
).void
|
285
|
+
end
|
286
|
+
def initialize(raw_version, requirement: nil)
|
287
|
+
super(
|
288
|
+
NAME,
|
289
|
+
Version.new(raw_version),
|
290
|
+
DEPRECATED_VERSIONS,
|
291
|
+
SUPPORTED_VERSIONS,
|
292
|
+
requirement
|
293
|
+
)
|
294
|
+
end
|
295
|
+
|
296
|
+
sig { override.returns(T::Boolean) }
|
297
|
+
def deprecated?
|
298
|
+
false
|
299
|
+
end
|
300
|
+
|
301
|
+
sig { override.returns(T::Boolean) }
|
302
|
+
def unsupported?
|
303
|
+
false
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
198
307
|
class PackageManagerHelper
|
199
308
|
extend T::Sig
|
200
309
|
extend T::Helpers
|
201
310
|
|
202
311
|
sig do
|
203
312
|
params(
|
204
|
-
package_json: T::Hash[String, T.untyped],
|
313
|
+
package_json: T.nilable(T::Hash[String, T.untyped]),
|
205
314
|
lockfiles: T::Hash[Symbol, T.nilable(Dependabot::DependencyFile)]
|
206
315
|
).void
|
207
316
|
end
|
208
317
|
def initialize(package_json, lockfiles:)
|
209
318
|
@package_json = package_json
|
210
319
|
@lockfiles = lockfiles
|
211
|
-
@
|
212
|
-
@
|
213
|
-
@
|
320
|
+
@package_manager_detector = T.let(PackageManagerDetector.new(lockfiles, package_json), PackageManagerDetector)
|
321
|
+
@manifest_package_manager = T.let(package_json&.fetch(MANIFEST_PACKAGE_MANAGER_KEY, nil), T.nilable(String))
|
322
|
+
@engines = T.let(package_json&.fetch(MANIFEST_ENGINES_KEY, nil), T.nilable(T::Hash[String, T.untyped]))
|
323
|
+
|
324
|
+
@installed_versions = T.let({}, T::Hash[String, String])
|
325
|
+
|
326
|
+
@language = T.let(nil, T.nilable(Ecosystem::VersionManager))
|
327
|
+
@language_requirement = T.let(nil, T.nilable(Requirement))
|
214
328
|
end
|
215
329
|
|
216
330
|
sig { returns(Ecosystem::VersionManager) }
|
@@ -220,8 +334,54 @@ module Dependabot
|
|
220
334
|
)
|
221
335
|
end
|
222
336
|
|
337
|
+
sig { returns(Ecosystem::VersionManager) }
|
338
|
+
def language
|
339
|
+
@language ||= Language.new(
|
340
|
+
Helpers.node_version,
|
341
|
+
requirement: language_requirement
|
342
|
+
)
|
343
|
+
end
|
344
|
+
|
345
|
+
sig { returns(T.nilable(Requirement)) }
|
346
|
+
def language_requirement
|
347
|
+
@language_requirement ||= find_engine_constraints_as_requirement(Language::NAME)
|
348
|
+
end
|
349
|
+
|
350
|
+
sig { params(name: String).returns(T.nilable(Requirement)) }
|
351
|
+
def find_engine_constraints_as_requirement(name)
|
352
|
+
Dependabot.logger.info("Processing engine constraints for #{name}")
|
353
|
+
|
354
|
+
return nil unless @engines.is_a?(Hash) && @engines[name]
|
355
|
+
|
356
|
+
raw_constraint = @engines[name].to_s.strip
|
357
|
+
return nil if raw_constraint.empty?
|
358
|
+
|
359
|
+
raw_constraints = raw_constraint.split
|
360
|
+
constraints = raw_constraints.map do |constraint|
|
361
|
+
case constraint
|
362
|
+
when /^\d+$/
|
363
|
+
">=#{constraint}.0.0 <#{constraint.to_i + 1}.0.0"
|
364
|
+
when /^\d+\.\d+$/
|
365
|
+
">=#{constraint} <#{constraint.split('.').first.to_i + 1}.0.0"
|
366
|
+
when /^\d+\.\d+\.\d+$/
|
367
|
+
"=#{constraint}"
|
368
|
+
else
|
369
|
+
Dependabot.logger.warn("Unrecognized constraint format for #{name}: #{constraint}")
|
370
|
+
constraint
|
371
|
+
end
|
372
|
+
end
|
373
|
+
|
374
|
+
Dependabot.logger.info("Parsed constraints for #{name}: #{constraints.join(', ')}")
|
375
|
+
Requirement.new(constraints)
|
376
|
+
rescue StandardError => e
|
377
|
+
Dependabot.logger.error("Error processing constraints for #{name}: #{e.message}")
|
378
|
+
nil
|
379
|
+
end
|
380
|
+
|
223
381
|
# rubocop:disable Metrics/CyclomaticComplexity
|
224
382
|
# rubocop:disable Metrics/PerceivedComplexity
|
383
|
+
# rubocop:disable Metrics/AbcSize
|
384
|
+
sig { params(name: String).returns(T.nilable(T.any(Integer, String))) }
|
225
385
|
def setup(name)
|
226
386
|
# we prioritize version mentioned in "packageManager" instead of "engines"
|
227
387
|
# i.e. if { engines : "pnpm" : "6" } and { packageManager: "pnpm@6.0.2" },
|
@@ -257,13 +417,13 @@ module Dependabot
|
|
257
417
|
|
258
418
|
if version
|
259
419
|
raise_if_unsupported!(name, version.to_s)
|
260
|
-
install(name, version)
|
420
|
+
install(name, version.to_s)
|
261
421
|
end
|
262
422
|
else
|
263
423
|
version ||= requested_version(name)
|
264
424
|
|
265
425
|
if version
|
266
|
-
raise_if_unsupported!(name, version)
|
426
|
+
raise_if_unsupported!(name, version.to_s)
|
267
427
|
|
268
428
|
install(name, version)
|
269
429
|
else
|
@@ -272,30 +432,74 @@ module Dependabot
|
|
272
432
|
if version
|
273
433
|
raise_if_unsupported!(name, version.to_s)
|
274
434
|
|
275
|
-
install(name, version) if name == PNPMPackageManager::NAME
|
435
|
+
install(name, version.to_s) if name == PNPMPackageManager::NAME
|
276
436
|
end
|
277
437
|
end
|
278
438
|
end
|
279
439
|
version
|
280
440
|
end
|
281
|
-
# rubocop:enable Metrics/CyclomaticComplexity
|
282
|
-
# rubocop:enable Metrics/PerceivedComplexity
|
283
|
-
|
284
|
-
private
|
285
441
|
|
286
442
|
sig { params(name: T.nilable(String)).returns(Ecosystem::VersionManager) }
|
287
443
|
def package_manager_by_name(name)
|
288
|
-
|
444
|
+
Dependabot.logger.info("Resolving package manager for: #{name || 'default'}")
|
289
445
|
|
290
|
-
|
446
|
+
name = ensure_valid_package_manager(name)
|
447
|
+
package_manager_class = T.must(PACKAGE_MANAGER_CLASSES[name])
|
291
448
|
|
292
|
-
|
449
|
+
installed_version = installed_version(name)
|
450
|
+
Dependabot.logger.info("Installed version for #{name}: #{installed_version}")
|
293
451
|
|
294
|
-
|
452
|
+
package_manager_requirement = find_engine_constraints_as_requirement(name)
|
453
|
+
if package_manager_requirement
|
454
|
+
Dependabot.logger.info("Version requirement for #{name}: #{package_manager_requirement}")
|
455
|
+
else
|
456
|
+
Dependabot.logger.info("No version requirement found for #{name}")
|
457
|
+
end
|
295
458
|
|
296
|
-
package_manager_class.new(
|
459
|
+
package_manager_instance = package_manager_class.new(
|
460
|
+
installed_version,
|
461
|
+
requirement: package_manager_requirement
|
462
|
+
)
|
463
|
+
|
464
|
+
Dependabot.logger.info("Package manager resolved for #{name}: #{package_manager_instance}")
|
465
|
+
package_manager_instance
|
466
|
+
rescue StandardError => e
|
467
|
+
Dependabot.logger.error("Error resolving package manager for #{name || 'default'}: #{e.message}")
|
468
|
+
raise
|
297
469
|
end
|
298
470
|
|
471
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
472
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
473
|
+
# rubocop:enable Metrics/AbcSize
|
474
|
+
# Retrieve the installed version of the package manager by executing
|
475
|
+
# the "corepack <name> -v" command and using the output.
|
476
|
+
# If the output does not match the expected version format (PACKAGE_MANAGER_VERSION_REGEX),
|
477
|
+
# fall back to the version inferred from the dependency files.
|
478
|
+
sig { params(name: String).returns(String) }
|
479
|
+
def installed_version(name)
|
480
|
+
# Return the memoized version if it has already been computed
|
481
|
+
return T.must(@installed_versions[name]) if @installed_versions.key?(name)
|
482
|
+
|
483
|
+
# Attempt to get the installed version through the package manager version command
|
484
|
+
@installed_versions[name] = Helpers.package_manager_version(name)
|
485
|
+
|
486
|
+
# If we can't get the installed version, we need to install the package manager and get the version
|
487
|
+
unless @installed_versions[name]&.match?(PACKAGE_MANAGER_VERSION_REGEX)
|
488
|
+
setup(name)
|
489
|
+
@installed_versions[name] = Helpers.package_manager_version(name)
|
490
|
+
end
|
491
|
+
|
492
|
+
# If we can't get the installed version or the version is invalid, we need to get inferred version
|
493
|
+
unless @installed_versions[name]&.match?(PACKAGE_MANAGER_VERSION_REGEX)
|
494
|
+
@installed_versions[name] = Helpers.public_send(:"#{name}_version_numeric", @lockfiles[name.to_sym]).to_s
|
495
|
+
end
|
496
|
+
|
497
|
+
T.must(@installed_versions[name])
|
498
|
+
end
|
499
|
+
|
500
|
+
private
|
501
|
+
|
502
|
+
sig { params(name: String, version: String).void }
|
299
503
|
def raise_if_unsupported!(name, version)
|
300
504
|
return unless name == PNPMPackageManager::NAME
|
301
505
|
return unless Version.new(version) < Version.new("7")
|
@@ -303,6 +507,7 @@ module Dependabot
|
|
303
507
|
raise ToolVersionNotSupported.new(PNPMPackageManager::NAME.upcase, version, "7.*, 8.*")
|
304
508
|
end
|
305
509
|
|
510
|
+
sig { params(name: String, version: T.nilable(String)).void }
|
306
511
|
def install(name, version)
|
307
512
|
if Dependabot::Experiments.enabled?(:enable_corepack_for_npm_and_yarn)
|
308
513
|
return Helpers.install(name, version.to_s)
|
@@ -316,6 +521,13 @@ module Dependabot
|
|
316
521
|
)
|
317
522
|
end
|
318
523
|
|
524
|
+
sig { params(name: T.nilable(String)).returns(String) }
|
525
|
+
def ensure_valid_package_manager(name)
|
526
|
+
name = DEFAULT_PACKAGE_MANAGER if name.nil? || PACKAGE_MANAGER_CLASSES[name].nil?
|
527
|
+
name
|
528
|
+
end
|
529
|
+
|
530
|
+
sig { params(name: String).returns(T.nilable(String)) }
|
319
531
|
def requested_version(name)
|
320
532
|
return unless @manifest_package_manager
|
321
533
|
|
@@ -326,6 +538,7 @@ module Dependabot
|
|
326
538
|
match["version"]
|
327
539
|
end
|
328
540
|
|
541
|
+
sig { params(name: String).returns(T.nilable(T.any(Integer, String))) }
|
329
542
|
def guessed_version(name)
|
330
543
|
lockfile = @lockfiles[name.to_sym]
|
331
544
|
return unless lockfile
|
@@ -339,6 +552,8 @@ module Dependabot
|
|
339
552
|
|
340
553
|
sig { params(name: T.untyped).returns(T.nilable(String)) }
|
341
554
|
def check_engine_version(name)
|
555
|
+
return if @package_json.nil?
|
556
|
+
|
342
557
|
version_selector = VersionSelector.new
|
343
558
|
engine_versions = version_selector.setup(@package_json, name)
|
344
559
|
|
@@ -70,8 +70,8 @@ module Dependabot
|
|
70
70
|
run_yarn_updater(path, lockfile_name)
|
71
71
|
elsif lockfile.name.end_with?("pnpm-lock.yaml")
|
72
72
|
run_pnpm_updater(path, lockfile_name)
|
73
|
-
elsif Helpers.npm8?(lockfile)
|
74
|
-
|
73
|
+
elsif !Helpers.npm8?(lockfile)
|
74
|
+
run_npm6_updater(path, lockfile_name)
|
75
75
|
else
|
76
76
|
run_npm_updater(path, lockfile_name)
|
77
77
|
end
|
@@ -143,7 +143,7 @@ module Dependabot
|
|
143
143
|
end
|
144
144
|
end
|
145
145
|
|
146
|
-
def
|
146
|
+
def run_npm_updater(path, lockfile_name)
|
147
147
|
SharedHelpers.with_git_configured(credentials: credentials) do
|
148
148
|
Dir.chdir(path) do
|
149
149
|
NativeHelpers.run_npm8_subdependency_update_command([dependency.name])
|
@@ -153,7 +153,7 @@ module Dependabot
|
|
153
153
|
end
|
154
154
|
end
|
155
155
|
|
156
|
-
def
|
156
|
+
def run_npm6_updater(path, lockfile_name)
|
157
157
|
SharedHelpers.with_git_configured(credentials: credentials) do
|
158
158
|
Dir.chdir(path) do
|
159
159
|
SharedHelpers.run_helper_subprocess(
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dependabot-npm_and_yarn
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.289.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dependabot
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-12-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: dependabot-common
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - '='
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 0.
|
19
|
+
version: 0.289.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - '='
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 0.
|
26
|
+
version: 0.289.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: debug
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -346,7 +346,7 @@ licenses:
|
|
346
346
|
- MIT
|
347
347
|
metadata:
|
348
348
|
bug_tracker_uri: https://github.com/dependabot/dependabot-core/issues
|
349
|
-
changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.
|
349
|
+
changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.289.0
|
350
350
|
post_install_message:
|
351
351
|
rdoc_options: []
|
352
352
|
require_paths:
|