dependabot-npm_and_yarn 0.212.0 → 0.213.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/helpers/.eslintrc +1 -1
- data/helpers/README.md +2 -2
- data/helpers/lib/npm/vulnerability-auditor.js +7 -7
- data/helpers/package-lock.json +2585 -2386
- data/helpers/package.json +4 -4
- data/lib/dependabot/npm_and_yarn/file_fetcher.rb +30 -5
- data/lib/dependabot/npm_and_yarn/file_parser/lockfile_parser.rb +19 -4
- data/lib/dependabot/npm_and_yarn/file_parser.rb +17 -5
- data/lib/dependabot/npm_and_yarn/file_updater/npm_lockfile_updater.rb +35 -21
- data/lib/dependabot/npm_and_yarn/file_updater/npmrc_builder.rb +7 -3
- data/lib/dependabot/npm_and_yarn/file_updater/package_json_updater.rb +2 -2
- data/lib/dependabot/npm_and_yarn/file_updater/yarn_lockfile_updater.rb +83 -24
- data/lib/dependabot/npm_and_yarn/file_updater.rb +54 -0
- data/lib/dependabot/npm_and_yarn/helpers.rb +48 -0
- data/lib/dependabot/npm_and_yarn/package_name.rb +2 -2
- data/lib/dependabot/npm_and_yarn/requirement.rb +3 -3
- data/lib/dependabot/npm_and_yarn/update_checker/latest_version_finder.rb +6 -1
- data/lib/dependabot/npm_and_yarn/update_checker/library_detector.rb +16 -3
- data/lib/dependabot/npm_and_yarn/update_checker/registry_finder.rb +67 -19
- data/lib/dependabot/npm_and_yarn/update_checker/requirements_updater.rb +3 -4
- data/lib/dependabot/npm_and_yarn/update_checker/subdependency_version_resolver.rb +23 -1
- data/lib/dependabot/npm_and_yarn/update_checker/version_resolver.rb +3 -3
- data/lib/dependabot/npm_and_yarn/update_checker/vulnerability_auditor.rb +33 -8
- data/lib/dependabot/npm_and_yarn/update_checker.rb +72 -19
- data/lib/dependabot/npm_and_yarn/version.rb +1 -1
- metadata +13 -55
data/helpers/package.json
CHANGED
@@ -10,16 +10,16 @@
|
|
10
10
|
},
|
11
11
|
"dependencies": {
|
12
12
|
"@dependabot/yarn-lib": "^1.22.19",
|
13
|
-
"@npmcli/arborist": "^
|
13
|
+
"@npmcli/arborist": "^6.0.0",
|
14
14
|
"detect-indent": "^6.1.0",
|
15
15
|
"nock": "^13.2.9",
|
16
16
|
"npm": "6.14.17",
|
17
|
-
"semver": "^7.3.
|
17
|
+
"semver": "^7.3.8"
|
18
18
|
},
|
19
19
|
"devDependencies": {
|
20
|
-
"eslint": "^8.
|
20
|
+
"eslint": "^8.26.0",
|
21
21
|
"eslint-config-prettier": "^8.5.0",
|
22
|
-
"jest": "^
|
22
|
+
"jest": "^29.2.1",
|
23
23
|
"prettier": "^2.7.1",
|
24
24
|
"rimraf": "^3.0.2"
|
25
25
|
}
|
@@ -19,9 +19,8 @@ module Dependabot
|
|
19
19
|
# when it specifies a path. Only include Yarn "link:"'s that start with a
|
20
20
|
# path and ignore symlinked package names that have been registered with
|
21
21
|
# "yarn link", e.g. "link:react"
|
22
|
-
PATH_DEPENDENCY_STARTS =
|
23
|
-
|
24
|
-
PATH_DEPENDENCY_CLEAN_REGEX = /^file:|^link:/.freeze
|
22
|
+
PATH_DEPENDENCY_STARTS = %w(file: link:. link:/ link:~/ / ./ ../ ~/).freeze
|
23
|
+
PATH_DEPENDENCY_CLEAN_REGEX = /^file:|^link:/
|
25
24
|
|
26
25
|
def self.required_files_in?(filenames)
|
27
26
|
filenames.include?("package.json")
|
@@ -42,6 +41,7 @@ module Dependabot
|
|
42
41
|
fetched_files << lerna_json if lerna_json
|
43
42
|
fetched_files << npmrc if npmrc
|
44
43
|
fetched_files << yarnrc if yarnrc
|
44
|
+
fetched_files << yarnrc_yml if yarnrc_yml
|
45
45
|
fetched_files += workspace_package_jsons
|
46
46
|
fetched_files += lerna_packages
|
47
47
|
fetched_files += path_dependencies(fetched_files)
|
@@ -53,8 +53,8 @@ module Dependabot
|
|
53
53
|
def instrument_package_manager_version
|
54
54
|
package_managers = {}
|
55
55
|
|
56
|
-
package_managers["npm"] =
|
57
|
-
package_managers["yarn"] =
|
56
|
+
package_managers["npm"] = Helpers.npm_version_numeric(package_lock.content) if package_lock
|
57
|
+
package_managers["yarn"] = yarn_version if yarn_version
|
58
58
|
package_managers["shrinkwrap"] = 1 if shrinkwrap
|
59
59
|
|
60
60
|
Dependabot.instrument(
|
@@ -64,6 +64,22 @@ module Dependabot
|
|
64
64
|
)
|
65
65
|
end
|
66
66
|
|
67
|
+
def yarn_version
|
68
|
+
return @yarn_version if defined?(@yarn_version)
|
69
|
+
|
70
|
+
package = JSON.parse(package_json.content)
|
71
|
+
if Experiments.enabled?(:yarn_berry) && (package_manager = package.fetch("packageManager", nil))
|
72
|
+
get_yarn_version_from_package_json(package_manager)
|
73
|
+
elsif yarn_lock
|
74
|
+
1
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def get_yarn_version_from_package_json(package_manager)
|
79
|
+
version_match = package_manager.match(/yarn@(?<version>\d+.\d+.\d+)/)
|
80
|
+
version_match&.named_captures&.fetch("version", nil)
|
81
|
+
end
|
82
|
+
|
67
83
|
def package_json
|
68
84
|
@package_json ||= fetch_file_from_host("package.json")
|
69
85
|
end
|
@@ -118,6 +134,11 @@ module Dependabot
|
|
118
134
|
@yarnrc
|
119
135
|
end
|
120
136
|
|
137
|
+
def yarnrc_yml
|
138
|
+
@yarnrc_yml ||= fetch_file_if_present(".yarnrc.yml")&.
|
139
|
+
tap { |f| f.support_file = true }
|
140
|
+
end
|
141
|
+
|
121
142
|
def lerna_json
|
122
143
|
@lerna_json ||= fetch_file_if_present("lerna.json")&.
|
123
144
|
tap { |f| f.support_file = true }
|
@@ -137,6 +158,8 @@ module Dependabot
|
|
137
158
|
|
138
159
|
path_dependency_details(fetched_files).each do |name, path|
|
139
160
|
path = path.gsub(PATH_DEPENDENCY_CLEAN_REGEX, "")
|
161
|
+
raise PathDependenciesNotReachable, "#{name} at #{path}" if path.start_with?("/")
|
162
|
+
|
140
163
|
filename = path
|
141
164
|
# NPM/Yarn support loading path dependencies from tarballs:
|
142
165
|
# https://docs.npmjs.com/cli/pack.html
|
@@ -212,6 +235,8 @@ module Dependabot
|
|
212
235
|
select { |_, v| v.is_a?(String) && v.start_with?(*path_starts) }.
|
213
236
|
map do |name, path|
|
214
237
|
path = path.gsub(PATH_DEPENDENCY_CLEAN_REGEX, "")
|
238
|
+
raise PathDependenciesNotReachable, "#{name} at #{path}" if path.start_with?("/")
|
239
|
+
|
215
240
|
path = File.join(current_dir, path) unless current_dir.nil?
|
216
241
|
[name, Pathname.new(path).cleanpath.to_path]
|
217
242
|
end
|
@@ -12,12 +12,17 @@ module Dependabot
|
|
12
12
|
@dependency_files = dependency_files
|
13
13
|
end
|
14
14
|
|
15
|
-
def
|
15
|
+
def parse_set
|
16
16
|
dependency_set = Dependabot::NpmAndYarn::FileParser::DependencySet.new
|
17
17
|
dependency_set += yarn_lock_dependencies if yarn_locks.any?
|
18
18
|
dependency_set += package_lock_dependencies if package_locks.any?
|
19
19
|
dependency_set += shrinkwrap_dependencies if shrinkwraps.any?
|
20
|
-
|
20
|
+
|
21
|
+
dependency_set
|
22
|
+
end
|
23
|
+
|
24
|
+
def parse
|
25
|
+
Helpers.dependencies_with_all_versions_metadata(parse_set)
|
21
26
|
end
|
22
27
|
|
23
28
|
def lockfile_details(dependency_name:, requirement:, manifest_name:)
|
@@ -77,7 +82,7 @@ module Dependabot
|
|
77
82
|
details_candidates.first.last
|
78
83
|
else
|
79
84
|
details_candidates.find do |k, _|
|
80
|
-
k.
|
85
|
+
k.scan(/(?<=\w)\@(?:npm:)?([^\s,]+)/).flatten.include?(requirement)
|
81
86
|
end&.last
|
82
87
|
end
|
83
88
|
end
|
@@ -96,6 +101,8 @@ module Dependabot
|
|
96
101
|
parse_yarn_lock(yarn_lock).each do |req, details|
|
97
102
|
next unless semver_version_for(details["version"])
|
98
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"
|
99
106
|
|
100
107
|
# NOTE: The DependencySet will de-dupe our dependencies, so they
|
101
108
|
# end up unique by name. That's not a perfect representation of
|
@@ -188,7 +195,15 @@ module Dependabot
|
|
188
195
|
end
|
189
196
|
|
190
197
|
def alias_package?(requirement)
|
191
|
-
|
198
|
+
if Experiments.enabled?(:yarn_berry)
|
199
|
+
requirement.match?(/@npm:(.+@(?!npm))/)
|
200
|
+
else
|
201
|
+
requirement.include?("@npm:")
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
def workspace_package?(requirement)
|
206
|
+
requirement.include?("@workspace:")
|
192
207
|
end
|
193
208
|
|
194
209
|
def parse_package_lock(package_lock)
|
@@ -3,9 +3,11 @@
|
|
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"
|
6
7
|
require "dependabot/file_parsers"
|
7
8
|
require "dependabot/file_parsers/base"
|
8
9
|
require "dependabot/shared_helpers"
|
10
|
+
require "dependabot/npm_and_yarn/helpers"
|
9
11
|
require "dependabot/npm_and_yarn/native_helpers"
|
10
12
|
require "dependabot/npm_and_yarn/version"
|
11
13
|
require "dependabot/git_metadata_fetcher"
|
@@ -29,13 +31,14 @@ module Dependabot
|
|
29
31
|
(?:\#(?=[\^~=<>*])(?<semver>.+))|
|
30
32
|
(?:\#(?<ref>.+))
|
31
33
|
)?$
|
32
|
-
}ix
|
34
|
+
}ix
|
33
35
|
|
34
36
|
def parse
|
35
37
|
dependency_set = DependencySet.new
|
36
38
|
dependency_set += manifest_dependencies
|
37
39
|
dependency_set += lockfile_dependencies
|
38
|
-
|
40
|
+
|
41
|
+
dependencies = Helpers.dependencies_with_all_versions_metadata(dependency_set)
|
39
42
|
|
40
43
|
# TODO: Currently, Dependabot can't handle dependencies that have both
|
41
44
|
# a git source *and* a non-git source. Fix that!
|
@@ -84,7 +87,7 @@ module Dependabot
|
|
84
87
|
end
|
85
88
|
|
86
89
|
def lockfile_dependencies
|
87
|
-
|
90
|
+
lockfile_parser.parse_set
|
88
91
|
end
|
89
92
|
|
90
93
|
def build_dependency(file:, type:, name:, requirement:)
|
@@ -94,6 +97,7 @@ module Dependabot
|
|
94
97
|
manifest_name: file.name
|
95
98
|
)
|
96
99
|
version = version_for(name, requirement, file.name)
|
100
|
+
|
97
101
|
return if lockfile_details && !version
|
98
102
|
return if ignore_requirement?(requirement)
|
99
103
|
return if workspace_package_names.include?(name)
|
@@ -239,11 +243,18 @@ module Dependabot
|
|
239
243
|
def source_for(name, requirement, manifest_name)
|
240
244
|
return git_source_for(requirement) if git_url?(requirement)
|
241
245
|
|
242
|
-
|
246
|
+
lockfile_details = lockfile_parser.lockfile_details(
|
243
247
|
dependency_name: name,
|
244
248
|
requirement: requirement,
|
245
249
|
manifest_name: manifest_name
|
246
|
-
)
|
250
|
+
)
|
251
|
+
resolved_url = lockfile_details&.fetch("resolved", nil)
|
252
|
+
|
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
|
247
258
|
|
248
259
|
return unless resolved_url
|
249
260
|
return unless resolved_url.start_with?("http")
|
@@ -326,6 +337,7 @@ module Dependabot
|
|
326
337
|
dependency_files.
|
327
338
|
select { |f| f.name.end_with?("package.json") }.
|
328
339
|
reject { |f| f.name == "package.json" }.
|
340
|
+
reject { |f| f.name.include?("node_modules/") if Experiments.enabled?(:yarn_berry) }.
|
329
341
|
reject(&:support_file?)
|
330
342
|
|
331
343
|
[
|
@@ -34,21 +34,21 @@ module Dependabot
|
|
34
34
|
|
35
35
|
attr_reader :lockfile, :dependencies, :dependency_files, :credentials
|
36
36
|
|
37
|
-
UNREACHABLE_GIT = /fatal: repository '(?<url>.*)' not found
|
38
|
-
FORBIDDEN_GIT = /fatal: Authentication failed for '(?<url>.*)'
|
39
|
-
FORBIDDEN_PACKAGE = %r{(?<package_req>[^/]+) - (Forbidden|Unauthorized)}
|
37
|
+
UNREACHABLE_GIT = /fatal: repository '(?<url>.*)' not found/
|
38
|
+
FORBIDDEN_GIT = /fatal: Authentication failed for '(?<url>.*)'/
|
39
|
+
FORBIDDEN_PACKAGE = %r{(?<package_req>[^/]+) - (Forbidden|Unauthorized)}
|
40
40
|
FORBIDDEN_PACKAGE_403 = %r{^403\sForbidden\s
|
41
|
-
-\sGET\shttps?://(?<source>[^/]+)/(?<package_req>[^/\s]+)}x
|
42
|
-
MISSING_PACKAGE = %r{(?<package_req>[^/]+) - Not found}
|
43
|
-
INVALID_PACKAGE = /Can't install (?<package_req>.*): Missing
|
41
|
+
-\sGET\shttps?://(?<source>[^/]+)/(?<package_req>[^/\s]+)}x
|
42
|
+
MISSING_PACKAGE = %r{(?<package_req>[^/]+) - Not found}
|
43
|
+
INVALID_PACKAGE = /Can't install (?<package_req>.*): Missing/
|
44
44
|
|
45
45
|
# TODO: look into fixing this in npm, seems like a bug in the git
|
46
46
|
# downloader introduced in npm 7
|
47
47
|
#
|
48
48
|
# NOTE: error message returned from arborist/npm 8 when trying to
|
49
49
|
# fetching a invalid/non-existent git ref
|
50
|
-
NPM8_MISSING_GIT_REF = /already exists and is not an empty directory
|
51
|
-
NPM6_MISSING_GIT_REF = /did not match any file\(s\) known to git
|
50
|
+
NPM8_MISSING_GIT_REF = /already exists and is not an empty directory/
|
51
|
+
NPM6_MISSING_GIT_REF = /did not match any file\(s\) known to git/
|
52
52
|
|
53
53
|
def updated_lockfile_content
|
54
54
|
return lockfile.content if npmrc_disables_lockfile?
|
@@ -112,7 +112,7 @@ module Dependabot
|
|
112
112
|
end
|
113
113
|
|
114
114
|
def run_current_npm_update
|
115
|
-
run_npm_updater(top_level_dependencies: top_level_dependencies)
|
115
|
+
run_npm_updater(top_level_dependencies: top_level_dependencies, sub_dependencies: sub_dependencies)
|
116
116
|
end
|
117
117
|
|
118
118
|
def run_previous_npm_update
|
@@ -127,16 +127,31 @@ module Dependabot
|
|
127
127
|
)
|
128
128
|
end
|
129
129
|
|
130
|
-
|
130
|
+
previous_sub_dependencies = sub_dependencies.map do |d|
|
131
|
+
Dependabot::Dependency.new(
|
132
|
+
name: d.name,
|
133
|
+
package_manager: d.package_manager,
|
134
|
+
version: d.previous_version,
|
135
|
+
previous_version: d.previous_version,
|
136
|
+
requirements: [],
|
137
|
+
previous_requirements: []
|
138
|
+
)
|
139
|
+
end
|
140
|
+
|
141
|
+
run_npm_updater(top_level_dependencies: previous_top_level_dependencies,
|
142
|
+
sub_dependencies: previous_sub_dependencies)
|
131
143
|
end
|
132
144
|
|
133
|
-
def run_npm_updater(top_level_dependencies:)
|
145
|
+
def run_npm_updater(top_level_dependencies:, sub_dependencies:)
|
134
146
|
SharedHelpers.with_git_configured(credentials: credentials) do
|
147
|
+
updated_files = {}
|
135
148
|
if top_level_dependencies.any?
|
136
|
-
run_npm_top_level_updater(top_level_dependencies: top_level_dependencies)
|
137
|
-
|
138
|
-
|
149
|
+
updated_files.merge!(run_npm_top_level_updater(top_level_dependencies: top_level_dependencies))
|
150
|
+
end
|
151
|
+
if sub_dependencies.any?
|
152
|
+
updated_files.merge!(run_npm_subdependency_updater(sub_dependencies: sub_dependencies))
|
139
153
|
end
|
154
|
+
updated_files
|
140
155
|
end
|
141
156
|
end
|
142
157
|
|
@@ -194,9 +209,9 @@ module Dependabot
|
|
194
209
|
{ lockfile_basename => File.read(lockfile_basename) }
|
195
210
|
end
|
196
211
|
|
197
|
-
def run_npm_subdependency_updater
|
212
|
+
def run_npm_subdependency_updater(sub_dependencies:)
|
198
213
|
if npm8?
|
199
|
-
run_npm8_subdependency_updater
|
214
|
+
run_npm8_subdependency_updater(sub_dependencies: sub_dependencies)
|
200
215
|
else
|
201
216
|
SharedHelpers.run_helper_subprocess(
|
202
217
|
command: NativeHelpers.helper_path,
|
@@ -206,7 +221,7 @@ module Dependabot
|
|
206
221
|
end
|
207
222
|
end
|
208
223
|
|
209
|
-
def run_npm8_subdependency_updater
|
224
|
+
def run_npm8_subdependency_updater(sub_dependencies:)
|
210
225
|
dependency_names = sub_dependencies.map(&:name)
|
211
226
|
SharedHelpers.run_shell_command(NativeHelpers.npm8_subdependency_update_command(dependency_names))
|
212
227
|
{ lockfile_basename => File.read(lockfile_basename) }
|
@@ -414,10 +429,9 @@ module Dependabot
|
|
414
429
|
reg = NpmAndYarn::UpdateChecker::RegistryFinder.new(
|
415
430
|
dependency: missing_dep,
|
416
431
|
credentials: credentials,
|
417
|
-
npmrc_file: dependency_files.
|
418
|
-
|
419
|
-
|
420
|
-
find { |f| f.name.end_with?(".yarnrc") }
|
432
|
+
npmrc_file: dependency_files. find { |f| f.name.end_with?(".npmrc") },
|
433
|
+
yarnrc_file: dependency_files. find { |f| f.name.end_with?(".yarnrc") },
|
434
|
+
yarnrc_yml_file: dependency_files.find { |f| f.name.end_with?(".yarnrc.yml") }
|
421
435
|
).registry
|
422
436
|
|
423
437
|
return if UpdateChecker::RegistryFinder.central_registry?(reg) && !package_name.start_with?("@")
|
@@ -13,7 +13,7 @@ module Dependabot
|
|
13
13
|
registry.yarnpkg.com
|
14
14
|
).freeze
|
15
15
|
|
16
|
-
SCOPED_REGISTRY = /^\s*@(?<scope>\S+):registry\s*=\s*(?<registry>\S+)
|
16
|
+
SCOPED_REGISTRY = /^\s*@(?<scope>\S+):registry\s*=\s*(?<registry>\S+)/
|
17
17
|
|
18
18
|
def initialize(dependency_files:, credentials:)
|
19
19
|
@dependency_files = dependency_files
|
@@ -42,7 +42,9 @@ module Dependabot
|
|
42
42
|
return unless yarn_lock || package_lock
|
43
43
|
return unless global_registry
|
44
44
|
|
45
|
-
|
45
|
+
registry = global_registry["registry"]
|
46
|
+
registry = "https://#{registry}" unless registry.start_with?("http")
|
47
|
+
"registry = #{registry}\n" \
|
46
48
|
"#{global_registry_auth_line}" \
|
47
49
|
"always-auth = true"
|
48
50
|
end
|
@@ -113,8 +115,10 @@ module Dependabot
|
|
113
115
|
return initial_content unless yarn_lock || package_lock
|
114
116
|
return initial_content unless global_registry
|
115
117
|
|
118
|
+
registry = global_registry["registry"]
|
119
|
+
registry = "https://#{registry}" unless registry.start_with?("http")
|
116
120
|
initial_content +
|
117
|
-
"registry =
|
121
|
+
"registry = #{registry}\n" \
|
118
122
|
"#{global_registry_auth_line}" \
|
119
123
|
"always-auth = true\n"
|
120
124
|
end
|
@@ -4,10 +4,12 @@ require "uri"
|
|
4
4
|
|
5
5
|
require "dependabot/npm_and_yarn/file_updater"
|
6
6
|
require "dependabot/npm_and_yarn/file_parser"
|
7
|
+
require "dependabot/npm_and_yarn/helpers"
|
7
8
|
require "dependabot/npm_and_yarn/update_checker/registry_finder"
|
8
9
|
require "dependabot/npm_and_yarn/native_helpers"
|
9
10
|
require "dependabot/shared_helpers"
|
10
11
|
require "dependabot/errors"
|
12
|
+
require "dependabot/experiments"
|
11
13
|
|
12
14
|
# rubocop:disable Metrics/ClassLength
|
13
15
|
module Dependabot
|
@@ -17,9 +19,10 @@ module Dependabot
|
|
17
19
|
require_relative "npmrc_builder"
|
18
20
|
require_relative "package_json_updater"
|
19
21
|
|
20
|
-
def initialize(dependencies:, dependency_files:, credentials:)
|
22
|
+
def initialize(dependencies:, dependency_files:, repo_contents_path:, credentials:)
|
21
23
|
@dependencies = dependencies
|
22
24
|
@dependency_files = dependency_files
|
25
|
+
@repo_contents_path = repo_contents_path
|
23
26
|
@credentials = credentials
|
24
27
|
end
|
25
28
|
|
@@ -35,12 +38,11 @@ module Dependabot
|
|
35
38
|
|
36
39
|
private
|
37
40
|
|
38
|
-
attr_reader :dependencies, :dependency_files, :credentials
|
41
|
+
attr_reader :dependencies, :dependency_files, :repo_contents_path, :credentials
|
39
42
|
|
40
|
-
UNREACHABLE_GIT = /ls-remote --tags --heads (?<url>.*)
|
41
|
-
TIMEOUT_FETCHING_PACKAGE =
|
42
|
-
|
43
|
-
INVALID_PACKAGE = /Can't add "(?<package_req>.*)": invalid/.freeze
|
43
|
+
UNREACHABLE_GIT = /ls-remote --tags --heads (?<url>.*)/
|
44
|
+
TIMEOUT_FETCHING_PACKAGE = %r{(?<url>.+)/(?<package>[^/]+): ETIMEDOUT}
|
45
|
+
INVALID_PACKAGE = /Can't add "(?<package_req>.*)": invalid/
|
44
46
|
|
45
47
|
def top_level_dependencies
|
46
48
|
dependencies.select(&:top_level?)
|
@@ -51,13 +53,14 @@ module Dependabot
|
|
51
53
|
end
|
52
54
|
|
53
55
|
def updated_yarn_lock(yarn_lock)
|
54
|
-
|
56
|
+
base_dir = dependency_files.first.directory
|
57
|
+
SharedHelpers.in_a_temporary_repo_directory(base_dir, repo_contents_path) do
|
55
58
|
write_temporary_dependency_files
|
56
59
|
lockfile_name = Pathname.new(yarn_lock.name).basename.to_s
|
57
60
|
path = Pathname.new(yarn_lock.name).dirname.to_s
|
58
61
|
updated_files = run_current_yarn_update(
|
59
62
|
path: path,
|
60
|
-
|
63
|
+
yarn_lock: yarn_lock
|
61
64
|
)
|
62
65
|
updated_files.fetch(lockfile_name)
|
63
66
|
end
|
@@ -65,7 +68,7 @@ module Dependabot
|
|
65
68
|
handle_yarn_lock_updater_error(e, yarn_lock)
|
66
69
|
end
|
67
70
|
|
68
|
-
def run_current_yarn_update(path:,
|
71
|
+
def run_current_yarn_update(path:, yarn_lock:)
|
69
72
|
top_level_dependency_updates = top_level_dependencies.map do |d|
|
70
73
|
{
|
71
74
|
name: d.name,
|
@@ -76,12 +79,12 @@ module Dependabot
|
|
76
79
|
|
77
80
|
run_yarn_updater(
|
78
81
|
path: path,
|
79
|
-
|
82
|
+
yarn_lock: yarn_lock,
|
80
83
|
top_level_dependency_updates: top_level_dependency_updates
|
81
84
|
)
|
82
85
|
end
|
83
86
|
|
84
|
-
def run_previous_yarn_update(path:,
|
87
|
+
def run_previous_yarn_update(path:, yarn_lock:)
|
85
88
|
previous_top_level_dependencies = top_level_dependencies.map do |d|
|
86
89
|
{
|
87
90
|
name: d.name,
|
@@ -94,22 +97,29 @@ module Dependabot
|
|
94
97
|
|
95
98
|
run_yarn_updater(
|
96
99
|
path: path,
|
97
|
-
|
100
|
+
yarn_lock: yarn_lock,
|
98
101
|
top_level_dependency_updates: previous_top_level_dependencies
|
99
102
|
)
|
100
103
|
end
|
101
104
|
|
102
105
|
# rubocop:disable Metrics/PerceivedComplexity
|
103
|
-
def run_yarn_updater(path:,
|
104
|
-
top_level_dependency_updates:)
|
106
|
+
def run_yarn_updater(path:, yarn_lock:, top_level_dependency_updates:)
|
105
107
|
SharedHelpers.with_git_configured(credentials: credentials) do
|
106
108
|
Dir.chdir(path) do
|
107
109
|
if top_level_dependency_updates.any?
|
108
|
-
|
109
|
-
top_level_dependency_updates: top_level_dependency_updates
|
110
|
-
|
110
|
+
if yarn_berry?(yarn_lock)
|
111
|
+
run_yarn_berry_top_level_updater(top_level_dependency_updates: top_level_dependency_updates,
|
112
|
+
yarn_lock: yarn_lock)
|
113
|
+
else
|
114
|
+
|
115
|
+
run_yarn_top_level_updater(
|
116
|
+
top_level_dependency_updates: top_level_dependency_updates
|
117
|
+
)
|
118
|
+
end
|
119
|
+
elsif yarn_berry?(yarn_lock)
|
120
|
+
run_yarn_berry_subdependency_updater(yarn_lock: yarn_lock)
|
111
121
|
else
|
112
|
-
run_yarn_subdependency_updater(
|
122
|
+
run_yarn_subdependency_updater(yarn_lock: yarn_lock)
|
113
123
|
end
|
114
124
|
end
|
115
125
|
end
|
@@ -133,6 +143,50 @@ module Dependabot
|
|
133
143
|
|
134
144
|
# rubocop:enable Metrics/PerceivedComplexity
|
135
145
|
|
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
|
+
def run_yarn_berry_top_level_updater(top_level_dependency_updates:, yarn_lock:)
|
156
|
+
# If the requirements have changed, it means we've updated the
|
157
|
+
# package.json file(s), and we can just run yarn install to get the
|
158
|
+
# lockfile in the right state. Otherwise we'll need to manually update
|
159
|
+
# the lockfile.
|
160
|
+
|
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)
|
170
|
+
{ yarn_lock.name => File.read(yarn_lock.name) }
|
171
|
+
end
|
172
|
+
|
173
|
+
def requirements_changed?(dependency_name)
|
174
|
+
dep = top_level_dependencies.first { |d| d.name == dependency_name }
|
175
|
+
dep.requirements != dep.previous_requirements
|
176
|
+
end
|
177
|
+
|
178
|
+
def run_yarn_berry_subdependency_updater(yarn_lock:)
|
179
|
+
dep = sub_dependencies.first
|
180
|
+
update = "#{dep.name}@#{dep.version}"
|
181
|
+
|
182
|
+
Helpers.run_yarn_commands(
|
183
|
+
"yarn add #{update}",
|
184
|
+
"yarn dedupe #{dep.name}",
|
185
|
+
"yarn remove #{dep.name}"
|
186
|
+
)
|
187
|
+
{ yarn_lock.name => File.read(yarn_lock.name) }
|
188
|
+
end
|
189
|
+
|
136
190
|
def run_yarn_top_level_updater(top_level_dependency_updates:)
|
137
191
|
SharedHelpers.run_helper_subprocess(
|
138
192
|
command: NativeHelpers.helper_path,
|
@@ -144,7 +198,8 @@ module Dependabot
|
|
144
198
|
)
|
145
199
|
end
|
146
200
|
|
147
|
-
def run_yarn_subdependency_updater(
|
201
|
+
def run_yarn_subdependency_updater(yarn_lock:)
|
202
|
+
lockfile_name = Pathname.new(yarn_lock.name).basename.to_s
|
148
203
|
SharedHelpers.run_helper_subprocess(
|
149
204
|
command: NativeHelpers.helper_path,
|
150
205
|
function: "yarn:updateSubdependency",
|
@@ -259,12 +314,11 @@ module Dependabot
|
|
259
314
|
|
260
315
|
@resolvable_before_update[yarn_lock.name] =
|
261
316
|
begin
|
262
|
-
|
317
|
+
base_dir = dependency_files.first.directory
|
318
|
+
SharedHelpers.in_a_temporary_repo_directory(base_dir, repo_contents_path) do
|
263
319
|
write_temporary_dependency_files(update_package_json: false)
|
264
|
-
lockfile_name = Pathname.new(yarn_lock.name).basename.to_s
|
265
320
|
path = Pathname.new(yarn_lock.name).dirname.to_s
|
266
|
-
run_previous_yarn_update(path: path,
|
267
|
-
lockfile_name: lockfile_name)
|
321
|
+
run_previous_yarn_update(path: path, yarn_lock: yarn_lock)
|
268
322
|
end
|
269
323
|
|
270
324
|
true
|
@@ -420,7 +474,8 @@ module Dependabot
|
|
420
474
|
dependency: missing_dep,
|
421
475
|
credentials: credentials,
|
422
476
|
npmrc_file: npmrc_file,
|
423
|
-
yarnrc_file: yarnrc_file
|
477
|
+
yarnrc_file: yarnrc_file,
|
478
|
+
yarnrc_yml_file: yarnrc_yml_file
|
424
479
|
).registry
|
425
480
|
|
426
481
|
return if UpdateChecker::RegistryFinder.central_registry?(reg) && !package_name.start_with?("@")
|
@@ -524,6 +579,10 @@ module Dependabot
|
|
524
579
|
def npmrc_file
|
525
580
|
dependency_files.find { |f| f.name == ".npmrc" }
|
526
581
|
end
|
582
|
+
|
583
|
+
def yarnrc_yml_file
|
584
|
+
dependency_files.find { |f| f.name.end_with?(".yarnrc.yml") }
|
585
|
+
end
|
527
586
|
end
|
528
587
|
end
|
529
588
|
end
|