dependabot-npm_and_yarn 0.213.0 → 0.214.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 +886 -851
- 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/npmrc_builder.rb +79 -4
- data/lib/dependabot/npm_and_yarn/file_updater/yarn_lockfile_updater.rb +31 -26
- data/lib/dependabot/npm_and_yarn/file_updater.rb +15 -17
- data/lib/dependabot/npm_and_yarn/helpers.rb +61 -15
- 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 +7 -14
- data/lib/dependabot/npm_and_yarn/update_checker/version_resolver.rb +71 -27
- data/lib/dependabot/npm_and_yarn/update_checker.rb +5 -3
- 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.3",
|
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.28.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
|
[
|
@@ -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,28 +142,21 @@ 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
152
|
command = if top_level_dependency_updates.all? { |dep| requirements_changed?(dep[:name]) }
|
162
|
-
"yarn install"
|
153
|
+
"yarn install #{Helpers.yarn_berry_args}".strip
|
163
154
|
else
|
164
155
|
updates = top_level_dependency_updates.collect do |dep|
|
165
|
-
dep[:
|
156
|
+
dep[:name]
|
166
157
|
end
|
167
|
-
|
158
|
+
|
159
|
+
"yarn up -R #{updates.join(' ')} #{Helpers.yarn_berry_args}".strip
|
168
160
|
end
|
169
161
|
Helpers.run_yarn_commands(command)
|
170
162
|
{ yarn_lock.name => File.read(yarn_lock.name) }
|
@@ -180,9 +172,9 @@ module Dependabot
|
|
180
172
|
update = "#{dep.name}@#{dep.version}"
|
181
173
|
|
182
174
|
Helpers.run_yarn_commands(
|
183
|
-
"yarn add #{update}",
|
184
|
-
"yarn dedupe #{dep.name}",
|
185
|
-
"yarn remove #{dep.name}"
|
175
|
+
"yarn add #{update} #{Helpers.yarn_berry_args}".strip,
|
176
|
+
"yarn dedupe #{dep.name} #{Helpers.yarn_berry_args}".strip,
|
177
|
+
"yarn remove #{dep.name} #{Helpers.yarn_berry_args}".strip
|
186
178
|
)
|
187
179
|
{ yarn_lock.name => File.read(yarn_lock.name) }
|
188
180
|
end
|
@@ -316,7 +308,7 @@ module Dependabot
|
|
316
308
|
begin
|
317
309
|
base_dir = dependency_files.first.directory
|
318
310
|
SharedHelpers.in_a_temporary_repo_directory(base_dir, repo_contents_path) do
|
319
|
-
write_temporary_dependency_files(update_package_json: false)
|
311
|
+
write_temporary_dependency_files(yarn_lock, update_package_json: false)
|
320
312
|
path = Pathname.new(yarn_lock.name).dirname.to_s
|
321
313
|
run_previous_yarn_update(path: path, yarn_lock: yarn_lock)
|
322
314
|
end
|
@@ -336,11 +328,15 @@ module Dependabot
|
|
336
328
|
end
|
337
329
|
end
|
338
330
|
|
339
|
-
def write_temporary_dependency_files(update_package_json: true)
|
331
|
+
def write_temporary_dependency_files(yarn_lock, update_package_json: true)
|
340
332
|
write_lockfiles
|
341
333
|
|
342
|
-
|
343
|
-
|
334
|
+
if Helpers.yarn_berry?(yarn_lock)
|
335
|
+
File.write(".yarnrc.yml", yarnrc_yml_content) if yarnrc_yml_file
|
336
|
+
else
|
337
|
+
File.write(".npmrc", npmrc_content) unless Helpers.yarn_berry?(yarn_lock)
|
338
|
+
File.write(".yarnrc", yarnrc_content) if yarnrc_specifies_private_reg?
|
339
|
+
end
|
344
340
|
|
345
341
|
package_files.each do |file|
|
346
342
|
path = file.name
|
@@ -526,7 +522,7 @@ module Dependabot
|
|
526
522
|
npmrc_content.match?(/^package-lock\s*=\s*false/)
|
527
523
|
end
|
528
524
|
|
529
|
-
def
|
525
|
+
def yarnrc_specifies_private_reg?
|
530
526
|
return false unless yarnrc_file
|
531
527
|
|
532
528
|
regex = UpdateChecker::RegistryFinder::YARN_GLOBAL_REGISTRY_REGEX
|
@@ -539,11 +535,16 @@ module Dependabot
|
|
539
535
|
|
540
536
|
return false unless yarnrc_global_registry
|
541
537
|
|
542
|
-
|
538
|
+
UpdateChecker::RegistryFinder::CENTRAL_REGISTRIES.any? do |r|
|
539
|
+
r.include?(URI(yarnrc_global_registry).host)
|
540
|
+
end
|
543
541
|
end
|
544
542
|
|
545
543
|
def yarnrc_content
|
546
|
-
|
544
|
+
NpmrcBuilder.new(
|
545
|
+
credentials: credentials,
|
546
|
+
dependency_files: dependency_files
|
547
|
+
).yarnrc_content
|
547
548
|
end
|
548
549
|
|
549
550
|
def sanitized_package_json_content(content)
|
@@ -583,6 +584,10 @@ module Dependabot
|
|
583
584
|
def yarnrc_yml_file
|
584
585
|
dependency_files.find { |f| f.name.end_with?(".yarnrc.yml") }
|
585
586
|
end
|
587
|
+
|
588
|
+
def yarnrc_yml_content
|
589
|
+
yarnrc_yml_file.content
|
590
|
+
end
|
586
591
|
end
|
587
592
|
end
|
588
593
|
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,38 @@ 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
|
59
|
-
vendor_updater.updated_vendor_cache_files(base_directory: base_dir).each { |file| updated_files << file }
|
60
|
-
install_state_updater.updated_vendor_cache_files(base_directory: base_dir).each do |file|
|
61
|
-
updated_files << file
|
62
|
-
end
|
63
64
|
pnp_updater.updated_vendor_cache_files(base_directory: base_dir).each do |file|
|
64
65
|
updated_files << file if file.name == ".pnp.cjs" || file.name == ".pnp.data.json"
|
65
66
|
end
|
67
|
+
# updated .pnp.cjs means zero install, include cache
|
68
|
+
if updated_files.find { |f| f.name == ".pnp.cjs" }
|
69
|
+
vendor_updater.updated_vendor_cache_files(base_directory: base_dir).each { |file| updated_files << file }
|
70
|
+
end
|
71
|
+
install_state_updater.updated_vendor_cache_files(base_directory: base_dir).each do |file|
|
72
|
+
updated_files << file
|
73
|
+
end
|
66
74
|
|
67
75
|
updated_files
|
68
76
|
end
|
69
77
|
|
70
|
-
private
|
71
|
-
|
72
78
|
# Dynamically fetch the vendor cache folder from yarn
|
73
79
|
def vendor_cache_dir
|
74
80
|
return @vendor_cache_dir if defined?(@vendor_cache_dir)
|
75
81
|
|
76
|
-
@vendor_cache_dir =
|
77
|
-
YAML.load_file(".yarnrc.yml").fetch("cacheFolder", "./.yarn/cache")
|
78
|
-
else
|
79
|
-
"./.yarn/cache"
|
80
|
-
end
|
82
|
+
@vendor_cache_dir = Helpers.fetch_yarnrc_yml_value("cacheFolder", "./.yarn/cache")
|
81
83
|
end
|
82
84
|
|
83
85
|
def install_state_path
|
84
86
|
return @install_state_path if defined?(@install_state_path)
|
85
87
|
|
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
|
88
|
+
@install_state_path = Helpers.fetch_yarnrc_yml_value("installStatePath", "./.yarn/install-state.gz")
|
91
89
|
end
|
92
90
|
|
93
91
|
def vendor_updater
|