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.
- checksums.yaml +4 -4
- data/helpers/package-lock.json +892 -857
- data/helpers/package.json +4 -4
- data/helpers/test/npm6/fixtures/conflicting-dependency-parser/deeply-nested/package-lock.json +3 -3
- data/lib/dependabot/npm_and_yarn/file_fetcher/path_dependency_builder.rb +11 -2
- data/lib/dependabot/npm_and_yarn/file_fetcher.rb +61 -1
- data/lib/dependabot/npm_and_yarn/file_parser/lockfile_parser.rb +3 -7
- data/lib/dependabot/npm_and_yarn/file_parser.rb +5 -8
- data/lib/dependabot/npm_and_yarn/file_updater/npm_lockfile_updater.rb +14 -2
- data/lib/dependabot/npm_and_yarn/file_updater/npmrc_builder.rb +79 -4
- data/lib/dependabot/npm_and_yarn/file_updater/yarn_lockfile_updater.rb +47 -34
- data/lib/dependabot/npm_and_yarn/file_updater.rb +11 -16
- data/lib/dependabot/npm_and_yarn/helpers.rb +69 -16
- data/lib/dependabot/npm_and_yarn/native_helpers.rb +15 -2
- data/lib/dependabot/npm_and_yarn/update_checker/dependency_files_builder.rb +43 -1
- data/lib/dependabot/npm_and_yarn/update_checker/latest_version_finder.rb +7 -13
- data/lib/dependabot/npm_and_yarn/update_checker/registry_finder.rb +10 -4
- data/lib/dependabot/npm_and_yarn/update_checker/subdependency_version_resolver.rb +10 -16
- data/lib/dependabot/npm_and_yarn/update_checker/version_resolver.rb +71 -27
- data/lib/dependabot/npm_and_yarn/update_checker.rb +7 -27
- data/lib/dependabot/npm_and_yarn.rb +2 -0
- metadata +8 -9
- data/lib/dependabot/npm_and_yarn/file_parser/yarn_lockfile_parser.rb +0 -59
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.
|
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.
|
20
|
+
"eslint": "^8.29.0",
|
21
21
|
"eslint-config-prettier": "^8.5.0",
|
22
|
-
"jest": "^29.
|
23
|
-
"prettier": "^2.
|
22
|
+
"jest": "^29.3.1",
|
23
|
+
"prettier": "^2.8.0",
|
24
24
|
"rimraf": "^3.0.2"
|
25
25
|
}
|
26
26
|
}
|
data/helpers/test/npm6/fixtures/conflicting-dependency-parser/deeply-nested/package-lock.json
CHANGED
@@ -446,9 +446,9 @@
|
|
446
446
|
}
|
447
447
|
},
|
448
448
|
"minimatch": {
|
449
|
-
"version": "3.
|
450
|
-
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.
|
451
|
-
"integrity": "sha512-
|
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
|
-
|
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
|
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
|
105
|
-
next if
|
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
|
-
|
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
|
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
|
-
|
254
|
-
|
255
|
-
|
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/")
|
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
|
-
|
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
|
-
|
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
|
-
"#{
|
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
|
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
|
-
"#{
|
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(
|
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
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
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
|
-
|
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
|
-
|
343
|
-
|
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
|
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
|
-
|
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
|
-
|
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 =
|
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 =
|
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
|