dependabot-npm_and_yarn 0.350.0 → 0.352.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.
data/helpers/package.json CHANGED
@@ -21,9 +21,9 @@
21
21
  "patch-package": "^8.0.0"
22
22
  },
23
23
  "devDependencies": {
24
- "eslint": "^9.31.0",
24
+ "eslint": "^9.39.1",
25
25
  "eslint-config-prettier": "^10.1.8",
26
- "jest": "^30.0.5",
27
- "prettier": "^3.6.2"
26
+ "jest": "^30.2.0",
27
+ "prettier": "^3.7.4"
28
28
  }
29
29
  }
@@ -261,9 +261,16 @@ module Dependabot
261
261
 
262
262
  full_version = Regexp.last_match(1)
263
263
 
264
- # Normalize version: if full_version does not have patch version, add ".0"
264
+ # Normalize version: ensure it has major.minor.patch format
265
265
  version_parts = T.must(full_version).split(".")
266
- full_version = "#{full_version}.0" if version_parts.length == 2
266
+ full_version = case version_parts.length
267
+ when 1
268
+ "#{full_version}.0.0" # major only -> major.0.0
269
+ when 2
270
+ "#{full_version}.0" # major.minor -> major.minor.0
271
+ else
272
+ full_version # already major.minor.patch
273
+ end
267
274
 
268
275
  _, major, minor = version_components(full_version)
269
276
  return nil if major.nil?
