dependabot-npm_and_yarn 0.213.0 → 0.215.0

Sign up to get free protection for your applications and to get access to all the features.
data/helpers/package.json CHANGED
@@ -10,17 +10,17 @@
10
10
  },
11
11
  "dependencies": {
12
12
  "@dependabot/yarn-lib": "^1.22.19",
13
- "@npmcli/arborist": "^6.0.0",
13
+ "@npmcli/arborist": "^6.1.4",
14
14
  "detect-indent": "^6.1.0",
15
15
  "nock": "^13.2.9",
16
16
  "npm": "6.14.17",
17
17
  "semver": "^7.3.8"
18
18
  },
19
19
  "devDependencies": {
20
- "eslint": "^8.26.0",
20
+ "eslint": "^8.29.0",
21
21
  "eslint-config-prettier": "^8.5.0",
22
- "jest": "^29.2.1",
23
- "prettier": "^2.7.1",
22
+ "jest": "^29.3.1",
23
+ "prettier": "^2.8.0",
24
24
  "rimraf": "^3.0.2"
25
25
  }
26
26
  }
@@ -446,9 +446,9 @@
446
446
  }
447
447
  },
448
448
  "minimatch": {
449
- "version": "3.0.4",
450
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
451
- "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
449
+ "version": "3.1.2",
450
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
451
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
452
452
  "requires": {
453
453
  "brace-expansion": "^1.1.7"
454
454
  }
@@ -4,7 +4,6 @@ require "json"
4
4
  require "dependabot/dependency_file"
5
5
  require "dependabot/errors"
6
6
  require "dependabot/npm_and_yarn/file_fetcher"
7
- require "dependabot/npm_and_yarn/file_parser/yarn_lockfile_parser"
8
7
 
9
8
  module Dependabot
10
9
  module NpmAndYarn
@@ -122,7 +121,17 @@ module Dependabot
122
121
  return {} unless yarn_lock
123
122
 
124
123
  @parsed_yarn_lock ||=
125
- FileParser::YarnLockfileParser.new(lockfile: yarn_lock).parse
124
+ SharedHelpers.in_a_temporary_directory do
125
+ File.write("yarn.lock", yarn_lock.content)
126
+
127
+ SharedHelpers.run_helper_subprocess(
128
+ command: NativeHelpers.helper_path,
129
+ function: "yarn:parseLockfile",
130
+ args: [Dir.pwd]
131
+ )
132
+ rescue SharedHelpers::HelperSubprocessFailed
133
+ raise Dependabot::DependencyFileNotParseable, yarn_lock.path
134
+ end
126
135
  end
127
136
 
128
137
  # The path back to the root lockfile
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "json"
4
+ require "dependabot/logger"
4
5
  require "dependabot/file_fetchers"
5
6
  require "dependabot/file_fetchers/base"
6
7
  require "dependabot/npm_and_yarn/helpers"
@@ -9,6 +10,7 @@ require "dependabot/npm_and_yarn/file_parser/lockfile_parser"
9
10
 
10
11
  module Dependabot
11
12
  module NpmAndYarn
13
+ # rubocop:disable Metrics/ClassLength
12
14
  class FileFetcher < Dependabot::FileFetchers::Base
13
15
  require_relative "file_fetcher/path_dependency_builder"
14
16
 
@@ -30,6 +32,24 @@ module Dependabot
30
32
  "Repo must contain a package.json."
31
33
  end
32
34
 
35
+ # Overridden to pull any yarn data or plugins which may be stored with Git LFS.
36
+ def clone_repo_contents
37
+ return @git_lfs_cloned_repo_contents_path if defined?(@git_lfs_cloned_repo_contents_path)
38
+
39
+ @git_lfs_cloned_repo_contents_path = super
40
+ begin
41
+ SharedHelpers.with_git_configured(credentials: credentials) do
42
+ Dir.chdir(@git_lfs_cloned_repo_contents_path) do
43
+ cache_dir = Helpers.fetch_yarnrc_yml_value("cacheFolder", "./yarn/cache")
44
+ SharedHelpers.run_shell_command("git lfs pull --include .yarn,#{cache_dir}")
45
+ end
46
+ @git_lfs_cloned_repo_contents_path
47
+ end
48
+ rescue StandardError
49
+ @git_lfs_cloned_repo_contents_path
50
+ end
51
+ end
52
+
33
53
  private
34
54
 
35
55
  def fetch_files
@@ -47,9 +67,42 @@ module Dependabot
47
67
  fetched_files += path_dependencies(fetched_files)
48
68
  instrument_package_manager_version
49
69
 
70
+ fetched_files << inferred_npmrc if inferred_npmrc
71
+
50
72
  fetched_files.uniq
51
73
  end
52
74
 
75
+ # If every entry in the lockfile uses the same registry, we can infer
76
+ # that there is a global .npmrc file, so add it here as if it were in the repo.
77
+ def inferred_npmrc
78
+ return @inferred_npmrc if defined?(@inferred_npmrc)
79
+ return @inferred_npmrc = nil unless npmrc.nil? && package_lock
80
+
81
+ known_registries = []
82
+ JSON.parse(package_lock.content).fetch("dependencies", {}).each do |_name, details|
83
+ resolved = details.fetch("resolved", "https://registry.npmjs.org")
84
+ begin
85
+ uri = URI.parse(resolved)
86
+ rescue URI::InvalidURIError
87
+ # Ignoring non-URIs since they're not registries.
88
+ # This can happen if resolved is false, for instance.
89
+ next
90
+ end
91
+ # Check for scheme since path dependencies will not have one
92
+ known_registries << "#{uri.scheme}://#{uri.host}" if uri.scheme && uri.host
93
+ end
94
+
95
+ if known_registries.uniq.length == 1 && known_registries.first != "https://registry.npmjs.org"
96
+ Dependabot.logger.info("Inferred global NPM registry is: #{known_registries.first}")
97
+ return @inferred_npmrc = Dependabot::DependencyFile.new(
98
+ name: ".npmrc",
99
+ content: "registry=#{known_registries.first}"
100
+ )
101
+ end
102
+
103
+ @inferred_npmrc = nil
104
+ end
105
+
53
106
  def instrument_package_manager_version
54
107
  package_managers = {}
55
108
 
@@ -68,7 +121,7 @@ module Dependabot
68
121
  return @yarn_version if defined?(@yarn_version)
69
122
 
70
123
  package = JSON.parse(package_json.content)
71
- if Experiments.enabled?(:yarn_berry) && (package_manager = package.fetch("packageManager", nil))
124
+ if (package_manager = package.fetch("packageManager", nil))
72
125
  get_yarn_version_from_package_json(package_manager)
73
126
  elsif yarn_lock
74
127
  1
@@ -152,11 +205,16 @@ module Dependabot
152
205
  @lerna_packages ||= fetch_lerna_packages
153
206
  end
154
207
 
208
+ # rubocop:disable Metrics/PerceivedComplexity
155
209
  def path_dependencies(fetched_files)
156
210
  package_json_files = []
157
211
  unfetchable_deps = []
158
212
 
159
213
  path_dependency_details(fetched_files).each do |name, path|
214
+ # This happens with relative paths in the package-lock. Skipping it since it results
215
+ # in /package.json which is outside of the project directory.
216
+ next if path == "file:"
217
+
160
218
  path = path.gsub(PATH_DEPENDENCY_CLEAN_REGEX, "")
161
219
  raise PathDependenciesNotReachable, "#{name} at #{path}" if path.start_with?("/")
162
220
 
@@ -185,6 +243,7 @@ module Dependabot
185
243
 
186
244
  package_json_files.tap { |fs| fs.each { |f| f.support_file = true } }
187
245
  end
246
+ # rubocop:enable Metrics/PerceivedComplexity
188
247
 
189
248
  def path_dependency_details(fetched_files)
190
249
  package_json_path_deps = []
@@ -438,6 +497,7 @@ module Dependabot
438
497
  raise Dependabot::DependencyFileNotParseable, lerna_json.path
439
498
  end
440
499
  end
500
+ # rubocop:enable Metrics/ClassLength
441
501
  end
442
502
  end
443
503
 
@@ -101,8 +101,8 @@ module Dependabot
101
101
  parse_yarn_lock(yarn_lock).each do |req, details|
102
102
  next unless semver_version_for(details["version"])
103
103
  next if alias_package?(req)
104
- next if Experiments.enabled?(:yarn_berry) && workspace_package?(req)
105
- next if Experiments.enabled?(:yarn_berry) && req == "__metadata"
104
+ next if workspace_package?(req)
105
+ next if req == "__metadata"
106
106
 
107
107
  # NOTE: The DependencySet will de-dupe our dependencies, so they
108
108
  # end up unique by name. That's not a perfect representation of
@@ -195,11 +195,7 @@ module Dependabot
195
195
  end
196
196
 
197
197
  def alias_package?(requirement)
198
- if Experiments.enabled?(:yarn_berry)
199
- requirement.match?(/@npm:(.+@(?!npm))/)
200
- else
201
- requirement.include?("@npm:")
202
- end
198
+ requirement.match?(/@npm:(.+@(?!npm))/)
203
199
  end
204
200
 
205
201
  def workspace_package?(requirement)
@@ -3,7 +3,6 @@
3
3
  # See https://docs.npmjs.com/files/package.json for package.json format docs.
4
4
 
5
5
  require "dependabot/dependency"
6
- require "dependabot/experiments"
7
6
  require "dependabot/file_parsers"
8
7
  require "dependabot/file_parsers/base"
9
8
  require "dependabot/shared_helpers"
@@ -105,7 +104,7 @@ module Dependabot
105
104
  # TODO: Handle aliased packages:
106
105
  # https://github.com/dependabot/dependabot-core/pull/1115
107
106
  #
108
- # Ignore dependencies with an alias in the name (only supported by Yarn)
107
+ # Ignore dependencies with an alias in the name
109
108
  # Example: "my-fetch-factory@npm:fetch-factory"
110
109
  return if aliased_package_name?(name)
111
110
 
@@ -250,11 +249,9 @@ module Dependabot
250
249
  )
