dependabot-npm_and_yarn 0.380.0 → 0.381.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 631095163bb149d67b2b6a84d04641a88beffea7ddbd04361548c52baeaafc75
4
- data.tar.gz: fb76bf1bd8bd9773e2bac5b6362edfc096cbb998b335c86a4eb688970f039adf
3
+ metadata.gz: a70ee3c0913d6a9622b867d20d898019ad3453701dd1a18e1c01b62887d52351
4
+ data.tar.gz: 80f0ce022e8e6a30f4f72bb7928f0a865abbe05f8868b873d3a84077ab139e02
5
5
  SHA512:
6
- metadata.gz: 2346f503da3529d8fb8b747cd6a1d1c9c45b51c745dd2639dab3993f892d554d529800f85171939afb1397d61f76d5ce03befb7ad81f34126f976ea9e6cb0d19
7
- data.tar.gz: 2076235475c42202704ab180b1fa1c31f8c77c44f02448653f89987e880af0b2be2fa6c8f6a7c06e4a9ca47c72fcd31ea4d5d48a7ebe74e41ec40b49661b18a4
6
+ metadata.gz: c56af922ead64141beb7c8fefea2b706d84e90007fd33620f9892fb80d11fcc6e9eb239fa0d0220d0d93b92177de0c780048ccb17dd9ef4ae2409886beb7cda8
7
+ data.tar.gz: f3d511e27a4e5ca500c869e0de11729544f4323ea4776c275df99f750b947936b17dad15166571d5515c0e5d524b57eb5c7079876923cebb019a5bf288690612
@@ -21,7 +21,7 @@ module Dependabot
21
21
  sig { returns(T::Hash[String, T.untyped]) }
22
22
  def parsed
23
23
  json_obj = JSON.parse(T.must(@dependency_file.content))
24
- @parsed ||= T.let(json_obj, T.untyped)
24
+ @parsed ||= T.let(json_obj, T.nilable(T::Hash[String, T.untyped]))
25
25
  rescue JSON::ParserError
26
26
  raise Dependabot::DependencyFileNotParseable, @dependency_file.path
27
27
  end
@@ -30,15 +30,23 @@ module Dependabot
30
30
  dependencies: T::Array[Dependabot::Dependency],
31
31
  dependency_files: T::Array[Dependabot::DependencyFile],
32
32
  repo_contents_path: T.nilable(String),
33
- credentials: T::Array[Dependabot::Credential]
33
+ credentials: T::Array[Dependabot::Credential],
34
+ security_updates_only: T::Boolean
34
35
  )
35
36
  .void
36
37
  end
37
- def initialize(dependencies:, dependency_files:, repo_contents_path:, credentials:)
38
+ def initialize(
39
+ dependencies:,
40
+ dependency_files:,
41
+ repo_contents_path:,
42
+ credentials:,
43
+ security_updates_only: false
44
+ )
38
45
  @dependencies = dependencies
39
46
  @dependency_files = dependency_files
40
47
  @repo_contents_path = repo_contents_path
41
48
  @credentials = credentials