@@ -0,0 +1,217 @@
1
+ # typed: strong
2
+ # frozen_string_literal: true
3
+
4
+ require "sorbet-runtime"
5
+
6
+ require "dependabot/dependency_file"
7
+ require "dependabot/shared_helpers"
8
+ require "dependabot/npm_and_yarn/helpers"
9
+ require "dependabot/npm_and_yarn/package_manager"
10
+ require "dependabot/npm_and_yarn/file_updater/npmrc_builder"
11
+
12
+ module Dependabot
13
+ module NpmAndYarn
14
+ class DependencyGrapher < Dependabot::DependencyGraphers::Base
15
+ class LockfileGenerator
16
+ extend T::Sig
17
+
18
+ sig do
19
+ params(
20
+ dependency_files: T::Array[Dependabot::DependencyFile],
21
+ package_manager: String,
22
+ credentials: T::Array[Dependabot::Credential]
23
+ ).void
24
+ end
25
+ def initialize(dependency_files:, package_manager:, credentials:)
26
+ @dependency_files = dependency_files
27
+ @package_manager = package_manager
28
+ @credentials = credentials
29
+ end
30
+
31
+ sig { returns(T.nilable(Dependabot::DependencyFile)) }
32
+ def generate
33
+ SharedHelpers.in_a_temporary_directory do
34
+ write_temporary_files
35
+ run_lockfile_generation
36
+ read_generated_lockfile
37
+ end
38
+ rescue SharedHelpers::HelperSubprocessFailed => e
39
+ handle_generation_error(e)
40
+ nil
41
+ end
42
+
43
+ private
44
+
45
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
46
+ attr_reader :dependency_files
47
+
48
+ sig { returns(String) }
49
+ attr_reader :package_manager
50
+
51
+ sig { returns(T::Array[Dependabot::Credential]) }
52
+ attr_reader :credentials
53
+
54
+ sig { void }
55
+ def write_temporary_files
56
+ write_package_files
57
+ write_npmrc
58
+ write_yarnrc if yarn?
59
+ end
60
+
61
+ sig { void }
62
+ def write_package_files
63
+ dependency_files.each do |file|
64
+ next unless file.name.end_with?(
65
+ "package.json", ".npmrc", ".yarnrc", ".yarnrc.yml", "pnpm-workspace.yaml"
66
+ )
67
+
68
+ path = file.name
69
+ FileUtils.mkdir_p(File.dirname(path))
70
+ File.write(path, file.content)
71
+ end
72
+ end
73
+
74
+ sig { void }
75
+ def write_npmrc
76
+ # Skip if .npmrc already exists in dependency files (already written above)
77
+ return if dependency_files.any? { |f| f.name.end_with?(".npmrc") }
78
+
79
+ # Use NpmrcBuilder to generate npmrc content from credentials
80
+ npmrc_content = FileUpdater::NpmrcBuilder.new(
81
+ credentials: credentials,
82
+ dependency_files: dependency_files
83
+ ).npmrc_content
84
+
85
+ return if npmrc_content.empty?
86
+
87
+ File.write(".npmrc", npmrc_content)
88
+ end
89
+
90
+ sig { void }
91
+ def write_yarnrc
92
+ return unless yarn_berry?
93
+
94
+ # For Yarn Berry, set up the environment properly
95
+ Helpers.setup_yarn_berry
96
+ end
97
+
98
+ sig { void }
99
+ def run_lockfile_generation
100
+ Dependabot.logger.info("Generating lockfile using #{package_manager}")
101
+
102
+ case package_manager
103
+ when NpmPackageManager::NAME
104
+ run_npm_lockfile_generation
105
+ when YarnPackageManager::NAME
106
+ run_yarn_lockfile_generation
107
+ when PNPMPackageManager::NAME
108
+ run_pnpm_lockfile_generation
109
+ else
110
+ raise "Unknown package manager: #{package_manager}"
111
+ end
112
+ end
113
+
114
+ sig { void }
115
+ def run_npm_lockfile_generation
116
+ # Set dependency files and credentials for automatic env variable injection
117
+ Helpers.dependency_files = dependency_files
118
+ Helpers.credentials = credentials
119
+
120
+ # Use --package-lock-only to generate lockfile without installing node_modules
121
+ # Use --ignore-scripts to prevent running any scripts
122
+ # Use --force to ignore platform checks
123
+ command = "install --package-lock-only --ignore-scripts --force"
124
+ Helpers.run_npm_command(command, fingerprint: command)
125
+ end
126
+
127
+ sig { void }
128
+ def run_yarn_lockfile_generation
129
+ if yarn_berry?
130
+ # Yarn Berry (2+) uses different commands
131
+ Helpers.run_yarn_command("install --mode update-lockfile")
132
+ else
133
+ # Yarn Classic (1.x)
134
+ SharedHelpers.run_shell_command(
135
+ "yarn install --ignore-scripts --frozen-lockfile=false",
136
+ fingerprint: "yarn install --ignore-scripts --frozen-lockfile=false"
137
+ )
138
+ end
139
+ end
140
+
141
+ sig { void }
142
+ def run_pnpm_lockfile_generation
143
+ # pnpm uses --lockfile-only to generate lockfile without installing
144
+ command = "install --lockfile-only --ignore-scripts"
145
+ Helpers.run_pnpm_command(command, fingerprint: command)
146
+ end
147
+
148
+ sig { returns(T.nilable(Dependabot::DependencyFile)) }
149
+ def read_generated_lockfile
150
+ lockfile_name = expected_lockfile_name
151
+
152
+ unless File.exist?(lockfile_name)
153
+ Dependabot.logger.warn("Lockfile #{lockfile_name} was not generated")
154
+ return nil
155
+ end
156
+
157
+ content = File.read(lockfile_name)
158
+
159
+ Dependabot::DependencyFile.new(
160
+ name: lockfile_name,
161
+ content: content,
162
+ directory: "/"
163
+ )
164
+ end
165
+
166
+ sig { returns(String) }
167
+ def expected_lockfile_name
168
+ case package_manager
169
+ when NpmPackageManager::NAME
170
+ NpmPackageManager::LOCKFILE_NAME
171
+ when YarnPackageManager::NAME
172
+ YarnPackageManager::LOCKFILE_NAME
173
+ when PNPMPackageManager::NAME
174
+ PNPMPackageManager::LOCKFILE_NAME
175
+ else
176
+ "package-lock.json"
177
+ end
178
+ end
179
+
180
+ sig { returns(T::Boolean) }
181
+ def yarn?
182
+ package_manager == YarnPackageManager::NAME
183
+ end
184
+
185
+ sig { returns(T::Boolean) }
186
+ def yarn_berry?
187
+ return false unless yarn?
188
+
189
+ # Check for .yarnrc.yml which indicates Yarn Berry
190
+ dependency_files.any? { |f| f.name.end_with?(".yarnrc.yml") }
191
+ end
192
+
193
+ sig { params(error: SharedHelpers::HelperSubprocessFailed).void }
194
+ def handle_generation_error(error)
195
+ Dependabot.logger.error(
196
+ "Failed to generate lockfile with #{package_manager}: #{error.message}"
197
+ )
198
+
199
+ # Log more details for debugging
200
+ if error.message.include?("ERESOLVE")
201
+ Dependabot.logger.error(
202
+ "Dependency resolution failed. This may be due to conflicting peer dependencies."
203
+ )
204
+ elsif error.message.include?("ENOTFOUND") || error.message.include?("ETIMEDOUT")
205
+ Dependabot.logger.error(
206
+ "Network error while generating lockfile. Registry may be unreachable."
207
+ )
208
+ elsif error.message.include?("401") || error.message.include?("403")
209
+ Dependabot.logger.error(
210
+ "Authentication error. Check that credentials are configured correctly."
211
+ )
212
+ end
213
+ end
214
+ end
215
+ end
216
+ end
217
+ end
@@ -0,0 +1,174 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require "sorbet-runtime"
5
+
6
+ require "dependabot/dependency_graphers"
7
+ require "dependabot/dependency_graphers/base"
8
+ require "dependabot/npm_and_yarn/file_parser"
9
+ require "dependabot/npm_and_yarn/package_manager"
10
+ require "dependabot/npm_and_yarn/helpers"
11
+
12
+ module Dependabot
13
+ module NpmAndYarn
14
+ class DependencyGrapher < Dependabot::DependencyGraphers::Base
15
+ extend T::Sig
16
+
17
+ require_relative "dependency_grapher/lockfile_generator"
18
+
19
+ sig { override.returns(Dependabot::DependencyFile) }
20
+ def relevant_dependency_file
21
+ # Prefer lockfile if present, otherwise use package.json
22
+ lockfile || package_json
23
+ end
24
+
25
+ sig { override.void }
26
+ def prepare!
27
+ if lockfile.nil?
28
+ Dependabot.logger.info("No lockfile found, generating ephemeral lockfile for dependency graphing")
29
+ generate_ephemeral_lockfile!
30
+ emit_missing_lockfile_warning!
31
+ end
32
+ super
33
+ end
34
+
35
+ private
36
+
37
+ sig { returns(Dependabot::DependencyFile) }
38
+ def package_json
39
+ return T.must(@package_json) if defined?(@package_json)
40
+
41
+ T.must(
42
+ @package_json = T.let(
43
+ T.must(dependency_files.find { |f| f.name.end_with?(NpmAndYarn::MANIFEST_FILENAME) }),
44
+ T.nilable(Dependabot::DependencyFile)
45
+ )
46
+ )
47
+ end
48
+
49
+ sig { returns(T.nilable(Dependabot::DependencyFile)) }
50
+ def lockfile
51
+ return @lockfile if defined?(@lockfile)
52
+
53
+ @lockfile = T.let(
54
+ dependency_files.find do |f|
55
+ f.name.end_with?(
56
+ NpmPackageManager::LOCKFILE_NAME,
57
+ YarnPackageManager::LOCKFILE_NAME,
58
+ PNPMPackageManager::LOCKFILE_NAME
59
+ )
60
+ end,
61
+ T.nilable(Dependabot::DependencyFile)
62
+ )
63
+ end
64
+
65
+ sig { returns(T::Hash[String, T.untyped]) }
66
+ def parsed_package_json
67
+ @parsed_package_json ||= T.let(
68
+ JSON.parse(T.must(package_json.content)),
69
+ T.nilable(T::Hash[String, T.untyped])
70
+ )
71
+ rescue JSON::ParserError
72
+ {}
73
+ end
74
+
75
+ sig { returns(T::Hash[Symbol, T.nilable(Dependabot::DependencyFile)]) }
76
+ def lockfiles_hash
77
+ {
78
+ npm: dependency_files.find { |f| f.name.end_with?(NpmPackageManager::LOCKFILE_NAME) },
79
+ yarn: dependency_files.find { |f| f.name.end_with?(YarnPackageManager::LOCKFILE_NAME) },
80
+ pnpm: dependency_files.find { |f| f.name.end_with?(PNPMPackageManager::LOCKFILE_NAME) }
81
+ }
82
+ end
83
+
84
+ sig { returns(String) }
85
+ def detected_package_manager
86
+ @detected_package_manager ||= T.let(
87
+ PackageManagerDetector.new(
88
+ lockfiles_hash,
89
+ parsed_package_json
90
+ ).detect_package_manager,
91
+ T.nilable(String)
92
+ )
93
+ end
94
+
95
+ sig { void }
96
+ def generate_ephemeral_lockfile!
97
+ generator = LockfileGenerator.new(
98
+ dependency_files: dependency_files,
99
+ package_manager: detected_package_manager,
100
+ credentials: file_parser.credentials
101
+ )
102
+
103
+ ephemeral_lockfile = generator.generate
104
+ return unless ephemeral_lockfile
105
+
106
+ # Inject the ephemeral lockfile into the dependency files
107
+ # so the file parser can use it
108
+ inject_ephemeral_lockfile(ephemeral_lockfile)
109
+
110
+ Dependabot.logger.info(
111
+ "Successfully generated ephemeral #{ephemeral_lockfile.name} for dependency graphing"
112
+ )
113
+ rescue StandardError => e
114
+ Dependabot.logger.warn(
115
+ "Failed to generate ephemeral lockfile: #{e.message}. " \
116
+ "Dependency versions may not be resolved."
117
+ )
118
+ end
119
+
120
+ sig { params(ephemeral_lockfile: Dependabot::DependencyFile).void }
121
+ def inject_ephemeral_lockfile(ephemeral_lockfile)
122
+ dependency_files << ephemeral_lockfile
123
+
124
+ # Clear our cached lockfile reference so it picks up the new one
125
+ @lockfile = T.let(nil, T.nilable(Dependabot::DependencyFile))
126
+
127
+ # Clear the FileParser's memoized lockfile references so it will
128
+ # find the newly injected lockfile when parse is called
129
+ file_parser.instance_variable_set(:@package_lock, nil)
130
+ file_parser.instance_variable_set(:@yarn_lock, nil)
131
+ file_parser.instance_variable_set(:@pnpm_lock, nil)
132
+ # Also clear the lockfile_parser which parses lockfile content
133
+ file_parser.instance_variable_set(:@lockfile_parser, nil)
134
+ # Also clear the package_manager_helper which caches lockfile info
135
+ file_parser.instance_variable_set(:@package_manager_helper, nil)
136
+ end
137
+
138
+ sig { void }
139
+ def emit_missing_lockfile_warning!
140
+ Dependabot.logger.warn(
141
+ "No lockfile was found in this repository. " \
142
+ "Dependabot generated a temporary lockfile to determine exact dependency versions.\n\n" \
143
+ "To ensure consistent builds and security scanning, we recommend:\n" \
144
+ " - Committing your package-lock.json (npm), yarn.lock (yarn), or pnpm-lock.yaml (pnpm)\n" \
145
+ " - Setting up a scheduled Dependabot graph job to periodically scan for changes\n\n" \
146
+ "Without a committed lockfile, resolved dependency versions may change between scans " \
147
+ "due to new package releases."
148
+ )
149
+ end
150
+
151
+ # Fetches subdependencies for a given dependency.
152
+ # For npm/yarn/pnpm, we can extract this from the lockfile parser if available.
153
+ sig { override.params(dependency: Dependabot::Dependency).returns(T::Array[String]) }
154
+ def fetch_subdependencies(dependency)
155
+ # Check if the parser has attached depends_on metadata
156
+ dependency.metadata.fetch(:depends_on, [])
157
+ end
158
+
159
+ sig { override.params(_dependency: Dependabot::Dependency).returns(String) }
160
+ def purl_pkg_for(_dependency)
161
+ "npm"
162
+ end
163
+
164
+ # npm packages use the package name as-is for the purl
165
+ sig { params(dependency: Dependabot::Dependency).returns(String) }
166
+ def purl_name_for(dependency)
167
+ # Handle scoped packages: @scope/name -> %40scope/name (URL encoded @)
168
+ dependency.name.sub(/^@/, "%40")
169
+ end
170
+ end
171
+ end
172
+ end
173
+
174
+ Dependabot::DependencyGraphers.register("npm_and_yarn", Dependabot::NpmAndYarn::DependencyGrapher)
@@ -139,6 +139,10 @@ module Dependabot
139
139
  return lockfile.content if npmrc_disables_lockfile?