251
250
  resolved_url = lockfile_details&.fetch("resolved", nil)
252
251
 
253
- if Experiments.enabled?(:yarn_berry) && resolved_url.nil?
254
- resolution = lockfile_details&.fetch("resolution", nil)
255
- package_match = resolution&.match(/__archiveUrl=(?<package_url>.+)/)
256
- resolved_url = CGI.unescape(package_match.named_captures.fetch("package_url", "")) if package_match
257
- end
252
+ resolution = lockfile_details&.fetch("resolution", nil)
253
+ package_match = resolution&.match(/__archiveUrl=(?<package_url>.+)/)
254
+ resolved_url = CGI.unescape(package_match.named_captures.fetch("package_url", "")) if package_match
258
255
 
259
256
  return unless resolved_url
260
257
  return unless resolved_url.start_with?("http")
@@ -337,7 +334,7 @@ module Dependabot
337
334
  dependency_files.
338
335
  select { |f| f.name.end_with?("package.json") }.
339
336
  reject { |f| f.name == "package.json" }.
340
- reject { |f| f.name.include?("node_modules/") if Experiments.enabled?(:yarn_berry) }.
337
+ reject { |f| f.name.include?("node_modules/") }.
341
338
  reject(&:support_file?)
342
339
 
343
340
  [
@@ -205,7 +205,19 @@ module Dependabot
205
205
  "--ignore-scripts",
206
206
  "--package-lock-only"
207
207
  ].join(" ")
