dependabot-npm_and_yarn 0.292.0 → 0.294.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/helpers/lib/npm/vulnerability-auditor.js +16 -16
- data/helpers/lib/npm6/updater.js +1 -1
- data/lib/dependabot/npm_and_yarn/bun_package_manager.rb +46 -0
- data/lib/dependabot/npm_and_yarn/dependency_files_filterer.rb +2 -1
- data/lib/dependabot/npm_and_yarn/file_fetcher.rb +61 -35
- data/lib/dependabot/npm_and_yarn/file_parser/bun_lock.rb +141 -0
- data/lib/dependabot/npm_and_yarn/file_parser/lockfile_parser.rb +33 -27
- data/lib/dependabot/npm_and_yarn/file_parser/pnpm_lock.rb +47 -0
- data/lib/dependabot/npm_and_yarn/file_parser.rb +17 -9
- data/lib/dependabot/npm_and_yarn/file_updater/bun_lockfile_updater.rb +144 -0
- data/lib/dependabot/npm_and_yarn/file_updater/pnpm_lockfile_updater.rb +127 -12
- data/lib/dependabot/npm_and_yarn/file_updater.rb +66 -0
- data/lib/dependabot/npm_and_yarn/helpers.rb +54 -2
- data/lib/dependabot/npm_and_yarn/language.rb +45 -0
- data/lib/dependabot/npm_and_yarn/npm_package_manager.rb +70 -0
- data/lib/dependabot/npm_and_yarn/package_manager.rb +16 -196
- data/lib/dependabot/npm_and_yarn/pnpm_package_manager.rb +55 -0
- data/lib/dependabot/npm_and_yarn/sub_dependency_files_filterer.rb +1 -0
- data/lib/dependabot/npm_and_yarn/update_checker/dependency_files_builder.rb +14 -7
- data/lib/dependabot/npm_and_yarn/update_checker/subdependency_version_resolver.rb +14 -0
- data/lib/dependabot/npm_and_yarn/update_checker/version_resolver.rb +19 -0
- data/lib/dependabot/npm_and_yarn/version.rb +4 -0
- data/lib/dependabot/npm_and_yarn/yarn_package_manager.rb +56 -0
- metadata +12 -5
@@ -110,7 +110,8 @@ module Dependabot
|
|
110
110
|
{
|
111
111
|
npm: package_lock || shrinkwrap,
|
112
112
|
yarn: yarn_lock,
|
113
|
-
pnpm: pnpm_lock
|
113
|
+
pnpm: pnpm_lock,
|
114
|
+
bun: bun_lock
|
114
115
|
}
|
115
116
|
end
|
116
117
|
|
@@ -142,49 +143,56 @@ module Dependabot
|
|
142
143
|
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
143
144
|
def shrinkwrap
|
144
145
|
@shrinkwrap ||= T.let(dependency_files.find do |f|
|
145
|
-
f.name
|
146
|
+
f.name.end_with?(NpmPackageManager::SHRINKWRAP_LOCKFILE_NAME)
|
146
147
|
end, T.nilable(Dependabot::DependencyFile))
|
147
148
|
end
|
148
149
|
|
149
150
|
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
150
151
|
def package_lock
|
151
152
|
@package_lock ||= T.let(dependency_files.find do |f|
|
152
|
-
f.name
|
153
|
+
f.name.end_with?(NpmPackageManager::LOCKFILE_NAME)
|
153
154
|
end, T.nilable(Dependabot::DependencyFile))
|
154
155
|
end
|
155
156
|
|
156
157
|
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
157
158
|
def yarn_lock
|
158
159
|
@yarn_lock ||= T.let(dependency_files.find do |f|
|
159
|
-
f.name
|
160
|
+
f.name.end_with?(YarnPackageManager::LOCKFILE_NAME)
|
160
161
|
end, T.nilable(Dependabot::DependencyFile))
|
161
162
|
end
|
162
163
|
|
163
164
|
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
164
165
|
def pnpm_lock
|
165
166
|
@pnpm_lock ||= T.let(dependency_files.find do |f|
|
166
|
-
f.name
|
167
|
+
f.name.end_with?(PNPMPackageManager::LOCKFILE_NAME)
|
168
|
+
end, T.nilable(Dependabot::DependencyFile))
|
169
|
+
end
|
170
|
+
|
171
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
172
|
+
def bun_lock
|
173
|
+
@bun_lock ||= T.let(dependency_files.find do |f|
|
174
|
+
f.name.end_with?(BunPackageManager::LOCKFILE_NAME)
|
167
175
|
end, T.nilable(Dependabot::DependencyFile))
|
168
176
|
end
|
169
177
|
|
170
178
|
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
171
179
|
def npmrc
|
172
180
|
@npmrc ||= T.let(dependency_files.find do |f|
|
173
|
-
f.name
|
181
|
+
f.name.end_with?(NpmPackageManager::RC_FILENAME)
|
174
182
|
end, T.nilable(Dependabot::DependencyFile))
|
175
183
|
end
|
176
184
|
|
177
185
|
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
178
186
|
def yarnrc
|
179
187
|
@yarnrc ||= T.let(dependency_files.find do |f|
|
180
|
-
f.name
|
188
|
+
f.name.end_with?(YarnPackageManager::RC_FILENAME)
|
181
189
|
end, T.nilable(Dependabot::DependencyFile))
|
182
190
|
end
|
183
191
|
|
184
192
|
sig { returns(T.nilable(DependencyFile)) }
|
185
193
|
def yarnrc_yml
|
186
194
|
@yarnrc_yml ||= T.let(dependency_files.find do |f|
|
187
|
-
f.name
|
195
|
+
f.name.end_with?(YarnPackageManager::RC_YML_FILENAME)
|
188
196
|
end, T.nilable(Dependabot::DependencyFile))
|
189
197
|
end
|
190
198
|
|
@@ -204,7 +212,7 @@ module Dependabot
|
|
204
212
|
next unless requirement.is_a?(String)
|
205
213
|
|
206
214
|
# Skip dependencies using Yarn workspace cross-references as requirements
|
207
|
-
next if requirement.start_with?("workspace:")
|
215
|
+
next if requirement.start_with?("workspace:", "catalog:")
|
208
216
|
|
209
217
|
requirement = "*" if requirement == ""
|
210
218
|
dep = build_dependency(
|
@@ -0,0 +1,144 @@
|
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "dependabot/npm_and_yarn/helpers"
|
5
|
+
require "dependabot/npm_and_yarn/update_checker/registry_finder"
|
6
|
+
require "dependabot/npm_and_yarn/registry_parser"
|
7
|
+
require "dependabot/shared_helpers"
|
8
|
+
|
9
|
+
module Dependabot
|
10
|
+
module NpmAndYarn
|
11
|
+
class FileUpdater < Dependabot::FileUpdaters::Base
|
12
|
+
class BunLockfileUpdater
|
13
|
+
require_relative "npmrc_builder"
|
14
|
+
require_relative "package_json_updater"
|
15
|
+
|
16
|
+
def initialize(dependencies:, dependency_files:, repo_contents_path:, credentials:)
|
17
|
+
@dependencies = dependencies
|
18
|
+
@dependency_files = dependency_files
|
19
|
+
@repo_contents_path = repo_contents_path
|
20
|
+
@credentials = credentials
|
21
|
+
end
|
22
|
+
|
23
|
+
def updated_bun_lock_content(bun_lock)
|
24
|
+
@updated_bun_lock_content ||= {}
|
25
|
+
return @updated_bun_lock_content[bun_lock.name] if @updated_bun_lock_content[bun_lock.name]
|
26
|
+
|
27
|
+
new_content = run_bun_update(bun_lock: bun_lock)
|
28
|
+
@updated_bun_lock_content[bun_lock.name] = new_content
|
29
|
+
rescue SharedHelpers::HelperSubprocessFailed => e
|
30
|
+
handle_bun_lock_updater_error(e, bun_lock)
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
attr_reader :dependencies
|
36
|
+
attr_reader :dependency_files
|
37
|
+
attr_reader :repo_contents_path
|
38
|
+
attr_reader :credentials
|
39
|
+
|
40
|
+
ERR_PATTERNS = {
|
41
|
+
/get .* 404/i => Dependabot::DependencyNotFound,
|
42
|
+
/installfailed cloning repository/i => Dependabot::DependencyNotFound,
|
43
|
+
/file:.* failed to resolve/i => Dependabot::DependencyNotFound,
|
44
|
+
/no version matching/i => Dependabot::DependencyFileNotResolvable,
|
45
|
+
/failed to resolve/i => Dependabot::DependencyFileNotResolvable
|
46
|
+
}.freeze
|
47
|
+
|
48
|
+
def run_bun_update(bun_lock:)
|
49
|
+
SharedHelpers.in_a_temporary_repo_directory(base_dir, repo_contents_path) do
|
50
|
+
File.write(".npmrc", npmrc_content(bun_lock))
|
51
|
+
|
52
|
+
SharedHelpers.with_git_configured(credentials: credentials) do
|
53
|
+
run_bun_updater
|
54
|
+
|
55
|
+
write_final_package_json_files
|
56
|
+
|
57
|
+
run_bun_install
|
58
|
+
|
59
|
+
File.read(bun_lock.name)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def run_bun_updater
|
65
|
+
dependency_updates = dependencies.map do |d|
|
66
|
+
"#{d.name}@#{d.version}"
|
67
|
+
end.join(" ")
|
68
|
+
|
69
|
+
Helpers.run_bun_command(
|
70
|
+
"install #{dependency_updates} --save-text-lockfile",
|
71
|
+
fingerprint: "install <dependency_updates> --save-text-lockfile"
|
72
|
+
)
|
73
|
+
end
|
74
|
+
|
75
|
+
def run_bun_install
|
76
|
+
Helpers.run_bun_command(
|
77
|
+
"install --save-text-lockfile"
|
78
|
+
)
|
79
|
+
end
|
80
|
+
|
81
|
+
def lockfile_dependencies(lockfile)
|
82
|
+
@lockfile_dependencies ||= {}
|
83
|
+
@lockfile_dependencies[lockfile.name] ||=
|
84
|
+
NpmAndYarn::FileParser.new(
|
85
|
+
dependency_files: [lockfile, *package_files],
|
86
|
+
source: nil,
|
87
|
+
credentials: credentials
|
88
|
+
).parse
|
89
|
+
end
|
90
|
+
|
91
|
+
def handle_bun_lock_updater_error(error, _bun_lock)
|
92
|
+
error_message = error.message
|
93
|
+
|
94
|
+
ERR_PATTERNS.each do |pattern, error_class|
|
95
|
+
raise error_class, error_message if error_message.match?(pattern)
|
96
|
+
end
|
97
|
+
|
98
|
+
raise error
|
99
|
+
end
|
100
|
+
|
101
|
+
def write_final_package_json_files
|
102
|
+
package_files.each do |file|
|
103
|
+
path = file.name
|
104
|
+
FileUtils.mkdir_p(Pathname.new(path).dirname)
|
105
|
+
File.write(path, updated_package_json_content(file))
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def npmrc_content(bun_lock)
|
110
|
+
NpmrcBuilder.new(
|
111
|
+
credentials: credentials,
|
112
|
+
dependency_files: dependency_files,
|
113
|
+
dependencies: lockfile_dependencies(bun_lock)
|
114
|
+
).npmrc_content
|
115
|
+
end
|
116
|
+
|
117
|
+
def updated_package_json_content(file)
|
118
|
+
@updated_package_json_content ||= {}
|
119
|
+
@updated_package_json_content[file.name] ||=
|
120
|
+
PackageJsonUpdater.new(
|
121
|
+
package_json: file,
|
122
|
+
dependencies: dependencies
|
123
|
+
).updated_package_json.content
|
124
|
+
end
|
125
|
+
|
126
|
+
def package_files
|
127
|
+
@package_files ||= dependency_files.select { |f| f.name.end_with?("package.json") }
|
128
|
+
end
|
129
|
+
|
130
|
+
def base_dir
|
131
|
+
dependency_files.first.directory
|
132
|
+
end
|
133
|
+
|
134
|
+
def npmrc_file
|
135
|
+
dependency_files.find { |f| f.name == ".npmrc" }
|
136
|
+
end
|
137
|
+
|
138
|
+
def sanitize_message(message)
|
139
|
+
message.gsub(/"|\[|\]|\}|\{/, "")
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
@@ -18,6 +18,10 @@ module Dependabot
|
|
18
18
|
@dependency_files = dependency_files
|
19
19
|
@repo_contents_path = repo_contents_path
|
20
20
|
@credentials = credentials
|
21
|
+
@error_handler = PnpmErrorHandler.new(
|
22
|
+
dependencies: dependencies,
|
23
|
+
dependency_files: dependency_files
|
24
|
+
)
|
21
25
|
end
|
22
26
|
|
23
27
|
def updated_pnpm_lock_content(pnpm_lock)
|
@@ -36,6 +40,7 @@ module Dependabot
|
|
36
40
|
attr_reader :dependency_files
|
37
41
|
attr_reader :repo_contents_path
|
38
42
|
attr_reader :credentials
|
43
|
+
attr_reader :error_handler
|
39
44
|
|
40
45
|
IRRESOLVABLE_PACKAGE = "ERR_PNPM_NO_MATCHING_VERSION"
|
41
46
|
INVALID_REQUIREMENT = "ERR_PNPM_SPEC_NOT_SUPPORTED_BY_ANY_RESOLVER"
|
@@ -46,12 +51,12 @@ module Dependabot
|
|
46
51
|
UNAUTHORIZED_PACKAGE = /ERR_PNPM_FETCH_401[ [^:print:]]+GET (?<dependency_url>.*): Unauthorized - 401/
|
47
52
|
|
48
53
|
# ERR_PNPM_FETCH ERROR CODES
|
49
|
-
ERR_PNPM_FETCH_401 = /ERR_PNPM_FETCH_401.*GET (?<dependency_url>.*)
|
50
|
-
ERR_PNPM_FETCH_403 = /ERR_PNPM_FETCH_403.*GET (?<dependency_url>.*)
|
51
|
-
ERR_PNPM_FETCH_404 = /ERR_PNPM_FETCH_404.*GET (?<dependency_url>.*)
|
52
|
-
ERR_PNPM_FETCH_500 = /ERR_PNPM_FETCH_500.*GET (?<dependency_url>.*)
|
53
|
-
ERR_PNPM_FETCH_502 = /ERR_PNPM_FETCH_502.*GET (?<dependency_url>.*)
|
54
|
-
ERR_PNPM_FETCH_503 = /ERR_PNPM_FETCH_503.*GET (?<dependency_url>.*)
|
54
|
+
ERR_PNPM_FETCH_401 = /ERR_PNPM_FETCH_401.*GET (?<dependency_url>.*):/
|
55
|
+
ERR_PNPM_FETCH_403 = /ERR_PNPM_FETCH_403.*GET (?<dependency_url>.*):/
|
56
|
+
ERR_PNPM_FETCH_404 = /ERR_PNPM_FETCH_404.*GET (?<dependency_url>.*):/
|
57
|
+
ERR_PNPM_FETCH_500 = /ERR_PNPM_FETCH_500.*GET (?<dependency_url>.*):/
|
58
|
+
ERR_PNPM_FETCH_502 = /ERR_PNPM_FETCH_502.*GET (?<dependency_url>.*):/
|
59
|
+
ERR_PNPM_FETCH_503 = /ERR_PNPM_FETCH_503.*GET (?<dependency_url>.*):/
|
55
60
|
|
56
61
|
# ERR_PNPM_UNSUPPORTED_ENGINE
|
57
62
|
ERR_PNPM_UNSUPPORTED_ENGINE = /ERR_PNPM_UNSUPPORTED_ENGINE/
|
@@ -62,6 +67,13 @@ module Dependabot
|
|
62
67
|
|
63
68
|
ERR_PNPM_PATCH_NOT_APPLIED = /ERR_PNPM_PATCH_NOT_APPLIED/
|
64
69
|
|
70
|
+
# this intermittent issue is related with Node v20
|
71
|
+
ERR_INVALID_THIS = /ERR_INVALID_THIS/
|
72
|
+
URL_SEARCH_PARAMS = /URLSearchParams/
|
73
|
+
|
74
|
+
# A modules directory is present and is linked to a different store directory.
|
75
|
+
ERR_PNPM_UNEXPECTED_STORE = /ERR_PNPM_UNEXPECTED_STORE/
|
76
|
+
|
65
77
|
# ERR_PNPM_UNSUPPORTED_PLATFORM
|
66
78
|
ERR_PNPM_UNSUPPORTED_PLATFORM = /ERR_PNPM_UNSUPPORTED_PLATFORM/
|
67
79
|
PLATFORM_PACAKGE_DEP = /Unsupported platform for (?<dep>.*)\: wanted/
|
@@ -78,12 +90,22 @@ module Dependabot
|
|
78
90
|
ERR_PNPM_LINKED_PKG_DIR_NOT_FOUND = /ERR_PNPM_LINKED_PKG_DIR_NOT_FOUND*.*Could not install from \"(?<dir>.*)\" /
|
79
91
|
ERR_PNPM_WORKSPACE_PKG_NOT_FOUND = /ERR_PNPM_WORKSPACE_PKG_NOT_FOUND/
|
80
92
|
|
93
|
+
# Unparsable package.json file
|
94
|
+
ERR_PNPM_INVALID_PACKAGE_JSON = /Invalid package.json in package/
|
95
|
+
|
96
|
+
# Unparsable lockfile
|
97
|
+
ERR_PNPM_UNEXPECTED_PKG_CONTENT_IN_STORE = /ERR_PNPM_UNEXPECTED_PKG_CONTENT_IN_STORE/
|
98
|
+
ERR_PNPM_OUTDATED_LOCKFILE = /ERR_PNPM_OUTDATED_LOCKFILE/
|
99
|
+
|
100
|
+
# Peer dependencies configuration error
|
101
|
+
ERR_PNPM_PEER_DEP_ISSUES = /ERR_PNPM_PEER_DEP_ISSUES/
|
102
|
+
|
81
103
|
def run_pnpm_update(pnpm_lock:)
|
82
104
|
SharedHelpers.in_a_temporary_repo_directory(base_dir, repo_contents_path) do
|
83
105
|
File.write(".npmrc", npmrc_content(pnpm_lock))
|
84
106
|
|
85
107
|
SharedHelpers.with_git_configured(credentials: credentials) do
|
86
|
-
|
108
|
+
run_pnpm_update_packages
|
87
109
|
|
88
110
|
write_final_package_json_files
|
89
111
|
|
@@ -94,15 +116,22 @@ module Dependabot
|
|
94
116
|
end
|
95
117
|
end
|
96
118
|
|
97
|
-
def
|
119
|
+
def run_pnpm_update_packages
|
98
120
|
dependency_updates = dependencies.map do |d|
|
99
121
|
"#{d.name}@#{d.version}"
|
100
122
|
end.join(" ")
|
101
123
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
124
|
+
if Dependabot::Experiments.enabled?(:enable_fix_for_pnpm_no_change_error)
|
125
|
+
Helpers.run_pnpm_command(
|
126
|
+
"update #{dependency_updates} --lockfile-only --no-save -r",
|
127
|
+
fingerprint: "update <dependency_updates> --lockfile-only --no-save -r"
|
128
|
+
)
|
129
|
+
else
|
130
|
+
Helpers.run_pnpm_command(
|
131
|
+
"install #{dependency_updates} --lockfile-only --ignore-workspace-root-check",
|
132
|
+
fingerprint: "install <dependency_updates> --lockfile-only --ignore-workspace-root-check"
|
133
|
+
)
|
134
|
+
end
|
106
135
|
end
|
107
136
|
|
108
137
|
def run_pnpm_install
|
@@ -196,15 +225,46 @@ module Dependabot
|
|
196
225
|
raise Dependabot::DependencyFileNotResolvable, msg
|
197
226
|
end
|
198
227
|
|
228
|
+
if error_message.match?(ERR_PNPM_INVALID_PACKAGE_JSON) || error_message.match?(ERR_PNPM_UNEXPECTED_STORE)
|
229
|
+
msg = "Error while resolving package.json."
|
230
|
+
Dependabot.logger.warn(error_message)
|
231
|
+
raise Dependabot::DependencyFileNotResolvable, msg
|
232
|
+
end
|
233
|
+
|
234
|
+
[ERR_PNPM_UNEXPECTED_PKG_CONTENT_IN_STORE, ERR_PNPM_OUTDATED_LOCKFILE]
|
235
|
+
.each do |regexp|
|
236
|
+
next unless error_message.match?(regexp)
|
237
|
+
|
238
|
+
error_msg = T.let("Error while resolving pnpm-lock.yaml file.", String)
|
239
|
+
|
240
|
+
Dependabot.logger.warn(error_message)
|
241
|
+
raise Dependabot::DependencyFileNotResolvable, error_msg
|
242
|
+
end
|
243
|
+
|
244
|
+
if error_message.match?(ERR_PNPM_PEER_DEP_ISSUES)
|
245
|
+
msg = "Missing or invalid configuration while installing peer dependencies."
|
246
|
+
|
247
|
+
Dependabot.logger.warn(error_message)
|
248
|
+
raise Dependabot::DependencyFileNotResolvable, msg
|
249
|
+
end
|
250
|
+
|
199
251
|
raise_patch_dependency_error(error_message) if error_message.match?(ERR_PNPM_PATCH_NOT_APPLIED)
|
200
252
|
|
201
253
|
raise_unsupported_engine_error(error_message, pnpm_lock) if error_message.match?(ERR_PNPM_UNSUPPORTED_ENGINE)
|
202
254
|
|
255
|
+
if error_message.match?(ERR_INVALID_THIS) && error_message.match?(URL_SEARCH_PARAMS)
|
256
|
+
msg = "Error while resolving dependencies."
|
257
|
+
Dependabot.logger.warn(error_message)
|
258
|
+
raise Dependabot::DependencyFileNotResolvable, msg
|
259
|
+
end
|
260
|
+
|
203
261
|
if error_message.match?(ERR_PNPM_UNSUPPORTED_PLATFORM)
|
204
262
|
raise_unsupported_platform_error(error_message,
|
205
263
|
pnpm_lock)
|
206
264
|
end
|
207
265
|
|
266
|
+
error_handler.handle_pnpm_error(error)
|
267
|
+
|
208
268
|
raise
|
209
269
|
end
|
210
270
|
# rubocop:enable Metrics/AbcSize
|
@@ -314,5 +374,60 @@ module Dependabot
|
|
314
374
|
end
|
315
375
|
end
|
316
376
|
end
|
377
|
+
|
378
|
+
class PnpmErrorHandler
|
379
|
+
extend T::Sig
|
380
|
+
|
381
|
+
# remote connection closed
|
382
|
+
ECONNRESET_ERROR = /ECONNRESET/
|
383
|
+
|
384
|
+
# socket hang up error code
|
385
|
+
SOCKET_HANG_UP = /socket hang up/
|
386
|
+
|
387
|
+
# ERR_PNPM_CATALOG_ENTRY_NOT_FOUND_FOR_SPEC error
|
388
|
+
ERR_PNPM_CATALOG_ENTRY_NOT_FOUND_FOR_SPEC = /ERR_PNPM_CATALOG_ENTRY_NOT_FOUND_FOR_SPEC/
|
389
|
+
|
390
|
+
# duplicate package error code
|
391
|
+
DUPLICATE_PACKAGE = /Found duplicates/
|
392
|
+
|
393
|
+
ERR_PNPM_NO_VERSIONS = /ERR_PNPM_NO_VERSIONS/
|
394
|
+
|
395
|
+
# Initializes the YarnErrorHandler with dependencies and dependency files
|
396
|
+
sig do
|
397
|
+
params(
|
398
|
+
dependencies: T::Array[Dependabot::Dependency],
|
399
|
+
dependency_files: T::Array[Dependabot::DependencyFile]
|
400
|
+
).void
|
401
|
+
end
|
402
|
+
def initialize(dependencies:, dependency_files:)
|
403
|
+
@dependencies = dependencies
|
404
|
+
@dependency_files = dependency_files
|
405
|
+
end
|
406
|
+
|
407
|
+
private
|
408
|
+
|
409
|
+
sig { returns(T::Array[Dependabot::Dependency]) }
|
410
|
+
attr_reader :dependencies
|
411
|
+
|
412
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
413
|
+
attr_reader :dependency_files
|
414
|
+
|
415
|
+
public
|
416
|
+
|
417
|
+
# Handles errors with specific to yarn error codes
|
418
|
+
sig { params(error: SharedHelpers::HelperSubprocessFailed).void }
|
419
|
+
def handle_pnpm_error(error)
|
420
|
+
if error.message.match?(DUPLICATE_PACKAGE) || error.message.match?(ERR_PNPM_NO_VERSIONS) ||
|
421
|
+
error.message.match?(ERR_PNPM_CATALOG_ENTRY_NOT_FOUND_FOR_SPEC)
|
422
|
+
|
423
|
+
raise DependencyFileNotResolvable, "Error resolving dependency"
|
424
|
+
end
|
425
|
+
|
426
|
+
## Clean error message from ANSI escape codes
|
427
|
+
return unless error.message.match?(ECONNRESET_ERROR) || error.message.match?(SOCKET_HANG_UP)
|
428
|
+
|
429
|
+
raise InconsistentRegistryResponse, "Inconsistent registry response while resolving dependency"
|
430
|
+
end
|
431
|
+
end
|
317
432
|
end
|
318
433
|
end
|
@@ -18,6 +18,7 @@ module Dependabot
|
|
18
18
|
require_relative "file_updater/npm_lockfile_updater"
|
19
19
|
require_relative "file_updater/yarn_lockfile_updater"
|
20
20
|
require_relative "file_updater/pnpm_lockfile_updater"
|
21
|
+
require_relative "file_updater/bun_lockfile_updater"
|
21
22
|
|
22
23
|
class NoChangeError < StandardError
|
23
24
|
extend T::Sig
|
@@ -47,6 +48,7 @@ module Dependabot
|
|
47
48
|
]
|
48
49
|
end
|
49
50
|
|
51
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
50
52
|
sig { override.returns(T::Array[DependencyFile]) }
|
51
53
|
def updated_dependency_files
|
52
54
|
updated_files = T.let([], T::Array[DependencyFile])
|
@@ -55,6 +57,22 @@ module Dependabot
|
|
55
57
|
updated_files += updated_lockfiles
|
56
58
|
|
57
59
|
if updated_files.none?
|
60
|
+
|
61
|
+
if Dependabot::Experiments.enabled?(:enable_fix_for_pnpm_no_change_error)
|
62
|
+
# when all dependencies are transitive
|
63
|
+
all_transitive = dependencies.none?(&:top_level?)
|
64
|
+
# when there is no update in package.json
|
65
|
+
no_package_json_update = package_files.empty?
|
66
|
+
# handle the no change error for transitive dependency updates
|
67
|
+
if pnpm_locks.any? && dependencies.length.positive? && all_transitive && no_package_json_update
|
68
|
+
raise ToolFeatureNotSupported.new(
|
69
|
+
tool_name: "pnpm",
|
70
|
+
tool_type: "package_manager",
|
71
|
+
feature: "updating transitive dependencies"
|
72
|
+
)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
58
76
|
raise NoChangeError.new(
|
59
77
|
message: "No files were updated!",
|
60
78
|
error_context: error_context(updated_files: updated_files)
|
@@ -71,6 +89,7 @@ module Dependabot
|
|
71
89
|
|
72
90
|
vendor_updated_files(updated_files)
|
73
91
|
end
|
92
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
74
93
|
|
75
94
|
private
|
76
95
|
|
@@ -189,6 +208,15 @@ module Dependabot
|
|
189
208
|
)
|
190
209
|
end
|
191
210
|
|
211
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
212
|
+
def bun_locks
|
213
|
+
@bun_locks ||= T.let(
|
214
|
+
filtered_dependency_files
|
215
|
+
.select { |f| f.name.end_with?("bun.lock") },
|
216
|
+
T.nilable(T::Array[Dependabot::DependencyFile])
|
217
|
+
)
|
218
|
+
end
|
219
|
+
|
192
220
|
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
193
221
|
def shrinkwraps
|
194
222
|
@shrinkwraps ||= T.let(
|
@@ -217,6 +245,11 @@ module Dependabot
|
|
217
245
|
pnpm_lock.content != updated_pnpm_lock_content(pnpm_lock)
|
218
246
|
end
|
219
247
|
|
248
|
+
sig { params(bun_lock: Dependabot::DependencyFile).returns(T::Boolean) }
|
249
|
+
def bun_lock_changed?(bun_lock)
|
250
|
+
bun_lock.content != updated_bun_lock_content(bun_lock)
|
251
|
+
end
|
252
|
+
|
220
253
|
sig { params(package_lock: Dependabot::DependencyFile).returns(T::Boolean) }
|
221
254
|
def package_lock_changed?(package_lock)
|
222
255
|
package_lock.content != updated_lockfile_content(package_lock)
|
@@ -237,6 +270,8 @@ module Dependabot
|
|
237
270
|
end
|
238
271
|
end
|
239
272
|
|
273
|
+
# rubocop:disable Metrics/MethodLength
|
274
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
240
275
|
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
241
276
|
def updated_lockfiles
|
242
277
|
updated_files = []
|
@@ -259,6 +294,15 @@ module Dependabot
|
|
259
294
|
)
|
260
295
|
end
|
261
296
|
|
297
|
+
bun_locks.each do |bun_lock|
|
298
|
+
next unless bun_lock_changed?(bun_lock)
|
299
|
+
|
300
|
+
updated_files << updated_file(
|
301
|
+
file: bun_lock,
|
302
|
+
content: updated_bun_lock_content(bun_lock)
|
303
|
+
)
|
304
|
+
end
|
305
|
+
|
262
306
|
package_locks.each do |package_lock|
|
263
307
|
next unless package_lock_changed?(package_lock)
|
264
308
|
|
@@ -279,6 +323,8 @@ module Dependabot
|
|
279
323
|
|
280
324
|
updated_files
|
281
325
|
end
|
326
|
+
# rubocop:enable Metrics/MethodLength
|
327
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
282
328
|
|
283
329
|
sig { params(yarn_lock: Dependabot::DependencyFile).returns(String) }
|
284
330
|
def updated_yarn_lock_content(yarn_lock)
|
@@ -294,6 +340,13 @@ module Dependabot
|
|
294
340
|
pnpm_lockfile_updater.updated_pnpm_lock_content(pnpm_lock)
|
295
341
|
end
|
296
342
|
|
343
|
+
sig { params(bun_lock: Dependabot::DependencyFile).returns(String) }
|
344
|
+
def updated_bun_lock_content(bun_lock)
|
345
|
+
@updated_bun_lock_content ||= T.let({}, T.nilable(T::Hash[String, T.nilable(String)]))
|
346
|
+
@updated_bun_lock_content[bun_lock.name] ||=
|
347
|
+
bun_lockfile_updater.updated_bun_lock_content(bun_lock)
|
348
|
+
end
|
349
|
+
|
297
350
|
sig { returns(Dependabot::NpmAndYarn::FileUpdater::YarnLockfileUpdater) }
|
298
351
|
def yarn_lockfile_updater
|
299
352
|
@yarn_lockfile_updater ||= T.let(
|
@@ -320,6 +373,19 @@ module Dependabot
|
|
320
373
|
)
|
321
374
|
end
|
322
375
|
|
376
|
+
sig { returns(Dependabot::NpmAndYarn::FileUpdater::BunLockfileUpdater) }
|
377
|
+
def bun_lockfile_updater
|
378
|
+
@bun_lockfile_updater ||= T.let(
|
379
|
+
BunLockfileUpdater.new(
|
380
|
+
dependencies: dependencies,
|
381
|
+
dependency_files: dependency_files,
|
382
|
+
repo_contents_path: repo_contents_path,
|
383
|
+
credentials: credentials
|
384
|
+
),
|
385
|
+
T.nilable(Dependabot::NpmAndYarn::FileUpdater::BunLockfileUpdater)
|
386
|
+
)
|
387
|
+
end
|
388
|
+
|
323
389
|
sig { params(file: Dependabot::DependencyFile).returns(T.nilable(String)) }
|
324
390
|
def updated_lockfile_content(file)
|
325
391
|
@updated_lockfile_content ||= T.let({}, T.nilable(T::Hash[String, T.nilable(String)]))
|
@@ -29,6 +29,10 @@ module Dependabot
|
|
29
29
|
PNPM_DEFAULT_VERSION = PNPM_V9
|
30
30
|
PNPM_FALLBACK_VERSION = PNPM_V6
|
31
31
|
|
32
|
+
# BUN Version Constants
|
33
|
+
BUN_V1 = 1
|
34
|
+
BUN_DEFAULT_VERSION = BUN_V1
|
35
|
+
|
32
36
|
# YARN Version Constants
|
33
37
|
YARN_V3 = 3
|
34
38
|
YARN_V2 = 2
|
@@ -36,6 +40,9 @@ module Dependabot
|
|
36
40
|
YARN_DEFAULT_VERSION = YARN_V3
|
37
41
|
YARN_FALLBACK_VERSION = YARN_V1
|
38
42
|
|
43
|
+
# corepack supported package managers
|
44
|
+
SUPPORTED_COREPACK_PACKAGE_MANAGERS = %w(npm yarn pnpm).freeze
|
45
|
+
|
39
46
|
# Determines the npm version depends to the feature flag
|
40
47
|
# If the feature flag is enabled, we are going to use the minimum version npm 8
|
41
48
|
# Otherwise, we are going to use old versionining npm 6
|
@@ -159,6 +166,11 @@ module Dependabot
|
|
159
166
|
PNPM_FALLBACK_VERSION
|
160
167
|
end
|
161
168
|
|
169
|
+
sig { params(_bun_lock: T.nilable(DependencyFile)).returns(Integer) }
|
170
|
+
def self.bun_version_numeric(_bun_lock)
|
171
|
+
BUN_DEFAULT_VERSION
|
172
|
+
end
|
173
|
+
|
162
174
|
sig { params(key: String, default_value: String).returns(T.untyped) }
|
163
175
|
def self.fetch_yarnrc_yml_value(key, default_value)
|
164
176
|
if File.exist?(".yarnrc.yml") && (yarnrc = YAML.load_file(".yarnrc.yml"))
|
@@ -315,8 +327,8 @@ module Dependabot
|
|
315
327
|
package_manager_run_command(NpmPackageManager::NAME, command, fingerprint: fingerprint)
|
316
328
|
else
|
317
329
|
Dependabot::SharedHelpers.run_shell_command(
|
318
|
-
"
|
319
|
-
fingerprint: "
|
330
|
+
"npm #{command}",
|
331
|
+
fingerprint: "npm #{fingerprint}"
|
320
332
|
)
|
321
333
|
end
|
322
334
|
end
|
@@ -352,6 +364,35 @@ module Dependabot
|
|
352
364
|
raise
|
353
365
|
end
|
354
366
|
|
367
|
+
sig { returns(T.nilable(String)) }
|
368
|
+
def self.bun_version
|
369
|
+
version = run_bun_command("--version", fingerprint: "--version").strip
|
370
|
+
if version.include?("+")
|
371
|
+
version.split("+").first # Remove build info, if present
|
372
|
+
end
|
373
|
+
rescue StandardError => e
|
374
|
+
Dependabot.logger.error("Error retrieving Bun version: #{e.message}")
|
375
|
+
nil
|
376
|
+
end
|
377
|
+
|
378
|
+
sig { params(command: String, fingerprint: T.nilable(String)).returns(String) }
|
379
|
+
def self.run_bun_command(command, fingerprint: nil)
|
380
|
+
full_command = "bun #{command}"
|
381
|
+
|
382
|
+
Dependabot.logger.info("Running bun command: #{full_command}")
|
383
|
+
|
384
|
+
result = Dependabot::SharedHelpers.run_shell_command(
|
385
|
+
full_command,
|
386
|
+
fingerprint: "bun #{fingerprint || command}"
|
387
|
+
)
|
388
|
+
|
389
|
+
Dependabot.logger.info("Command executed successfully: #{full_command}")
|
390
|
+
result
|
391
|
+
rescue StandardError => e
|
392
|
+
Dependabot.logger.error("Error running bun command: #{full_command}, Error: #{e.message}")
|
393
|
+
raise
|
394
|
+
end
|
395
|
+
|
355
396
|
# Setup yarn and run a single yarn command returning stdout/stderr
|
356
397
|
sig { params(command: String, fingerprint: T.nilable(String)).returns(String) }
|
357
398
|
def self.run_yarn_command(command, fingerprint: nil)
|
@@ -446,6 +487,8 @@ module Dependabot
|
|
446
487
|
.returns(String)
|
447
488
|
end
|
448
489
|
def self.package_manager_install(name, version, env: {})
|
490
|
+
return "Corepack does not support #{name}" unless corepack_supported_package_manager?(name)
|
491
|
+
|
449
492
|
Dependabot::SharedHelpers.run_shell_command(
|
450
493
|
"corepack install #{name}@#{version} --global --cache-only",
|
451
494
|
fingerprint: "corepack install <name>@<version> --global --cache-only",
|
@@ -456,6 +499,8 @@ module Dependabot
|
|
456
499
|
# Prepare the package manager for use by using corepack
|
457
500
|
sig { params(name: String, version: String).returns(String) }
|
458
501
|
def self.package_manager_activate(name, version)
|
502
|
+
return "Corepack does not support #{name}" unless corepack_supported_package_manager?(name)
|
503
|
+
|
459
504
|
Dependabot::SharedHelpers.run_shell_command(
|
460
505
|
"corepack prepare #{name}@#{version} --activate",
|
461
506
|
fingerprint: "corepack prepare <name>@<version> --activate"
|
@@ -496,6 +541,8 @@ module Dependabot
|
|
496
541
|
).returns(String)
|
497
542
|
end
|
498
543
|
def self.package_manager_run_command(name, command, fingerprint: nil)
|
544
|
+
return run_bun_command(command, fingerprint: fingerprint) if name == BunPackageManager::NAME
|
545
|
+
|
499
546
|
full_command = "corepack #{name} #{command}"
|
500
547
|
|
501
548
|
result = Dependabot::SharedHelpers.run_shell_command(
|
@@ -526,6 +573,11 @@ module Dependabot
|
|
526
573
|
dependency
|
527
574
|
end
|
528
575
|
end
|
576
|
+
|
577
|
+
sig { params(name: String).returns(T::Boolean) }
|
578
|
+
def self.corepack_supported_package_manager?(name)
|
579
|
+
SUPPORTED_COREPACK_PACKAGE_MANAGERS.include?(name)
|
580
|
+
end
|
529
581
|
end
|
530
582
|
end
|
531
583
|
end
|