140
140
  return lockfile.content unless updatable_dependencies.any?
141
141
 
142
+ # Set dependency files and credentials for automatic env variable injection
143
+ Helpers.dependency_files = dependency_files
144
+ Helpers.credentials = credentials
145
+
142
146
  @updated_lockfile_content ||= T.let(
143
147
  SharedHelpers.in_a_temporary_directory do
144
148
  write_temporary_dependency_files
@@ -284,14 +288,18 @@ module Dependabot
284
288
  # TODO: Update the npm 6 updater to use these args as we currently
285
289
  # do the same in the js updater helper, we've kept it separate for
286
290
  # the npm 7 rollout
287
- install_args = top_level_dependencies.map { |dependency| npm_install_args(dependency) }
288
291
 
289
- run_npm_install_lockfile_only(install_args)
292
+ top_level_dependencies.each do |dependency|
293
+ install_args = [npm_install_args(dependency)]
294
+ is_optional = optional_dependency?(dependency)
295
+ run_npm_install_lockfile_only(install_args, has_optional_dependencies: is_optional)
296
+ end
290
297
 
291
298
  unless dependencies_in_current_package_json
292
299
  File.write(T.must(package_json).name, previous_package_json)
293
300
 
294
- run_npm_install_lockfile_only
301
+ # Run final install without specific dependencies
302
+ run_npm_install_lockfile_only([], has_optional_dependencies: false)
295
303
  end
296
304
 
297
305
  { lockfile_basename => File.read(lockfile_basename) }
@@ -335,31 +343,35 @@ module Dependabot
335
343
  #
336
344
  # Other npm flags:
337
345
  # - `--force` ignores checks for platform (os, cpu) and engines
338
- # - `--dry-run=false` the updater sets a global .npmrc with `dry-run: true`
339
- # to work around an issue in npm 6, we don't want that here
340
346
  # - `--ignore-scripts` disables prepare and prepack scripts which are
341
347
  # run when installing git dependencies
342
- sig { params(install_args: T::Array[String]).returns(String) }
343
- def run_npm_install_lockfile_only(install_args = [])
344
- command = [
348
+ # - `--save-optional` when updating optional dependencies to ensure they
349
+ # stay in optionalDependencies section and allow version upgrades
350
+ sig { params(install_args: T::Array[String], has_optional_dependencies: T::Boolean).returns(String) }
351
+ def run_npm_install_lockfile_only(install_args = [], has_optional_dependencies: false)
352
+ command_args = [
345
353
  "install",
346
354
  *install_args,
347
355
  "--force",
348
- "--dry-run",
349
- "false",
350
356
  "--ignore-scripts",
351
357
  "--package-lock-only"
352
- ].join(" ")
358
+ ]
359
+
360
+ command_args << "--save-optional" if has_optional_dependencies
353
361
 
354
- fingerprint = [
362
+ command = command_args.join(" ")
363
+
364
+ fingerprint_args = [
355
365
  "install",
356
366
  install_args.empty? ? "" : "<install_args>",
357
367
  "--force",
358
- "--dry-run",
359
- "false",
360
368
  "--ignore-scripts",
361
369
  "--package-lock-only"
362
- ].join(" ")
370
+ ]
371
+
372
+ fingerprint_args << "--save-optional" if has_optional_dependencies
373
+
374
+ fingerprint = fingerprint_args.join(" ")
363
375
 
364
376
  Helpers.run_npm_command(command, fingerprint: fingerprint)
365
377
  end
@@ -408,6 +420,13 @@ module Dependabot
408
420
  end
409
421
  end
410
422
 
423
+ sig { params(dependency: Dependabot::Dependency).returns(T::Boolean) }
424
+ def optional_dependency?(dependency)
425
+ dependency.requirements.any? do |req|
426
+ req[:groups]&.include?("optionalDependencies")
427
+ end
428
+ end
429
+
411
430
  # rubocop:disable Metrics/AbcSize
412
431
  # rubocop:disable Metrics/CyclomaticComplexity
413
432
  # rubocop:disable Metrics/PerceivedComplexity
@@ -143,6 +143,10 @@ module Dependabot
143
143
  .returns(String)
144
144
  end
145
145
  def run_pnpm_update(pnpm_lock:, updated_pnpm_workspace_content: nil)
146
+ # Set dependency files and credentials for automatic env variable injection
147
+ Helpers.dependency_files = dependency_files
148
+ Helpers.credentials = credentials
149
+
146
150
  SharedHelpers.in_a_temporary_repo_directory(base_dir, repo_contents_path) do
147
151
  File.write(".npmrc", npmrc_content(pnpm_lock))
148
152
 
@@ -89,6 +89,10 @@ module Dependabot
89
89
 
90
90
  sig { params(yarn_lock: Dependabot::DependencyFile).returns(String) }
91
91
  def updated_yarn_lock(yarn_lock)
92
+ # Set dependency files and credentials for automatic env variable injection
93
+ Helpers.dependency_files = dependency_files
94
+ Helpers.credentials = credentials
95
+
92
96
  base_dir = T.must(dependency_files.first).directory
93
97
  SharedHelpers.in_a_temporary_repo_directory(base_dir, repo_contents_path) do
94
98
  write_temporary_dependency_files(yarn_lock)
@@ -5,6 +5,8 @@ require "dependabot/dependency"
5
5
  require "dependabot/file_parsers"
6
6
  require "dependabot/file_parsers/base"
7
7
  require "dependabot/shared_helpers"
8
+ require "dependabot/npm_and_yarn/registry_helper"
9
+ require "dependabot/experiments"
8
10
  require "sorbet-runtime"
9
11
 
10
12
  module Dependabot
@@ -12,6 +14,32 @@ module Dependabot
12
14
  module Helpers # rubocop:disable Metrics/ModuleLength
13
15
  extend T::Sig
14
16
 
17
+ # Thread-local storage for dependency files and credentials
18
+ # This allows automatic env variable injection without passing parameters everywhere
19
+ class << self
20
+ extend T::Sig
21
+
22
+ sig { params(files: T::Array[Dependabot::DependencyFile]).void }
23
+ def dependency_files=(files)
24
+ Thread.current[:npm_and_yarn_dependency_files] = files
25
+ end
26
+
27
+ sig { returns(T.nilable(T::Array[Dependabot::DependencyFile])) }
28
+ def dependency_files
29
+ T.cast(Thread.current[:npm_and_yarn_dependency_files], T.nilable(T::Array[Dependabot::DependencyFile]))
30
+ end
31
+
32
+ sig { params(creds: T::Array[Dependabot::Credential]).void }
33
+ def credentials=(creds)
34
+ Thread.current[:npm_and_yarn_credentials] = creds
35
+ end
36
+
37
+ sig { returns(T.nilable(T::Array[Dependabot::Credential])) }
38
+ def credentials
39
+ T.cast(Thread.current[:npm_and_yarn_credentials], T.nilable(T::Array[Dependabot::Credential]))
40
+ end
41
+ end
42
+
15
43
  YARN_PATH_NOT_FOUND =
16
44
  /^.*(?<error>The "yarn-path" option has been set \(in [^)]+\), but the specified location doesn't exist)/
17
45
 
@@ -268,13 +296,14 @@ module Dependabot
268
296
  ).returns(String)