49
+ @security_updates_only = T.let(security_updates_only, T::Boolean)
42
50
  @error_handler = T.let(
43
51
  YarnErrorHandler.new(
44
52
  dependencies: dependencies,
@@ -222,7 +230,7 @@ module Dependabot
222
230
  # the lockfile.
223
231
 
224
232
  if top_level_dependency_updates.all? { |dep| requirements_changed?(dep[:name]) }
225
- Helpers.run_yarn_command("install #{yarn_berry_args}".strip)
233
+ Helpers.run_yarn_command("install #{yarn_berry_args}".strip, env: yarn_time_gate_env)
226
234
 
227
235
  # Yarn berry resolves ranges to the latest matching version, which
228
236
  # may differ from Dependabot's target. If the lockfile resolved to a
@@ -237,7 +245,8 @@ module Dependabot
237
245
 
238
246
  Helpers.run_yarn_command(
239
247
  "up -R #{updates.join(' ')} #{yarn_berry_args}".strip,
240
- fingerprint: "up -R <dependency_names> #{yarn_berry_args}".strip
248
+ fingerprint: "up -R <dependency_names> #{yarn_berry_args}".strip,
249
+ env: yarn_time_gate_env
241
250
  )
242
251
  end
243
252
  { yarn_lock.name => File.read(yarn_lock.name) }
@@ -291,7 +300,8 @@ module Dependabot
291
300
 
292
301
  Helpers.run_yarn_command(
293
302
  "up #{dep_name}@#{version} #{yarn_berry_args}".strip,
294
- fingerprint: "up <dep>@<version> #{yarn_berry_args}".strip
303
+ fingerprint: "up <dep>@<version> #{yarn_berry_args}".strip,
304
+ env: yarn_time_gate_env
295
305
  )
296
306
 
297
307
  reqs.each do |req|
@@ -304,7 +314,7 @@ module Dependabot
304
314
  # Restore package.json and re-install to normalize lockfile descriptors,
305
315
  # same as yarn classic's replaceLockfileDeclaration flow.
306
316
  restore_package_jsons(saved_package_jsons)
307
- Helpers.run_yarn_command("install #{yarn_berry_args}".strip)
317
+ Helpers.run_yarn_command("install #{yarn_berry_args}".strip, env: yarn_time_gate_env)
308
318
  end
309
319
 
310
320
  sig { returns(T::Hash[String, String]) }
@@ -353,6 +363,24 @@ module Dependabot
353
363
  { yarn_lock.name => updated_content }
354
364
  end
355
365
 
366
+ sig { returns(T::Boolean) }
367
+ def security_updates_only?
368
+ @security_updates_only
369
+ end
370
+
371
+ # Returns an env hash that disables Yarn Berry's npmMinimalAgeGate for security updates.
372
+ # npmMinimalAgeGate was introduced in Yarn 4.10.0. Setting the corresponding
373
+ # YARN_NPM_MINIMAL_AGE_GATE env var on older Yarn Berry releases (or Yarn classic) raises
374
+ # "Unrecognized or legacy configuration settings found: npmMinimalAgeGate" and aborts the
375
+ # install, so we gate on a version check.
376
+ sig { returns(T.nilable(T::Hash[String, String])) }
377
+ def yarn_time_gate_env
378
+ return nil unless security_updates_only?
379
+ return nil unless Helpers.yarn_berry_supports_minimal_age_gate?
380
+
381
+ { "YARN_NPM_MINIMAL_AGE_GATE" => "0" }
382
+ end
383
+
356
384
  sig { returns(String) }
357
385
  def yarn_berry_args
358
386
  @yarn_berry_args ||= T.let(
@@ -535,13 +563,15 @@ module Dependabot
535
563
  def write_temporary_dependency_files(yarn_lock, update_package_json: true)
536
564
  write_lockfiles
537
565
 
538
- if Helpers.yarn_berry?(yarn_lock) && yarnrc_yml_file
539
- yarnrc_yml_sanitize_content = sanitize_yarnrc_content(yarnrc_yml_content)
540
- File.write(".yarnrc.yml", yarnrc_yml_sanitize_content)
541
- else
542
- File.write(".npmrc", npmrc_content)
543
- File.write(".yarnrc", yarnrc_content) if yarnrc_specifies_private_reg?
566
+ if Helpers.yarn_berry?(yarn_lock)
567
+ if yarnrc_yml_file
568
+ yarnrc_yml_sanitize_content = sanitize_yarnrc_content(yarnrc_yml_content)
569
+ File.write(".yarnrc.yml", yarnrc_yml_sanitize_content)
570
+ end
571
+ elsif yarnrc_specifies_private_reg?
572
+ File.write(".yarnrc", yarnrc_content)
544
573
  end
574
+ File.write(".npmrc", npmrc_content)
545
575
 
546
576
  package_files.each do |file|
547
577
  path = file.name
@@ -5,6 +5,7 @@ require "dependabot/file_updaters"
5
5
  require "dependabot/file_updaters/base"
6
6
  require "dependabot/file_updaters/vendor_updater"
7
7
  require "dependabot/file_updaters/artifact_updater"
8
+ require "dependabot/errors"
8
9
  require "dependabot/npm_and_yarn/dependency_files_filterer"
9
10
  require "dependabot/npm_and_yarn/sub_dependency_files_filterer"
10
11
  require "sorbet-runtime"
@@ -22,6 +23,7 @@ module Dependabot
22
23
 
23
24
  class NoChangeError < StandardError
24
25
  extend T::Sig
26
+ include Dependabot::HasSentryContext
25
27
 
26
28
  sig { params(message: String, error_context: T::Hash[Symbol, T.untyped]).void }
27
29
  def initialize(message:, error_context:)
@@ -29,7 +31,7 @@ module Dependabot
29
31
  @error_context = error_context
30
32
  end
31
33
 
32
- sig { returns(T::Hash[Symbol, T.untyped]) }
34
+ sig { override.returns(T::Hash[Symbol, T.untyped]) }
33
35
  def sentry_context
34
36
  { extra: @error_context }
35
37
  end
@@ -440,7 +442,8 @@ module Dependabot
440
442
  dependencies: dependencies,
441
443
  dependency_files: dependency_files,
442
444
  repo_contents_path: repo_contents_path,
443
- credentials: credentials
445
+ credentials: credentials,
446
+ security_updates_only: options.fetch(:security_updates_only, false)
444
447
  ),
445
448
  T.nilable(Dependabot::NpmAndYarn::FileUpdater::YarnLockfileUpdater)
446
449
  )
@@ -248,6 +248,31 @@ module Dependabot
248
248
  yarn_major_version >= 4
249
249
  end
250
250
 
251
+ sig { returns(T::Boolean) }
252
+ def self.yarn_berry_supports_minimal_age_gate?
253
+ version = Version.new(run_single_yarn_command("--version"))
254
+ supported = version >= Version.new("4.10.0")
255
+ if supported
256
+ Dependabot.logger.info(
257
+ "Yarn #{version} supports npmMinimalAgeGate. " \
258
+ "Setting YARN_NPM_MINIMAL_AGE_GATE=0 to bypass the release-age gate for security updates."
259
+ )
260
+ else
261
+ Dependabot.logger.info(
262
+ "Yarn #{version} does not support npmMinimalAgeGate (requires 4.10.0+). " \
263
+ "YARN_NPM_MINIMAL_AGE_GATE will not be set."
264
+ )
265
+ end
266
+ supported
267
+ rescue StandardError => e
268
+ Dependabot.logger.warn(
269
+ "Could not determine Yarn version to check npmMinimalAgeGate support: #{e.message}. " \
270
+ "Assuming unsupported (returning false). YARN_NPM_MINIMAL_AGE_GATE will not be set, " \
271
+ "so the registry's release-age gate may still block security updates."
272
+ )
273
+ false
274
+ end
275
+
251
276
  sig { returns(T.nilable(String)) }
252
277
  def self.setup_yarn_berry
253
278
  # Always disable immutable installs so yarn's CI detection doesn't prevent updates.
@@ -362,10 +387,16 @@ module Dependabot
362
387
  end
363
388
 
364
389
  # Setup yarn and run a single yarn command returning stdout/stderr
365
- sig { params(command: String, fingerprint: T.nilable(String)).returns(String) }
366
- def self.run_yarn_command(command, fingerprint: nil)
390
+ sig do
391
+ params(
392
+ command: String,
393
+ fingerprint: T.nilable(String),
394
+ env: T.nilable(T::Hash[String, String])
395
+ ).returns(String)
396
+ end
397
+ def self.run_yarn_command(command, fingerprint: nil, env: nil)
367
398
  setup_yarn_berry
368
- run_single_yarn_command(command, fingerprint: fingerprint)
399
+ run_single_yarn_command(command, fingerprint: fingerprint, env: env)
369
400
  end
370
401
 
371
402
  # Run single pnpm command returning stdout/stderr
@@ -382,14 +413,21 @@ module Dependabot
382
413
  end
383
414
 
384
415
  # Run single yarn command returning stdout/stderr
385
- sig { params(command: String, fingerprint: T.nilable(String)).returns(String) }
386
- def self.run_single_yarn_command(command, fingerprint: nil)
416
+ sig do
417
+ params(
418
+ command: String,
419
+ fingerprint: T.nilable(String),
420
+ env: T.nilable(T::Hash[String, String])
421
+ ).returns(String)
422
+ end
423
+ def self.run_single_yarn_command(command, fingerprint: nil, env: nil)
387
424
  if Dependabot::Experiments.enabled?(:enable_corepack_for_npm_and_yarn)
388
- package_manager_run_command(YarnPackageManager::NAME, command, fingerprint: fingerprint)
425
+ package_manager_run_command(YarnPackageManager::NAME, command, fingerprint: fingerprint, env: env)
389
426
  else
390
427
  Dependabot::SharedHelpers.run_shell_command(
391
428
  "yarn #{command}",
392
- fingerprint: "yarn #{fingerprint || command}"
429
+ fingerprint: "yarn #{fingerprint || command}",
430
+ env: env
393
431
  )
394
432
  end
395
433
  end
@@ -259,14 +259,20 @@ module Dependabot
259
259
  sig { params(_block: T.untyped).returns(T.nilable(Dependabot::Version)) }
260
260
  def with_custom_registry_rescue(&_block)
261
261
  yield
262
- rescue Excon::Error::Socket, Excon::Error::Timeout, RegistryError
263
- raise unless package_fetcher.custom_registry?
262
+ rescue Excon::Error::Socket, Excon::Error::Timeout, RegistryError => e
263
+ raise unless package_fetcher.custom_registry? || eof_socket_error?(e)
264
264
 
265
- # Custom registries can be flaky. We don't want to make that
266
- # our problem, so quietly return `nil` here.
265
+ # Custom registries can be flaky, and the global npm registry can
266
+ # occasionally terminate connections (EOFError). Don't abort the update
267
+ # flow for these transient failures; quietly return `nil` here.
267
268
  nil
268
269
  end
269
270
 
271
+ sig { params(error: StandardError).returns(T::Boolean) }
272
+ def eof_socket_error?(error)
273
+ error.is_a?(Excon::Error::Socket) && error.socket_error.is_a?(EOFError)
274
+ end
275
+
270
276
  sig { returns(T::Boolean) }
271
277
  def valid_npm_details?
272
278
  !!package_details&.releases&.any?
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dependabot-npm_and_yarn
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.380.0
4
+ version: 0.381.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dependabot
@@ -15,14 +15,14 @@ dependencies:
15
15
  requirements:
16
16
  - - '='
17
17
  - !ruby/object:Gem::Version
18
- version: 0.380.0
18
+ version: 0.381.0
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - '='
24
24
  - !ruby/object:Gem::Version
25
- version: 0.380.0
25
+ version: 0.381.0
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: debug
28
28
  requirement: !ruby/object:Gem::Requirement
@@ -374,7 +374,7 @@ licenses:
374
374
  - MIT
375
375
  metadata:
376
376
  bug_tracker_uri: https://github.com/dependabot/dependabot-core/issues
377
- changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.380.0
377
+ changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.381.0
378
378
  rdoc_options: []
379
379
  require_paths:
380
380
  - lib