208
- SharedHelpers.run_shell_command(command)
208
+
209
+ fingerprint = [
210
+ "npm",
211
+ "install",
212
+ "<install_args>",
213
+ "--force",
214
+ "--dry-run",
215
+ "false",
216
+ "--ignore-scripts",
217
+ "--package-lock-only"
218
+ ].join(" ")
219
+
220
+ SharedHelpers.run_shell_command(command, fingerprint: fingerprint)
209
221
  { lockfile_basename => File.read(lockfile_basename) }
210
222
  end
211
223
 
@@ -223,7 +235,7 @@ module Dependabot
223
235
 
224
236
  def run_npm8_subdependency_updater(sub_dependencies:)
225
237
  dependency_names = sub_dependencies.map(&:name)
226
- SharedHelpers.run_shell_command(NativeHelpers.npm8_subdependency_update_command(dependency_names))
238
+ NativeHelpers.run_npm8_subdependency_update_command(dependency_names)
227
239
  { lockfile_basename => File.read(lockfile_basename) }
228
240
  end
229
241
 
@@ -7,6 +7,7 @@ module Dependabot
7
7
  class FileUpdater
8
8
  # Build a .npmrc file from the lockfile content, credentials, and any
9
9
  # committed .npmrc
10
+ # We should refactor this to use UpdateChecker::RegistryFinder
10
11
  class NpmrcBuilder