269
297
  end
270
298
  def self.run_npm_command(command, fingerprint: command, env: nil)
299
+ merged_env = merge_corepack_env(env)
271
300
  if Dependabot::Experiments.enabled?(:enable_corepack_for_npm_and_yarn)
272
301
  package_manager_run_command(
273
302
  NpmPackageManager::NAME,
274
303
  command,
275
304
  fingerprint: fingerprint,
276
305
  output_observer: ->(output) { command_observer(output) },
277
- env: env
306
+ env: merged_env
278
307
  )
279
308
  else
280
309
  Dependabot::SharedHelpers.run_shell_command(
@@ -510,6 +539,35 @@ module Dependabot
510
539
  raise
511
540
  end
512
541
 
542
+ sig { params(env: T.nilable(T::Hash[String, String])).returns(T.nilable(T::Hash[String, String])) }
543
+ def self.merge_corepack_env(env)
544
+ corepack_env = build_corepack_env_variables
545
+ return env if corepack_env.nil? || corepack_env.empty?
546
+ return corepack_env if env.nil?
547
+
548
+ corepack_env.merge(env)
549
+ end
550
+
551
+ sig { returns(T.nilable(T::Hash[String, String])) }
552
+ def self.build_corepack_env_variables
553
+ return nil unless Dependabot::Experiments.enabled?(:enable_private_registry_for_corepack)
554
+ return nil if dependency_files.nil? || credentials.nil?
555
+
556
+ files = T.must(dependency_files)
557
+ creds = T.must(credentials)
558
+
559
+ registry_helper = RegistryHelper.new(
560
+ {
561
+ npmrc: files.find { |f| f.name.end_with?(".npmrc") },
562
+ yarnrc: files.find { |f| f.name.end_with?(".yarnrc") && !f.name.end_with?(".yarnrc.yml") },
563
+ yarnrc_yml: files.find { |f| f.name.end_with?(".yarnrc.yml") }
564
+ },
565
+ creds
566
+ )
567
+
568
+ registry_helper.find_corepack_env_variables
569
+ end
570
+
513
571
  private_class_method :run_single_yarn_command
514
572
 
515
573
  sig { params(pnpm_lock: DependencyFile).returns(T.nilable(String)) }
@@ -25,16 +25,12 @@ module Dependabot
25
25
  def self.run_npm8_subdependency_update_command(dependency_names)
26
26
  # NOTE: npm options
27
27
  # - `--force` ignores checks for platform (os, cpu) and engines
28
- # - `--dry-run=false` the updater sets a global .npmrc with dry-run: true to
29
- # work around an issue in npm 6, we don't want that here
30
28
  # - `--ignore-scripts` disables prepare and prepack scripts which are run
31
29
  # when installing git dependencies
32
30
  command = [
33
31
  "update",
34
32
  *dependency_names,
35
33
  "--force",
36
- "--dry-run",
37
- "false",
38
34
  "--ignore-scripts",
39
35
  "--package-lock-only"
40
36
  ].join(" ")
@@ -43,8 +39,6 @@ module Dependabot
43
39
  "update",
44
40
  "<dependency_names>",
45
41
  "--force",
46
- "--dry-run",
47
- "false",
48
42
  "--ignore-scripts",
49
43
  "--package-lock-only"
50
44
  ].join(" ")