dependabot-bun 0.296.2 → 0.296.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/helpers/.eslintrc +11 -0
- data/helpers/README.md +29 -0
- data/helpers/build +26 -0
- data/helpers/jest.config.js +5 -0
- data/helpers/lib/npm/conflicting-dependency-parser.js +78 -0
- data/helpers/lib/npm/index.js +9 -0
- data/helpers/lib/npm/vulnerability-auditor.js +291 -0
- data/helpers/lib/npm6/helpers.js +25 -0
- data/helpers/lib/npm6/index.js +9 -0
- data/helpers/lib/npm6/peer-dependency-checker.js +111 -0
- data/helpers/lib/npm6/remove-dependencies-from-lockfile.js +22 -0
- data/helpers/lib/npm6/subdependency-updater.js +78 -0
- data/helpers/lib/npm6/updater.js +199 -0
- data/helpers/lib/pnpm/index.js +5 -0
- data/helpers/lib/pnpm/lockfile-parser.js +82 -0
- data/helpers/lib/yarn/conflicting-dependency-parser.js +176 -0
- data/helpers/lib/yarn/fix-duplicates.js +80 -0
- data/helpers/lib/yarn/helpers.js +54 -0
- data/helpers/lib/yarn/index.js +14 -0
- data/helpers/lib/yarn/lockfile-parser.js +21 -0
- data/helpers/lib/yarn/peer-dependency-checker.js +132 -0
- data/helpers/lib/yarn/replace-lockfile-declaration.js +57 -0
- data/helpers/lib/yarn/subdependency-updater.js +83 -0
- data/helpers/lib/yarn/updater.js +209 -0
- data/helpers/package-lock.json +28519 -0
- data/helpers/package.json +29 -0
- data/helpers/patches/npm++pacote+9.5.12.patch +14 -0
- data/helpers/run.js +30 -0
- data/helpers/test/npm6/conflicting-dependency-parser.test.js +66 -0
- data/helpers/test/npm6/fixtures/conflicting-dependency-parser/deeply-nested/package-lock.json +591 -0
- data/helpers/test/npm6/fixtures/conflicting-dependency-parser/deeply-nested/package.json +14 -0
- data/helpers/test/npm6/fixtures/conflicting-dependency-parser/nested/package-lock.json +188 -0
- data/helpers/test/npm6/fixtures/conflicting-dependency-parser/nested/package.json +14 -0
- data/helpers/test/npm6/fixtures/conflicting-dependency-parser/simple/package-lock.json +27 -0
- data/helpers/test/npm6/fixtures/conflicting-dependency-parser/simple/package.json +14 -0
- data/helpers/test/npm6/fixtures/updater/original/package-lock.json +16 -0
- data/helpers/test/npm6/fixtures/updater/original/package.json +9 -0
- data/helpers/test/npm6/fixtures/updater/updated/package-lock.json +16 -0
- data/helpers/test/npm6/helpers.js +21 -0
- data/helpers/test/npm6/updater.test.js +30 -0
- data/helpers/test/pnpm/fixtures/parser/empty_version/pnpm-lock.yaml +72 -0
- data/helpers/test/pnpm/fixtures/parser/no_lockfile_change/pnpm-lock.yaml +2744 -0
- data/helpers/test/pnpm/fixtures/parser/only_dev_dependencies/pnpm-lock.yaml +16 -0
- data/helpers/test/pnpm/fixtures/parser/peer_disambiguation/pnpm-lock.yaml +855 -0
- data/helpers/test/pnpm/lockfile-parser.test.js +62 -0
- data/helpers/test/yarn/conflicting-dependency-parser.test.js +83 -0
- data/helpers/test/yarn/fixtures/conflicting-dependency-parser/deeply-nested/package.json +14 -0
- data/helpers/test/yarn/fixtures/conflicting-dependency-parser/deeply-nested/yarn.lock +496 -0
- data/helpers/test/yarn/fixtures/conflicting-dependency-parser/dev-dependencies/package.json +14 -0
- data/helpers/test/yarn/fixtures/conflicting-dependency-parser/dev-dependencies/yarn.lock +21 -0
- data/helpers/test/yarn/fixtures/conflicting-dependency-parser/nested/package.json +14 -0
- data/helpers/test/yarn/fixtures/conflicting-dependency-parser/nested/yarn.lock +183 -0
- data/helpers/test/yarn/fixtures/conflicting-dependency-parser/simple/package.json +14 -0
- data/helpers/test/yarn/fixtures/conflicting-dependency-parser/simple/yarn.lock +21 -0
- data/helpers/test/yarn/fixtures/updater/illegal_character/package.json +8 -0
- data/helpers/test/yarn/fixtures/updater/illegal_character/yarn.lock +14 -0
- data/helpers/test/yarn/fixtures/updater/original/package.json +6 -0
- data/helpers/test/yarn/fixtures/updater/original/yarn.lock +11 -0
- data/helpers/test/yarn/fixtures/updater/updated/yarn.lock +12 -0
- data/helpers/test/yarn/fixtures/updater/with-version-comments/package.json +5 -0
- data/helpers/test/yarn/fixtures/updater/with-version-comments/yarn.lock +13 -0
- data/helpers/test/yarn/helpers.js +18 -0
- data/helpers/test/yarn/updater.test.js +117 -0
- data/lib/dependabot/bun/bun_package_manager.rb +47 -0
- data/lib/dependabot/bun/constraint_helper.rb +359 -0
- data/lib/dependabot/bun/dependency_files_filterer.rb +157 -0
- data/lib/dependabot/bun/file_fetcher/path_dependency_builder.rb +184 -0
- data/lib/dependabot/bun/file_fetcher.rb +402 -0
- data/lib/dependabot/bun/file_parser/bun_lock.rb +140 -0
- data/lib/dependabot/bun/file_parser/lockfile_parser.rb +105 -0
- data/lib/dependabot/bun/file_parser.rb +477 -0
- data/lib/dependabot/bun/file_updater/bun_lockfile_updater.rb +144 -0
- data/lib/dependabot/bun/file_updater/npmrc_builder.rb +256 -0
- data/lib/dependabot/bun/file_updater/package_json_preparer.rb +88 -0
- data/lib/dependabot/bun/file_updater/package_json_updater.rb +378 -0
- data/lib/dependabot/bun/file_updater.rb +203 -0
- data/lib/dependabot/bun/helpers.rb +93 -0
- data/lib/dependabot/bun/language.rb +45 -0
- data/lib/dependabot/bun/metadata_finder.rb +214 -0
- data/lib/dependabot/bun/native_helpers.rb +19 -0
- data/lib/dependabot/bun/package_manager.rb +280 -0
- data/lib/dependabot/bun/package_name.rb +118 -0
- data/lib/dependabot/bun/pnpm_package_manager.rb +55 -0
- data/lib/dependabot/bun/registry_helper.rb +188 -0
- data/lib/dependabot/bun/registry_parser.rb +93 -0
- data/lib/dependabot/bun/requirement.rb +146 -0
- data/lib/dependabot/bun/sub_dependency_files_filterer.rb +82 -0
- data/lib/dependabot/bun/update_checker/conflicting_dependency_resolver.rb +59 -0
- data/lib/dependabot/bun/update_checker/dependency_files_builder.rb +79 -0
- data/lib/dependabot/bun/update_checker/latest_version_finder.rb +448 -0
- data/lib/dependabot/bun/update_checker/library_detector.rb +76 -0
- data/lib/dependabot/bun/update_checker/registry_finder.rb +279 -0
- data/lib/dependabot/bun/update_checker/requirements_updater.rb +206 -0
- data/lib/dependabot/bun/update_checker/subdependency_version_resolver.rb +154 -0
- data/lib/dependabot/bun/update_checker/version_resolver.rb +583 -0
- data/lib/dependabot/bun/update_checker/vulnerability_auditor.rb +164 -0
- data/lib/dependabot/bun/update_checker.rb +455 -0
- data/lib/dependabot/bun/version.rb +138 -0
- data/lib/dependabot/bun/version_selector.rb +61 -0
- data/lib/dependabot/bun.rb +337 -35
- metadata +108 -65
- data/lib/dependabot/javascript/bun/file_fetcher.rb +0 -77
- data/lib/dependabot/javascript/bun/file_parser/bun_lock.rb +0 -156
- data/lib/dependabot/javascript/bun/file_parser/lockfile_parser.rb +0 -55
- data/lib/dependabot/javascript/bun/file_parser.rb +0 -74
- data/lib/dependabot/javascript/bun/file_updater/lockfile_updater.rb +0 -138
- data/lib/dependabot/javascript/bun/file_updater.rb +0 -75
- data/lib/dependabot/javascript/bun/helpers.rb +0 -72
- data/lib/dependabot/javascript/bun/package_manager.rb +0 -48
- data/lib/dependabot/javascript/bun/requirement.rb +0 -11
- data/lib/dependabot/javascript/bun/update_checker/conflicting_dependency_resolver.rb +0 -64
- data/lib/dependabot/javascript/bun/update_checker/dependency_files_builder.rb +0 -47
- data/lib/dependabot/javascript/bun/update_checker/latest_version_finder.rb +0 -450
- data/lib/dependabot/javascript/bun/update_checker/library_detector.rb +0 -76
- data/lib/dependabot/javascript/bun/update_checker/requirements_updater.rb +0 -203
- data/lib/dependabot/javascript/bun/update_checker/subdependency_version_resolver.rb +0 -144
- data/lib/dependabot/javascript/bun/update_checker/version_resolver.rb +0 -525
- data/lib/dependabot/javascript/bun/update_checker/vulnerability_auditor.rb +0 -165
- data/lib/dependabot/javascript/bun/update_checker.rb +0 -440
- data/lib/dependabot/javascript/bun/version.rb +0 -11
- data/lib/dependabot/javascript/shared/constraint_helper.rb +0 -359
- data/lib/dependabot/javascript/shared/dependency_files_filterer.rb +0 -164
- data/lib/dependabot/javascript/shared/file_fetcher.rb +0 -283
- data/lib/dependabot/javascript/shared/file_parser/lockfile_parser.rb +0 -106
- data/lib/dependabot/javascript/shared/file_parser.rb +0 -454
- data/lib/dependabot/javascript/shared/file_updater/npmrc_builder.rb +0 -394
- data/lib/dependabot/javascript/shared/file_updater/package_json_preparer.rb +0 -87
- data/lib/dependabot/javascript/shared/file_updater/package_json_updater.rb +0 -376
- data/lib/dependabot/javascript/shared/file_updater.rb +0 -179
- data/lib/dependabot/javascript/shared/language.rb +0 -45
- data/lib/dependabot/javascript/shared/metadata_finder.rb +0 -209
- data/lib/dependabot/javascript/shared/native_helpers.rb +0 -21
- data/lib/dependabot/javascript/shared/package_manager_detector.rb +0 -72
- data/lib/dependabot/javascript/shared/package_name.rb +0 -118
- data/lib/dependabot/javascript/shared/registry_helper.rb +0 -190
- data/lib/dependabot/javascript/shared/registry_parser.rb +0 -93
- data/lib/dependabot/javascript/shared/requirement.rb +0 -144
- data/lib/dependabot/javascript/shared/sub_dependency_files_filterer.rb +0 -79
- data/lib/dependabot/javascript/shared/update_checker/dependency_files_builder.rb +0 -87
- data/lib/dependabot/javascript/shared/update_checker/registry_finder.rb +0 -358
- data/lib/dependabot/javascript/shared/version.rb +0 -133
- data/lib/dependabot/javascript/shared/version_selector.rb +0 -60
- data/lib/dependabot/javascript.rb +0 -39
@@ -0,0 +1,378 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "sorbet-runtime"
|
5
|
+
|
6
|
+
require "dependabot/bun/file_updater"
|
7
|
+
|
8
|
+
module Dependabot
|
9
|
+
module Bun
|
10
|
+
class FileUpdater < Dependabot::FileUpdaters::Base
|
11
|
+
class PackageJsonUpdater
|
12
|
+
extend T::Sig
|
13
|
+
|
14
|
+
LOCAL_PACKAGE = T.let([/portal:/, /file:/].freeze, T::Array[Regexp])
|
15
|
+
|
16
|
+
PATCH_PACKAGE = T.let([/patch:/].freeze, T::Array[Regexp])
|
17
|
+
|
18
|
+
sig do
|
19
|
+
params(
|
20
|
+
package_json: Dependabot::DependencyFile,
|
21
|
+
dependencies: T::Array[Dependabot::Dependency]
|
22
|
+
).void
|
23
|
+
end
|
24
|
+
def initialize(package_json:, dependencies:)
|
25
|
+
@package_json = package_json
|
26
|
+
@dependencies = dependencies
|
27
|
+
end
|
28
|
+
|
29
|
+
sig { returns(Dependabot::DependencyFile) }
|
30
|
+
def updated_package_json
|
31
|
+
updated_file = package_json.dup
|
32
|
+
updated_file.content = updated_package_json_content
|
33
|
+
updated_file
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
sig { returns(Dependabot::DependencyFile) }
|
39
|
+
attr_reader :package_json
|
40
|
+
|
41
|
+
sig { returns(T::Array[Dependabot::Dependency]) }
|
42
|
+
attr_reader :dependencies
|
43
|
+
|
44
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
45
|
+
|
46
|
+
sig { returns(T.nilable(String)) }
|
47
|
+
def updated_package_json_content
|
48
|
+
# checks if we are updating single dependency in package.json
|
49
|
+
unique_deps_count = dependencies.map(&:name).to_a.uniq.compact.length
|
50
|
+
|
51
|
+
dependencies.reduce(package_json.content.dup) do |content, dep|
|
52
|
+
updated_requirements(dep)&.each do |new_req|
|
53
|
+
old_req = old_requirement(dep, new_req)
|
54
|
+
|
55
|
+
new_content = update_package_json_declaration(
|
56
|
+
package_json_content: T.must(content),
|
57
|
+
dependency_name: dep.name,
|
58
|
+
old_req: old_req,
|
59
|
+
new_req: new_req
|
60
|
+
)
|
61
|
+
|
62
|
+
if Dependabot::Experiments.enabled?(:avoid_duplicate_updates_package_json) &&
|
63
|
+
(content == new_content && unique_deps_count > 1)
|
64
|
+
|
65
|
+
# (we observed that) package.json does not always contains the same dependencies compared to
|
66
|
+
# "dependencies" list, for example, dependencies object can contain same name dependency "dep"=> "1.0.0"
|
67
|
+
# and "dev" => "1.0.1" while package.json can only contain "dep" => "1.0.0",the other dependency is
|
68
|
+
# not present in package.json so we don't have to update it, this is most likely (as observed)
|
69
|
+
# a transitive dependency which only needs update in lockfile, So we avoid throwing exception and let
|
70
|
+
# the update continue.
|
71
|
+
|
72
|
+
Dependabot.logger.info("experiment: avoid_duplicate_updates_package_json.
|
73
|
+
Updating package.json for #{dep.name} ")
|
74
|
+
|
75
|
+
raise "Expected content to change!"
|
76
|
+
end
|
77
|
+
|
78
|
+
if !Dependabot::Experiments.enabled?(:avoid_duplicate_updates_package_json) && (content == new_content)
|
79
|
+
raise "Expected content to change!"
|
80
|
+
end
|
81
|
+
|
82
|
+
content = new_content
|
83
|
+
end
|
84
|
+
|
85
|
+
new_requirements(dep).each do |new_req|
|
86
|
+
old_req = old_requirement(dep, new_req)
|
87
|
+
|
88
|
+
content = update_package_json_resolutions(
|
89
|
+
package_json_content: T.must(content),
|
90
|
+
new_req: new_req,
|
91
|
+
dependency: dep,
|
92
|
+
old_req: old_req
|
93
|
+
)
|
94
|
+
end
|
95
|
+
|
96
|
+
content
|
97
|
+
end
|
98
|
+
end
|
99
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
100
|
+
sig do
|
101
|
+
params(
|
102
|
+
dependency: Dependabot::Dependency,
|
103
|
+
new_requirement: T::Hash[Symbol, T.untyped]
|
104
|
+
)
|
105
|
+
.returns(T.nilable(T::Hash[Symbol, T.untyped]))
|
106
|
+
end
|
107
|
+
def old_requirement(dependency, new_requirement)
|
108
|
+
T.must(dependency.previous_requirements)
|
109
|
+
.select { |r| r[:file] == package_json.name }
|
110
|
+
.find { |r| r[:groups] == new_requirement[:groups] }
|
111
|
+
end
|
112
|
+
|
113
|
+
sig { params(dependency: Dependabot::Dependency).returns(T::Array[T::Hash[Symbol, T.untyped]]) }
|
114
|
+
def new_requirements(dependency)
|
115
|
+
dependency.requirements.select { |r| r[:file] == package_json.name }
|
116
|
+
end
|
117
|
+
|
118
|
+
sig { params(dependency: Dependabot::Dependency).returns(T.nilable(T::Array[T::Hash[Symbol, T.untyped]])) }
|
119
|
+
def updated_requirements(dependency)
|
120
|
+
return unless dependency.previous_requirements
|
121
|
+
|
122
|
+
preliminary_check_for_update(dependency)
|
123
|
+
|
124
|
+
updated_requirement_pairs =
|
125
|
+
dependency.requirements.zip(T.must(dependency.previous_requirements))
|
126
|
+
.reject do |new_req, old_req|
|
127
|
+
next true if new_req == old_req
|
128
|
+
next false unless old_req&.fetch(:source).nil?
|
129
|
+
|
130
|
+
new_req[:requirement] == old_req&.fetch(:requirement)
|
131
|
+
end
|
132
|
+
|
133
|
+
updated_requirement_pairs
|
134
|
+
.map(&:first)
|
135
|
+
.select { |r| r[:file] == package_json.name }
|
136
|
+
end
|
137
|
+
|
138
|
+
sig do
|
139
|
+
params(
|
140
|
+
package_json_content: String,
|
141
|
+
new_req: T::Hash[Symbol, T.untyped],
|
142
|
+
dependency_name: String,
|
143
|
+
old_req: T.nilable(T::Hash[Symbol, T.untyped])
|
144
|
+
)
|
145
|
+
.returns(String)
|
146
|
+
end
|
147
|
+
def update_package_json_declaration(package_json_content:, new_req:, dependency_name:, old_req:)
|
148
|
+
original_line = declaration_line(
|
149
|
+
dependency_name: dependency_name,
|
150
|
+
dependency_req: old_req,
|
151
|
+
content: package_json_content
|
152
|
+
)
|
153
|
+
|
154
|
+
replacement_line = replacement_declaration_line(
|
155
|
+
original_line: original_line,
|
156
|
+
old_req: old_req,
|
157
|
+
new_req: new_req
|
158
|
+
)
|
159
|
+
|
160
|
+
groups = new_req.fetch(:groups)
|
161
|
+
|
162
|
+
update_package_json_sections(
|
163
|
+
groups,
|
164
|
+
package_json_content,
|
165
|
+
original_line,
|
166
|
+
replacement_line
|
167
|
+
)
|
168
|
+
end
|
169
|
+
|
170
|
+
# For full details on how Yarn resolutions work, see
|
171
|
+
# https://github.com/yarnpkg/rfcs/blob/master/implemented/
|
172
|
+
# 0000-selective-versions-resolutions.md
|
173
|
+
sig do
|
174
|
+
params(
|
175
|
+
package_json_content: String,
|
176
|
+
new_req: T::Hash[Symbol, T.untyped],
|
177
|
+
dependency: Dependabot::Dependency,
|
178
|
+
old_req: T.nilable(T::Hash[Symbol, T.untyped])
|
179
|
+
)
|
180
|
+
.returns(String)
|
181
|
+
end
|
182
|
+
def update_package_json_resolutions(package_json_content:, new_req:, dependency:, old_req:)
|
183
|
+
dep = dependency
|
184
|
+
parsed_json_content = JSON.parse(package_json_content)
|
185
|
+
resolutions =
|
186
|
+
parsed_json_content.fetch("resolutions", parsed_json_content.dig("pnpm", "overrides") || {})
|
187
|
+
.reject { |_, v| v != old_req && v != dep.previous_version }
|
188
|
+
.select { |k, _| k == dep.name || k.end_with?("/#{dep.name}") }
|
189
|
+
|
190
|
+
return package_json_content unless resolutions.any?
|
191
|
+
|
192
|
+
content = package_json_content
|
193
|
+
resolutions.each do |_, resolution|
|
194
|
+
original_line = declaration_line(
|
195
|
+
dependency_name: dep.name,
|
196
|
+
dependency_req: { requirement: resolution },
|
197
|
+
content: content
|
198
|
+
)
|
199
|
+
|
200
|
+
new_resolution = resolution == old_req ? new_req : dep.version
|
201
|
+
|
202
|
+
replacement_line = replacement_declaration_line(
|
203
|
+
original_line: original_line,
|
204
|
+
old_req: { requirement: resolution },
|
205
|
+
new_req: { requirement: new_resolution }
|
206
|
+
)
|
207
|
+
|
208
|
+
content = update_package_json_sections(
|
209
|
+
%w(resolutions overrides), content, original_line, replacement_line
|
210
|
+
)
|
211
|
+
end
|
212
|
+
content
|
213
|
+
end
|
214
|
+
|
215
|
+
sig do
|
216
|
+
params(
|
217
|
+
dependency_name: String,
|
218
|
+
dependency_req: T.nilable(T::Hash[Symbol, T.untyped]),
|
219
|
+
content: String
|
220
|
+
)
|
221
|
+
.returns(String)
|
222
|
+
end
|
223
|
+
def declaration_line(dependency_name:, dependency_req:, content:)
|
224
|
+
git_dependency = dependency_req&.dig(:source, :type) == "git"
|
225
|
+
|
226
|
+
unless git_dependency
|
227
|
+
requirement = dependency_req&.fetch(:requirement)
|
228
|
+
return content.match(/"#{Regexp.escape(dependency_name)}"\s*:\s*
|
229
|
+
"#{Regexp.escape(requirement)}"/x).to_s
|
230
|
+
end
|
231
|
+
|
232
|
+
username, repo =
|
233
|
+
dependency_req&.dig(:source, :url)&.split("/")&.last(2)
|
234
|
+
|
235
|
+
content.match(
|
236
|
+
%r{"#{Regexp.escape(dependency_name)}"\s*:\s*
|
237
|
+
".*?#{Regexp.escape(username)}/#{Regexp.escape(repo)}.*"}x
|
238
|
+
).to_s
|
239
|
+
end
|
240
|
+
|
241
|
+
sig do
|
242
|
+
params(
|
243
|
+
original_line: String,
|
244
|
+
old_req: T.nilable(T::Hash[Symbol, T.untyped]),
|
245
|
+
new_req: T::Hash[Symbol, T.untyped]
|
246
|
+
)
|
247
|
+
.returns(String)
|
248
|
+
end
|
249
|
+
def replacement_declaration_line(original_line:, old_req:, new_req:)
|
250
|
+
was_git_dependency = old_req&.dig(:source, :type) == "git"
|
251
|
+
now_git_dependency = new_req.dig(:source, :type) == "git"
|
252
|
+
|
253
|
+
unless was_git_dependency
|
254
|
+
return original_line.gsub(
|
255
|
+
%("#{old_req&.fetch(:requirement)}"),
|
256
|
+
%("#{new_req.fetch(:requirement)}")
|
257
|
+
)
|
258
|
+
end
|
259
|
+
|
260
|
+
unless now_git_dependency
|
261
|
+
return original_line.gsub(
|
262
|
+
/(?<=\s").*[^\\](?=")/,
|
263
|
+
new_req.fetch(:requirement)
|
264
|
+
)
|
265
|
+
end
|
266
|
+
|
267
|
+
if original_line.match?(/#[\^~=<>]|semver:/)
|
268
|
+
return update_git_semver_requirement(
|
269
|
+
original_line: original_line,
|
270
|
+
old_req: old_req,
|
271
|
+
new_req: new_req
|
272
|
+
)
|
273
|
+
end
|
274
|
+
|
275
|
+
original_line.gsub(
|
276
|
+
%(##{old_req&.dig(:source, :ref)}"),
|
277
|
+
%(##{new_req.dig(:source, :ref)}")
|
278
|
+
)
|
279
|
+
end
|
280
|
+
|
281
|
+
sig do
|
282
|
+
params(
|
283
|
+
original_line: String,
|
284
|
+
old_req: T.nilable(T::Hash[Symbol, String]),
|
285
|
+
new_req: T::Hash[Symbol, String]
|
286
|
+
)
|
287
|
+
.returns(String)
|
288
|
+
end
|
289
|
+
def update_git_semver_requirement(original_line:, old_req:, new_req:)
|
290
|
+
if original_line.include?("semver:")
|
291
|
+
return original_line.gsub(
|
292
|
+
%(semver:#{old_req&.fetch(:requirement)}"),
|
293
|
+
%(semver:#{new_req.fetch(:requirement)}")
|
294
|
+
)
|
295
|
+
end
|
296
|
+
|
297
|
+
raise "Not a semver req!" unless original_line.match?(/#[\^~=<>]/)
|
298
|
+
|
299
|
+
original_line.gsub(
|
300
|
+
%(##{old_req&.fetch(:requirement)}"),
|
301
|
+
%(##{new_req.fetch(:requirement)}")
|
302
|
+
)
|
303
|
+
end
|
304
|
+
|
305
|
+
sig do
|
306
|
+
params(
|
307
|
+
sections: T::Array[String],
|
308
|
+
content: String,
|
309
|
+
old_line: String,
|
310
|
+
new_line: String
|
311
|
+
)
|
312
|
+
.returns(String)
|
313
|
+
end
|
314
|
+
def update_package_json_sections(sections, content, old_line, new_line)
|
315
|
+
# Currently, Dependabot doesn't update peerDependencies. However,
|
316
|
+
# if a development dependency is being updated and its requirement
|
317
|
+
# matches the requirement on a peer dependency we probably want to
|
318
|
+
# update the peer too.
|
319
|
+
#
|
320
|
+
# TODO: Move this logic to the UpdateChecker (and parse peer deps)
|
321
|
+
sections += ["peerDependencies"]
|
322
|
+
sections_regex = /#{sections.join('|')}/
|
323
|
+
|
324
|
+
declaration_blocks = T.let([], T::Array[String])
|
325
|
+
|
326
|
+
content.scan(/['"]#{sections_regex}['"]\s*:\s*\{/m) do
|
327
|
+
mtch = T.must(Regexp.last_match)
|
328
|
+
declaration_blocks <<
|
329
|
+
(mtch.to_s + T.must(mtch.post_match[0..closing_bracket_index(mtch.post_match)]))
|
330
|
+
end
|
331
|
+
|
332
|
+
declaration_blocks.reduce(content.dup) do |new_content, block|
|
333
|
+
updated_block = block.sub(old_line, new_line)
|
334
|
+
new_content.sub(block, updated_block)
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
sig { params(string: String).returns(Integer) }
|
339
|
+
def closing_bracket_index(string)
|
340
|
+
closes_required = 1
|
341
|
+
|
342
|
+
string.chars.each_with_index do |char, index|
|
343
|
+
closes_required += 1 if char == "{"
|
344
|
+
closes_required -= 1 if char == "}"
|
345
|
+
return index if closes_required.zero?
|
346
|
+
end
|
347
|
+
|
348
|
+
0
|
349
|
+
end
|
350
|
+
|
351
|
+
sig { params(dependency: Dependabot::Dependency).void }
|
352
|
+
def preliminary_check_for_update(dependency)
|
353
|
+
T.must(dependency.previous_requirements).each do |req, _dep|
|
354
|
+
next if req.fetch(:requirement).nil?
|
355
|
+
|
356
|
+
# some deps are patched with local patches, we don't need to update them
|
357
|
+
if req.fetch(:requirement).match?(Regexp.union(PATCH_PACKAGE))
|
358
|
+
Dependabot.logger.info("Func: updated_requirements. dependency patched #{dependency.name}," \
|
359
|
+
" Requirement: '#{req.fetch(:requirement)}'")
|
360
|
+
|
361
|
+
raise DependencyFileNotResolvable,
|
362
|
+
"Dependency is patched locally, Update not required."
|
363
|
+
end
|
364
|
+
|
365
|
+
# some deps are added as local packages, we don't need to update them as they are referred to a local path
|
366
|
+
next unless req.fetch(:requirement).match?(Regexp.union(LOCAL_PACKAGE))
|
367
|
+
|
368
|
+
Dependabot.logger.info("Func: updated_requirements. local package #{dependency.name}," \
|
369
|
+
" Requirement: '#{req.fetch(:requirement)}'")
|
370
|
+
|
371
|
+
raise DependencyFileNotResolvable,
|
372
|
+
"Local package, Update not required."
|
373
|
+
end
|
374
|
+
end
|
375
|
+
end
|
376
|
+
end
|
377
|
+
end
|
378
|
+
end
|
@@ -0,0 +1,203 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "dependabot/file_updaters"
|
5
|
+
require "dependabot/file_updaters/base"
|
6
|
+
require "dependabot/file_updaters/vendor_updater"
|
7
|
+
require "dependabot/file_updaters/artifact_updater"
|
8
|
+
require "dependabot/bun/dependency_files_filterer"
|
9
|
+
require "dependabot/bun/sub_dependency_files_filterer"
|
10
|
+
require "sorbet-runtime"
|
11
|
+
|
12
|
+
module Dependabot
|
13
|
+
module Bun
|
14
|
+
class FileUpdater < Dependabot::FileUpdaters::Base
|
15
|
+
extend T::Sig
|
16
|
+
|
17
|
+
require_relative "file_updater/package_json_updater"
|
18
|
+
require_relative "file_updater/bun_lockfile_updater"
|
19
|
+
|
20
|
+
class NoChangeError < StandardError
|
21
|
+
extend T::Sig
|
22
|
+
|
23
|
+
sig { params(message: String, error_context: T::Hash[Symbol, T.untyped]).void }
|
24
|
+
def initialize(message:, error_context:)
|
25
|
+
super(message)
|
26
|
+
@error_context = error_context
|
27
|
+
end
|
28
|
+
|
29
|
+
sig { returns(T::Hash[Symbol, T.untyped]) }
|
30
|
+
def sentry_context
|
31
|
+
{ extra: @error_context }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
sig { override.returns(T::Array[Regexp]) }
|
36
|
+
def self.updated_files_regex
|
37
|
+
[
|
38
|
+
%r{^(?:.*/)?package\.json$},
|
39
|
+
%r{^(?:.*/)?\.pnp\.(?:js|cjs)$} # Matches .pnp.js or .pnp.cjs files
|
40
|
+
]
|
41
|
+
end
|
42
|
+
|
43
|
+
sig { override.returns(T::Array[DependencyFile]) }
|
44
|
+
def updated_dependency_files
|
45
|
+
updated_files = T.let([], T::Array[DependencyFile])
|
46
|
+
|
47
|
+
updated_files += updated_manifest_files
|
48
|
+
updated_files += updated_lockfiles
|
49
|
+
|
50
|
+
if updated_files.none?
|
51
|
+
|
52
|
+
raise NoChangeError.new(
|
53
|
+
message: "No files were updated!",
|
54
|
+
error_context: error_context(updated_files: updated_files)
|
55
|
+
)
|
56
|
+
end
|
57
|
+
|
58
|
+
sorted_updated_files = updated_files.sort_by(&:name)
|
59
|
+
if sorted_updated_files == filtered_dependency_files.sort_by(&:name)
|
60
|
+
raise NoChangeError.new(
|
61
|
+
message: "Updated files are unchanged!",
|
62
|
+
error_context: error_context(updated_files: updated_files)
|
63
|
+
)
|
64
|
+
end
|
65
|
+
|
66
|
+
vendor_updated_files(updated_files)
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
sig { params(updated_files: T::Array[Dependabot::DependencyFile]).returns(T::Array[Dependabot::DependencyFile]) }
|
72
|
+
def vendor_updated_files(updated_files)
|
73
|
+
base_dir = T.must(updated_files.first).directory
|
74
|
+
pnp_updater.updated_files(base_directory: base_dir, only_paths: [".pnp.cjs", ".pnp.data.json"]).each do |file|
|
75
|
+
updated_files << file
|
76
|
+
end
|
77
|
+
|
78
|
+
updated_files
|
79
|
+
end
|
80
|
+
|
81
|
+
sig { returns(Dependabot::FileUpdaters::ArtifactUpdater) }
|
82
|
+
def pnp_updater
|
83
|
+
Dependabot::FileUpdaters::ArtifactUpdater.new(
|
84
|
+
repo_contents_path: repo_contents_path,
|
85
|
+
target_directory: "./"
|
86
|
+
)
|
87
|
+
end
|
88
|
+
|
89
|
+
sig { returns(T::Array[DependencyFile]) }
|
90
|
+
def filtered_dependency_files
|
91
|
+
@filtered_dependency_files ||= T.let(
|
92
|
+
if dependencies.any?(&:top_level?)
|
93
|
+
DependencyFilesFilterer.new(
|
94
|
+
dependency_files: dependency_files,
|
95
|
+
updated_dependencies: dependencies
|
96
|
+
).files_requiring_update
|
97
|
+
else
|
98
|
+
SubDependencyFilesFilterer.new(
|
99
|
+
dependency_files: dependency_files,
|
100
|
+
updated_dependencies: dependencies
|
101
|
+
).files_requiring_update
|
102
|
+
end, T.nilable(T::Array[DependencyFile])
|
103
|
+
)
|
104
|
+
end
|
105
|
+
|
106
|
+
sig { override.void }
|
107
|
+
def check_required_files
|
108
|
+
raise DependencyFileNotFound.new(nil, "package.json not found.") unless get_original_file("package.json")
|
109
|
+
end
|
110
|
+
|
111
|
+
sig { params(updated_files: T::Array[DependencyFile]).returns(T::Hash[Symbol, T.untyped]) }
|
112
|
+
def error_context(updated_files:)
|
113
|
+
{
|
114
|
+
dependencies: dependencies.map(&:to_h),
|
115
|
+
updated_files: updated_files.map(&:name),
|
116
|
+
dependency_files: dependency_files.map(&:name)
|
117
|
+
}
|
118
|
+
end
|
119
|
+
|
120
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
121
|
+
def bun_locks
|
122
|
+
@bun_locks ||= T.let(
|
123
|
+
filtered_dependency_files
|
124
|
+
.select { |f| f.name.end_with?("bun.lock") },
|
125
|
+
T.nilable(T::Array[Dependabot::DependencyFile])
|
126
|
+
)
|
127
|
+
end
|
128
|
+
|
129
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
130
|
+
def package_files
|
131
|
+
@package_files ||= T.let(
|
132
|
+
filtered_dependency_files.select do |f|
|
133
|
+
f.name.end_with?("package.json")
|
134
|
+
end, T.nilable(T::Array[DependencyFile])
|
135
|
+
)
|
136
|
+
end
|
137
|
+
|
138
|
+
sig { params(bun_lock: Dependabot::DependencyFile).returns(T::Boolean) }
|
139
|
+
def bun_lock_changed?(bun_lock)
|
140
|
+
bun_lock.content != updated_bun_lock_content(bun_lock)
|
141
|
+
end
|
142
|
+
|
143
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
144
|
+
def updated_manifest_files
|
145
|
+
package_files.filter_map do |file|
|
146
|
+
updated_content = updated_package_json_content(file)
|
147
|
+
next if updated_content == file.content
|
148
|
+
|
149
|
+
updated_file(file: file, content: T.must(updated_content))
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
154
|
+
def updated_lockfiles
|
155
|
+
updated_files = []
|
156
|
+
|
157
|
+
bun_locks.each do |bun_lock|
|
158
|
+
next unless bun_lock_changed?(bun_lock)
|
159
|
+
|
160
|
+
updated_files << updated_file(
|
161
|
+
file: bun_lock,
|
162
|
+
content: updated_bun_lock_content(bun_lock)
|
163
|
+
)
|
164
|
+
end
|
165
|
+
|
166
|
+
updated_files
|
167
|
+
end
|
168
|
+
|
169
|
+
sig { params(bun_lock: Dependabot::DependencyFile).returns(String) }
|
170
|
+
def updated_bun_lock_content(bun_lock)
|
171
|
+
@updated_bun_lock_content ||= T.let({}, T.nilable(T::Hash[String, T.nilable(String)]))
|
172
|
+
@updated_bun_lock_content[bun_lock.name] ||=
|
173
|
+
bun_lockfile_updater.updated_bun_lock_content(bun_lock)
|
174
|
+
end
|
175
|
+
|
176
|
+
sig { returns(Dependabot::Bun::FileUpdater::BunLockfileUpdater) }
|
177
|
+
def bun_lockfile_updater
|
178
|
+
@bun_lockfile_updater ||= T.let(
|
179
|
+
BunLockfileUpdater.new(
|
180
|
+
dependencies: dependencies,
|
181
|
+
dependency_files: dependency_files,
|
182
|
+
repo_contents_path: repo_contents_path,
|
183
|
+
credentials: credentials
|
184
|
+
),
|
185
|
+
T.nilable(Dependabot::Bun::FileUpdater::BunLockfileUpdater)
|
186
|
+
)
|
187
|
+
end
|
188
|
+
|
189
|
+
sig { params(file: Dependabot::DependencyFile).returns(T.nilable(String)) }
|
190
|
+
def updated_package_json_content(file)
|
191
|
+
@updated_package_json_content ||= T.let({}, T.nilable(T::Hash[String, T.nilable(String)]))
|
192
|
+
@updated_package_json_content[file.name] ||=
|
193
|
+
PackageJsonUpdater.new(
|
194
|
+
package_json: file,
|
195
|
+
dependencies: dependencies
|
196
|
+
).updated_package_json.content
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
Dependabot::FileUpdaters
|
203
|
+
.register("bun", Dependabot::Bun::FileUpdater)
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# typed: strong
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "dependabot/dependency"
|
5
|
+
require "dependabot/file_parsers"
|
6
|
+
require "dependabot/file_parsers/base"
|
7
|
+
require "dependabot/shared_helpers"
|
8
|
+
require "sorbet-runtime"
|
9
|
+
|
10
|
+
module Dependabot
|
11
|
+
module Bun
|
12
|
+
module Helpers
|
13
|
+
extend T::Sig
|
14
|
+
|
15
|
+
# BUN Version Constants
|
16
|
+
BUN_V1 = 1
|
17
|
+
BUN_DEFAULT_VERSION = BUN_V1
|
18
|
+
|
19
|
+
sig { params(_bun_lock: T.nilable(DependencyFile)).returns(Integer) }
|
20
|
+
def self.bun_version_numeric(_bun_lock)
|
21
|
+
BUN_DEFAULT_VERSION
|
22
|
+
end
|
23
|
+
|
24
|
+
sig { returns(T.nilable(String)) }
|
25
|
+
def self.node_version
|
26
|
+
version = run_node_command("-v", fingerprint: "-v").strip
|
27
|
+
|
28
|
+
# Validate the output format (e.g., "v20.18.1" or "20.18.1")
|
29
|
+
if version.match?(/^v?\d+(\.\d+){2}$/)
|
30
|
+
version.strip.delete_prefix("v") # Remove the "v" prefix if present
|
31
|
+
end
|
32
|
+
rescue StandardError => e
|
33
|
+
Dependabot.logger.error("Error retrieving Node.js version: #{e.message}")
|
34
|
+
nil
|
35
|
+
end
|
36
|
+
|
37
|
+
sig { params(command: String, fingerprint: T.nilable(String)).returns(String) }
|
38
|
+
def self.run_node_command(command, fingerprint: nil)
|
39
|
+
full_command = "node #{command}"
|
40
|
+
|
41
|
+
Dependabot.logger.info("Running node command: #{full_command}")
|
42
|
+
|
43
|
+
result = Dependabot::SharedHelpers.run_shell_command(
|
44
|
+
full_command,
|
45
|
+
fingerprint: "node #{fingerprint || command}"
|
46
|
+
)
|
47
|
+
|
48
|
+
Dependabot.logger.info("Command executed successfully: #{full_command}")
|
49
|
+
result
|
50
|
+
rescue StandardError => e
|
51
|
+
Dependabot.logger.error("Error running node command: #{full_command}, Error: #{e.message}")
|
52
|
+
raise
|
53
|
+
end
|
54
|
+
|
55
|
+
sig { returns(T.nilable(String)) }
|
56
|
+
def self.bun_version
|
57
|
+
version = run_bun_command("--version", fingerprint: "--version").strip
|
58
|
+
if version.include?("+")
|
59
|
+
version.split("+").first # Remove build info, if present
|
60
|
+
end
|
61
|
+
rescue StandardError => e
|
62
|
+
Dependabot.logger.error("Error retrieving Bun version: #{e.message}")
|
63
|
+
nil
|
64
|
+
end
|
65
|
+
|
66
|
+
sig { params(command: String, fingerprint: T.nilable(String)).returns(String) }
|
67
|
+
def self.run_bun_command(command, fingerprint: nil)
|
68
|
+
full_command = "bun #{command}"
|
69
|
+
|
70
|
+
Dependabot.logger.info("Running bun command: #{full_command}")
|
71
|
+
|
72
|
+
result = Dependabot::SharedHelpers.run_shell_command(
|
73
|
+
full_command,
|
74
|
+
fingerprint: "bun #{fingerprint || command}"
|
75
|
+
)
|
76
|
+
|
77
|
+
Dependabot.logger.info("Command executed successfully: #{full_command}")
|
78
|
+
result
|
79
|
+
rescue StandardError => e
|
80
|
+
Dependabot.logger.error("Error running bun command: #{full_command}, Error: #{e.message}")
|
81
|
+
raise
|
82
|
+
end
|
83
|
+
|
84
|
+
sig { params(dependency_set: Dependabot::FileParsers::Base::DependencySet).returns(T::Array[Dependency]) }
|
85
|
+
def self.dependencies_with_all_versions_metadata(dependency_set)
|
86
|
+
dependency_set.dependencies.map do |dependency|
|
87
|
+
dependency.metadata[:all_versions] = dependency_set.all_versions_for_name(dependency.name)
|
88
|
+
dependency
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|