11
12
  CENTRAL_REGISTRIES = %w(
12
13
  registry.npmjs.org
@@ -34,6 +35,20 @@ module Dependabot
34
35
  ([initial_content] + credential_lines_for_npmrc).compact.join("\n")
35
36
  end
36
37
 
38
+ # PROXY WORK
39
+ # Yarn allows registries to be defined either in an .npmrc or .yarnrc
40
+ # so we need to parse both files for registry keys
41
+ def yarnrc_content
42
+ initial_content =
43
+ if npmrc_file then complete_yarnrc_from_credentials
44
+ elsif yarnrc_file then build_yarnrc_from_yarnrc
45
+ else
46
+ build_yarnrc_content_from_lockfile
47
+ end
48
+
49
+ initial_content || ""
50
+ end
51
+
37
52
  private
38
53
 
39
54
  attr_reader :dependency_files, :credentials
@@ -45,10 +60,19 @@ module Dependabot
45
60
  registry = global_registry["registry"]
46
61
  registry = "https://#{registry}" unless registry.start_with?("http")
47
62
  "registry = #{registry}\n" \
48
- "#{global_registry_auth_line}" \
63
+ "#{npmrc_global_registry_auth_line}" \
49
64
  "always-auth = true"
50
65
  end
51
66
 
67
+ def build_yarnrc_content_from_lockfile
68
+ return unless yarn_lock || package_lock
69
+ return unless global_registry
70
+
71
+ "registry \"https://#{global_registry['registry']}\"\n" \
72
+ "#{yarnrc_global_registry_auth_line}" \
73
+ "npmAlwaysAuth: true"
74
+ end
75
+
52
76
  def global_registry # rubocop:disable Metrics/PerceivedComplexity
53
77
  return @global_registry if defined?(@global_registry)
54
78
 
@@ -62,6 +86,8 @@ module Dependabot
62
86
  # Check if this registry has already been defined in .npmrc as a scoped registry
63
87
  next false if npmrc_scoped_registries.any? { |sr| sr.include?(cred["registry"]) }
64
88
 
89
+ next false if yarnrc_scoped_registries.any? { |sr| sr.include?(cred["registry"]) }
90
+
65
91
  # If any unscoped URLs include this registry, assume it's global
66
92
  dependency_urls.
67
93
  reject { |u| u.include?("@") || u.include?("%40") }.
@@ -69,7 +95,9 @@ module Dependabot
69
95
  end
70
96
  end
71
97
 
72
- def global_registry_auth_line
98
+ def npmrc_global_registry_auth_line
99
+ # This token is passed in from the Dependabot Config
100
+ # We write it to the .npmrc file so that it can be used by the VulnerabilityAuditor
73
101
  token = global_registry.fetch("token", nil)
74
102
  return "" unless token
75
103
 
@@ -84,6 +112,21 @@ module Dependabot
84
112
  end
85
113
  end
86
114
 
115
+ def yarnrc_global_registry_auth_line
116
+ token = global_registry.fetch("token", nil)
117
+ return "" unless token
118
+
119
+ if token.include?(":")
120
+ encoded_token = Base64.encode64(token).delete("\n")
121
+ "npmAuthIdent: \"#{encoded_token}\"\n"
122
+ elsif Base64.decode64(token).ascii_only? &&
123
+ Base64.decode64(token).include?(":")
124
+ "npmAuthIdent: \"#{token.delete("\n")}\"\n"
125
+ else
126
+ "npmAuthToken: \"#{token}\"\n"
127
+ end
128
+ end
129
+
87
130
  def dependency_urls
88
131
  return @dependency_urls if defined?(@dependency_urls)
89
132
 
@@ -119,15 +162,27 @@ module Dependabot
119
162
  registry = "https://#{registry}" unless registry.start_with?("http")
120
163
  initial_content +
121
164
  "registry = #{registry}\n" \
122
- "#{global_registry_auth_line}" \
165
+ "#{npmrc_global_registry_auth_line}" \
123
166
  "always-auth = true\n"
124
167
  end
125
168
 
169
+ def complete_yarnrc_from_credentials
170
+ initial_content = yarnrc_file.content.
171
+ gsub(/^.*\$\{.*\}.*/, "").strip + "\n"
172
+ return initial_content unless yarn_lock || package_lock
173
+ return initial_content unless global_registry
174
+
175
+ initial_content +
176
+ "registry: \"https://#{global_registry['registry']}\"\n" \
177
+ "#{yarnrc_global_registry_auth_line}" \
178
+ "npmAlwaysAuth: true\n"
179
+ end
180
+
126
181
  def build_npmrc_from_yarnrc
127
182
  yarnrc_global_registry =
128
183
  yarnrc_file.content.
129
184
  lines.find { |line| line.match?(/^\s*registry\s/) }&.
130
- match(/^\s*registry\s+"(?<registry>[^"]+)"/)&.
185
+ match(NpmAndYarn::UpdateChecker::RegistryFinder::YARN_GLOBAL_REGISTRY_REGEX)&.
131
186
  named_captures&.fetch("registry")
