dependabot-npm_and_yarn 0.287.0 → 0.289.0
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|