132
187
 
133
188
  return "registry = #{yarnrc_global_registry}\n" if yarnrc_global_registry
@@ -135,6 +190,18 @@ module Dependabot
135
190
  build_npmrc_content_from_lockfile
136
191
  end
137
192
 
193
+ def build_yarnrc_from_yarnrc
194
+ yarnrc_global_registry =
195
+ yarnrc_file.content.
196
+ lines.find { |line| line.match?(/^\s*registry\s/) }&.
197
+ match(/^\s*registry\s+"(?<registry>[^"]+)"/)&.
198
+ named_captures&.fetch("registry")
199
+
200
+ return "registry \"#{yarnrc_global_registry}\"\n" if yarnrc_global_registry
201
+
202
+ build_yarnrc_content_from_lockfile
203
+ end
204
+
138
205
  def credential_lines_for_npmrc
139
206
  lines = []
140
207
  registry_credentials.each do |cred|
@@ -173,6 +240,14 @@ module Dependabot
173
240
  filter_map { |line| line.match(SCOPED_REGISTRY)&.named_captures&.fetch("registry") }
174
241
  end
175
242
 
243
+ def yarnrc_scoped_registries
244
+ return [] unless yarnrc_file
245
+
246
+ @yarnrc_scoped_registries ||=
247
+ yarnrc_file.content.lines.select { |line| line.match?(SCOPED_REGISTRY) }.
248
+ filter_map { |line| line.match(SCOPED_REGISTRY)&.named_captures&.fetch("registry") }
249
+ end
250
+
176
251
  # rubocop:disable Metrics/PerceivedComplexity
177
252
  def registry_scopes(registry)
178
253
  # Central registries don't just apply to scopes
@@ -9,7 +9,6 @@ require "dependabot/npm_and_yarn/update_checker/registry_finder"
9
9
  require "dependabot/npm_and_yarn/native_helpers"
10
10
  require "dependabot/shared_helpers"
11
11
  require "dependabot/errors"
12
- require "dependabot/experiments"
13
12
 
14
13
  # rubocop:disable Metrics/ClassLength
15
14
  module Dependabot
@@ -55,7 +54,7 @@ module Dependabot
55
54
  def updated_yarn_lock(yarn_lock)
56
55
  base_dir = dependency_files.first.directory
57
56
  SharedHelpers.in_a_temporary_repo_directory(base_dir, repo_contents_path) do
58
- write_temporary_dependency_files
57
+ write_temporary_dependency_files(yarn_lock)
59
58
  lockfile_name = Pathname.new(yarn_lock.name).basename.to_s
60
59
  path = Pathname.new(yarn_lock.name).dirname.to_s
61
60
  updated_files = run_current_yarn_update(
@@ -107,7 +106,7 @@ module Dependabot
107
106
  SharedHelpers.with_git_configured(credentials: credentials) do
108
107
  Dir.chdir(path) do
109
108
  if top_level_dependency_updates.any?
110
- if yarn_berry?(yarn_lock)
109
+ if Helpers.yarn_berry?(yarn_lock)
111
110
  run_yarn_berry_top_level_updater(top_level_dependency_updates: top_level_dependency_updates,
112
111
  yarn_lock: yarn_lock)
113
112
  else
@@ -116,7 +115,7 @@ module Dependabot
116
115
  top_level_dependency_updates: top_level_dependency_updates
117
116
  )
118
117
  end
119
- elsif yarn_berry?(yarn_lock)
118
+ elsif Helpers.yarn_berry?(yarn_lock)
120
119
  run_yarn_berry_subdependency_updater(yarn_lock: yarn_lock)
121
120
  else
122
121
  run_yarn_subdependency_updater(yarn_lock: yarn_lock)
@@ -143,30 +142,25 @@ module Dependabot
143
142
 
144
143
  # rubocop:enable Metrics/PerceivedComplexity
145
144
 
146
- def yarn_berry?(yarn_lock)
147
- return false unless Experiments.enabled?(:yarn_berry)
148
-
149
- yaml = YAML.safe_load(yarn_lock.content)
150
- yaml.key?("__metadata")
151
- rescue StandardError
152
- false
153
- end
154
-
155
145
  def run_yarn_berry_top_level_updater(top_level_dependency_updates:, yarn_lock:)
146
+ write_temporary_dependency_files(yarn_lock)
156
147
  # If the requirements have changed, it means we've updated the
157
148
  # package.json file(s), and we can just run yarn install to get the
158
149
  # lockfile in the right state. Otherwise we'll need to manually update
159
150
  # the lockfile.
160
151
 
161
- command = if top_level_dependency_updates.all? { |dep| requirements_changed?(dep[:name]) }
162
- "yarn install"
163
- else
164
- updates = top_level_dependency_updates.collect do |dep|
165
- dep[:requirements].map { |req| "#{dep[:name]}@#{req[:requirement]}" }.join(" ")
166
- end
167
- "yarn up #{updates.join(' ')}"
168
- end
169
- Helpers.run_yarn_commands(command)
152
+ if top_level_dependency_updates.all? { |dep| requirements_changed?(dep[:name]) }
153
+ Helpers.run_yarn_command("yarn install #{yarn_berry_args}".strip)
154
+ else
155
+ updates = top_level_dependency_updates.collect do |dep|
156
+ dep[:name]
157
+ end
158
+
159
+ Helpers.run_yarn_command(
160
+ "yarn up -R #{updates.join(' ')} #{yarn_berry_args}".strip,
161
+ fingerprint: "yarn up -R <dependency_names> #{yarn_berry_args}".strip
162
+ )
163
+ end
170
164
  { yarn_lock.name => File.read(yarn_lock.name) }
171
165
  end
172
166
 
@@ -179,14 +173,20 @@ module Dependabot
179
173
  dep = sub_dependencies.first
180
174
  update = "#{dep.name}@#{dep.version}"
181
175
 
182
- Helpers.run_yarn_commands(
183
- "yarn add #{update}",
184
- "yarn dedupe #{dep.name}",
185
- "yarn remove #{dep.name}"
186
- )
176
+ commands = [
177
+ ["yarn add #{update} #{yarn_berry_args}".strip, "yarn add <update> #{yarn_berry_args}".strip],
178
+ ["yarn dedupe #{dep.name} #{yarn_berry_args}".strip, "yarn dedupe <dep_name> #{yarn_berry_args}".strip],
179
+ ["yarn remove #{dep.name} #{yarn_berry_args}".strip, "yarn remove <dep_name> #{yarn_berry_args}".strip]
180
+ ]
181
+
182
+ Helpers.run_yarn_commands(*commands)
187
183
  { yarn_lock.name => File.read(yarn_lock.name) }
188
184
  end
189
185
 
186
+ def yarn_berry_args
187
+ Helpers.yarn_berry_args
188
+ end
189
+
190
190
  def run_yarn_top_level_updater(top_level_dependency_updates:)
191
191
  SharedHelpers.run_helper_subprocess(
192
192
  command: NativeHelpers.helper_path,
@@ -316,7 +316,7 @@ module Dependabot
316
316
  begin
317
317
  base_dir = dependency_files.first.directory
318
318
  SharedHelpers.in_a_temporary_repo_directory(base_dir, repo_contents_path) do
319
- write_temporary_dependency_files(update_package_json: false)
319
+ write_temporary_dependency_files(yarn_lock, update_package_json: false)
320
320
  path = Pathname.new(yarn_lock.name).dirname.to_s
321
321
  run_previous_yarn_update(path: path, yarn_lock: yarn_lock)
322
322
  end
@@ -336,11 +336,15 @@ module Dependabot
336
336
  end
337
337
  end
338
338
 
339
- def write_temporary_dependency_files(update_package_json: true)
339
+ def write_temporary_dependency_files(yarn_lock, update_package_json: true)
340
340
  write_lockfiles
341
341
 
342
- File.write(".npmrc", npmrc_content)
343
- File.write(".yarnrc", yarnrc_content) if yarnrc_specifies_npm_reg?
342
+ if Helpers.yarn_berry?(yarn_lock)
343
+ File.write(".yarnrc.yml", yarnrc_yml_content) if yarnrc_yml_file
344
+ else
345
+ File.write(".npmrc", npmrc_content) unless Helpers.yarn_berry?(yarn_lock)
346
+ File.write(".yarnrc", yarnrc_content) if yarnrc_specifies_private_reg?
347
+ end
344
348
 
345
349
  package_files.each do |file|
346
350
  path = file.name
@@ -526,7 +530,7 @@ module Dependabot
526
530
  npmrc_content.match?(/^package-lock\s*=\s*false/)
527
531
  end
528
532
 
529
- def yarnrc_specifies_npm_reg?
533
+ def yarnrc_specifies_private_reg?
530
534
  return false unless yarnrc_file
531
535
 
532
536
  regex = UpdateChecker::RegistryFinder::YARN_GLOBAL_REGISTRY_REGEX
@@ -539,11 +543,16 @@ module Dependabot
539
543
 
540
544
  return false unless yarnrc_global_registry
541
545
 
542
- URI(yarnrc_global_registry).host == "registry.npmjs.org"
546
+ UpdateChecker::RegistryFinder::CENTRAL_REGISTRIES.any? do |r|
547
+ r.include?(URI(yarnrc_global_registry).host)
548
+ end
543
549
  end
544
550
 
545
551
  def yarnrc_content
546
- 'registry "https://registry.npmjs.org"'
552
+ NpmrcBuilder.new(
553
+ credentials: credentials,
554
+ dependency_files: dependency_files
555
+ ).yarnrc_content
547
556
  end
548
557
 
549
558
  def sanitized_package_json_content(content)
@@ -583,6 +592,10 @@ module Dependabot
583
592
  def yarnrc_yml_file
584
593
  dependency_files.find { |f| f.name.end_with?(".yarnrc.yml") }
585
594
  end
595
+
596
+ def yarnrc_yml_content
597
+ yarnrc_yml_file.content
598
+ end
586
599
  end
587
600
  end
588
601
  end
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "dependabot/experiments"
4
3
  require "dependabot/file_updaters"
5
4
  require "dependabot/file_updaters/base"
6
5
  require "dependabot/file_updaters/vendor_updater"
@@ -55,39 +54,35 @@ module Dependabot
55
54
  )
56
55
  end
57
56
 
57
+ vendor_updated_files(updated_files)
58
+ end
59
+
60
+ private
61
+
62
+ def vendor_updated_files(updated_files)
58
63
  base_dir = updated_files.first.directory
64
+ pnp_updater.updated_vendor_cache_files(base_directory: base_dir).each do |file|
65
+ updated_files << file if file.name == ".pnp.cjs" || file.name == ".pnp.data.json"
66
+ end
59
67
  vendor_updater.updated_vendor_cache_files(base_directory: base_dir).each { |file| updated_files << file }
60
68
  install_state_updater.updated_vendor_cache_files(base_directory: base_dir).each do |file|
61
69
  updated_files << file
62
70
  end
63
- pnp_updater.updated_vendor_cache_files(base_directory: base_dir).each do |file|
64
- updated_files << file if file.name == ".pnp.cjs" || file.name == ".pnp.data.json"
65
- end
66
71
 
67
72
  updated_files
68
73
  end
69
74
 
70
- private
71
-
72
75
  # Dynamically fetch the vendor cache folder from yarn
73
76
  def vendor_cache_dir
74
77
  return @vendor_cache_dir if defined?(@vendor_cache_dir)
75
78
 
76
- @vendor_cache_dir = if File.exist?(".yarnrc.yml")
77
- YAML.load_file(".yarnrc.yml").fetch("cacheFolder", "./.yarn/cache")
78
- else
79
- "./.yarn/cache"
80
- end
79
+ @vendor_cache_dir = Helpers.fetch_yarnrc_yml_value("cacheFolder", "./.yarn/cache")
81
80
  end
82
81
 
83
82
  def install_state_path
84
83
  return @install_state_path if defined?(@install_state_path)
85
84
 
86
- @install_state_path = if File.exist?(".yarnrc.yml")
87
- YAML.load_file(".yarnrc.yml").fetch("installStatePath", "./.yarn/install-state.gz")
88
- else
89
- "./.yarn/install-state.gz"
90
- end
85
+ @install_state_path = Helpers.fetch_yarnrc_yml_value("installStatePath", "./.yarn/install-state.gz")
91
86
  end
92
87
 
93
88
  def vendor_updater