dependabot-bundler 0.333.0 → 0.335.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.
Files changed (25) hide show
  1. checksums.yaml +4 -4
  2. data/helpers/v2/lib/functions/force_updater.rb +7 -2
  3. data/helpers/v2/lib/functions/version_resolver.rb +6 -2
  4. data/helpers/v2/spec/functions/conflicting_dependency_resolver_spec.rb +64 -52
  5. data/helpers/v2/spec/functions/dependency_source_spec.rb +14 -10
  6. data/helpers/v2/spec/functions/version_resolver_spec.rb +4 -2
  7. data/lib/dependabot/bundler/file_fetcher.rb +12 -6
  8. data/lib/dependabot/bundler/file_parser.rb +4 -2
  9. data/lib/dependabot/bundler/file_updater/gemspec_updater.rb +13 -6
  10. data/lib/dependabot/bundler/file_updater/git_pin_replacer.rb +27 -6
  11. data/lib/dependabot/bundler/file_updater/git_source_remover.rb +14 -2
  12. data/lib/dependabot/bundler/file_updater/lockfile_updater.rb +90 -26
  13. data/lib/dependabot/bundler/file_updater/requirement_replacer.rb +92 -17
  14. data/lib/dependabot/bundler/file_updater.rb +48 -19
  15. data/lib/dependabot/bundler/metadata_finder.rb +61 -30
  16. data/lib/dependabot/bundler/package/package_details_fetcher.rb +60 -2
  17. data/lib/dependabot/bundler/requirement.rb +3 -2
  18. data/lib/dependabot/bundler/update_checker/file_preparer.rb +81 -25
  19. data/lib/dependabot/bundler/update_checker/force_updater.rb +11 -5
  20. data/lib/dependabot/bundler/update_checker/latest_version_finder/dependency_source.rb +53 -15
  21. data/lib/dependabot/bundler/update_checker/latest_version_finder.rb +4 -2
  22. data/lib/dependabot/bundler/update_checker/requirements_updater.rb +92 -31
  23. data/lib/dependabot/bundler/update_checker/version_resolver.rb +14 -7
  24. data/lib/dependabot/bundler/update_checker.rb +14 -7
  25. metadata +12 -12
@@ -1,6 +1,8 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
+ require "sorbet-runtime"
5
+
4
6
  require "dependabot/dependency_file"
5
7
  require "dependabot/bundler/update_checker"
6
8
  require "dependabot/bundler/cached_lockfile_parser"
@@ -26,10 +28,13 @@ module Dependabot
26
28
  # version allowed by the gemspec, if the gemspec has a required ruby
27
29
  # version range
28
30
  class FilePreparer
31
+ extend T::Sig
32
+
29
33
  VERSION_REGEX = /[0-9]+(?:\.[A-Za-z0-9\-_]+)*/
30
34
 
31
35
  # Can't be a constant because some of these don't exist in bundler
32
36
  # 1.15, which Heroku uses, which causes an exception on boot.
37
+ sig { returns(T::Array[T.class_of(::Bundler::Source::Path)]) }
33
38
  def gemspec_sources
34
39
  [
35
40
  ::Bundler::Source::Path,
@@ -37,31 +42,50 @@ module Dependabot
37
42
  ]
38
43
  end
39
44
 
40
- def initialize(dependency_files:, dependency:,
41
- remove_git_source: false,
42
- unlock_requirement: true,
43
- replacement_git_pin: nil,
44
- latest_allowable_version: nil,
45
- lock_ruby_version: true)
46
- @dependency_files = dependency_files
47
- @dependency = dependency
48
- @remove_git_source = remove_git_source
49
- @unlock_requirement = unlock_requirement
50
- @replacement_git_pin = replacement_git_pin
51
- @latest_allowable_version = latest_allowable_version
52
- @lock_ruby_version = lock_ruby_version
45
+ sig do
46
+ params(
47
+ dependency_files: T::Array[Dependabot::DependencyFile],
48
+ dependency: Dependabot::Dependency,
49
+ remove_git_source: T::Boolean,
50
+ unlock_requirement: T::Boolean,
51
+ replacement_git_pin: T.nilable(String),
52
+ latest_allowable_version: T.nilable(T.any(String, Dependabot::Version)),
53
+ lock_ruby_version: T::Boolean
54
+ ).void
55
+ end
56
+ def initialize(
57
+ dependency_files:,
58
+ dependency:,
59
+ remove_git_source: false,
60
+ unlock_requirement: true,
61
+ replacement_git_pin: nil,
62
+ latest_allowable_version: nil,
63
+ lock_ruby_version: true
64
+ )
65
+ @dependency_files = T.let(dependency_files, T::Array[Dependabot::DependencyFile])
66
+ @dependency = T.let(dependency, Dependabot::Dependency)
67
+ @remove_git_source = T.let(remove_git_source, T::Boolean)
68
+ @unlock_requirement = T.let(unlock_requirement, T::Boolean)
69
+ @replacement_git_pin = T.let(replacement_git_pin, T.nilable(String))
70
+ @latest_allowable_version = T.let(
71
+ latest_allowable_version&.to_s,
72
+ T.nilable(String)
73
+ )
74
+ @lock_ruby_version = T.let(lock_ruby_version, T::Boolean)
53
75
  end
54
76
 
55
77
  # rubocop:disable Metrics/AbcSize
56
78
  # rubocop:disable Metrics/MethodLength
79
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
57
80
  def prepared_dependency_files
58
81
  files = []
59
82
 
60
- if gemfile
83
+ gemfile_file = gemfile
84
+ if gemfile_file
61
85
  files << DependencyFile.new(
62
- name: gemfile.name,
63
- content: gemfile_content_for_update_check(gemfile),
64
- directory: gemfile.directory
86
+ name: gemfile_file.name,
87
+ content: gemfile_content_for_update_check(gemfile_file),
88
+ directory: gemfile_file.directory
65
89
  )
66
90
  end
67
91
 
@@ -76,7 +100,7 @@ module Dependabot
76
100
  path_gemspecs.each do |file|
77
101
  files << DependencyFile.new(
78
102
  name: file.name,
79
- content: sanitize_gemspec_content(file.content),
103
+ content: sanitize_gemspec_content(T.must(file.content)),
80
104
  directory: file.directory,
81
105
  support_file: file.support_file?
82
106
  )
@@ -104,28 +128,40 @@ module Dependabot
104
128
 
105
129
  private
106
130
 
131
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
107
132
  attr_reader :dependency_files
133
+
134
+ sig { returns(Dependabot::Dependency) }
108
135
  attr_reader :dependency
136
+
137
+ sig { returns(T.nilable(String)) }
109
138
  attr_reader :replacement_git_pin
139
+
140
+ sig { returns(T.nilable(String)) }
110
141
  attr_reader :latest_allowable_version
111
142
 
143
+ sig { returns(T::Boolean) }
112
144
  def remove_git_source?
113
145
  @remove_git_source
114
146
  end
115
147
 
148
+ sig { returns(T::Boolean) }
116
149
  def unlock_requirement?
117
150
  @unlock_requirement
118
151
  end
119
152
 
153
+ sig { returns(T::Boolean) }
120
154
  def replace_git_pin?
121
155
  !replacement_git_pin.nil?
122
156
  end
123
157
 
158
+ sig { returns(T.nilable(Dependabot::DependencyFile)) }
124
159
  def gemfile
125
160
  dependency_files.find { |f| f.name == "Gemfile" } ||
126
161
  dependency_files.find { |f| f.name == "gems.rb" }
127
162
  end
128
163
 
164
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
129
165
  def evaled_gemfiles
130
166
  dependency_files
131
167
  .reject { |f| f.name.end_with?(".gemspec") }
@@ -137,41 +173,49 @@ module Dependabot
137
173
  .reject(&:support_file?)
138
174
  end
139
175
 
176
+ sig { returns(T.nilable(Dependabot::DependencyFile)) }
140
177
  def lockfile
141
178
  dependency_files.find { |f| f.name == "Gemfile.lock" } ||
142
179
  dependency_files.find { |f| f.name == "gems.locked" }
143
180
  end
144
181
 
182
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
145
183
  def specification_files
146
184
  dependency_files.select { |f| f.name.end_with?(".specification") }
147
185
  end
148
186
 
187
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
149
188
  def top_level_gemspecs
150
189
  dependency_files
151
190
  .select { |f| f.name.end_with?(".gemspec") }
152
191
  end
153
192
 
193
+ sig { returns(T.nilable(Dependabot::DependencyFile)) }
154
194
  def ruby_version_file
155
195
  dependency_files.find { |f| f.name == ".ruby-version" }
156
196
  end
157
197
 
198
+ sig { returns(T.nilable(Dependabot::DependencyFile)) }
158
199
  def tool_versions_file
159
200
  dependency_files.find { |f| f.name == ".tool-versions" }
160
201
  end
161
202
 
203
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
162
204
  def path_gemspecs
163
205
  all = dependency_files.select { |f| f.name.end_with?(".gemspec") }
164
206
  all - top_level_gemspecs
165
207
  end
166
208
 
209
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
167
210
  def imported_ruby_files
168
211
  dependency_files
169
212
  .select { |f| f.name.end_with?(".rb") }
170
213
  .reject { |f| f.name == "gems.rb" }
171
214
  end
172
215
 
216
+ sig { params(file: Dependabot::DependencyFile).returns(String) }
173
217
  def gemfile_content_for_update_check(file)
174
- content = file.content
218
+ content = T.must(file.content)
175
219
  content = replace_gemfile_constraint(content, file.name)
176
220
  content = remove_git_source(content) if remove_git_source?
177
221
  content = replace_git_pin(content) if replace_git_pin?
@@ -179,12 +223,14 @@ module Dependabot
179
223
  content
180
224
  end
181
225
 
226
+ sig { params(gemspec: Dependabot::DependencyFile).returns(String) }
182
227
  def gemspec_content_for_update_check(gemspec)
183
- content = gemspec.content
228
+ content = T.must(gemspec.content)
184
229
  content = replace_gemspec_constraint(content, gemspec.name)
185
230
  sanitize_gemspec_content(content)
186
231
  end
187
232
 
233
+ sig { params(content: String, filename: String).returns(String) }
188
234
  def replace_gemfile_constraint(content, filename)
189
235
  FileUpdater::RequirementReplacer.new(
190
236
  dependency: dependency,
@@ -194,6 +240,7 @@ module Dependabot
194
240
  ).rewrite(content)
195
241
  end
196
242
 
243
+ sig { params(content: String, filename: String).returns(String) }
197
244
  def replace_gemspec_constraint(content, filename)
198
245
  FileUpdater::RequirementReplacer.new(
199
246
  dependency: dependency,
@@ -203,6 +250,7 @@ module Dependabot
203
250
  ).rewrite(content)
204
251
  end
205
252
 
253
+ sig { params(gemspec_content: String).returns(String) }
206
254
  def sanitize_gemspec_content(gemspec_content)
207
255
  new_version = replacement_version_for_gemspec(gemspec_content)
208
256
 
@@ -211,6 +259,7 @@ module Dependabot
211
259
  .rewrite(gemspec_content)
212
260
  end
213
261
 
262
+ sig { params(filename: String).returns(String) }
214
263
  def updated_version_requirement_string(filename)
215
264
  lower_bound_req = updated_version_req_lower_bound(filename)
216
265
 
@@ -221,6 +270,7 @@ module Dependabot
221
270
  end
222
271
 
223
272
  # rubocop:disable Metrics/PerceivedComplexity
273
+ sig { params(filename: String).returns(String) }
224
274
  def updated_version_req_lower_bound(filename) # rubocop:disable Metrics/CyclomaticComplexity
225
275
  original_req = dependency.requirements
226
276
  .find { |r| r.fetch(:file) == filename }
@@ -243,19 +293,22 @@ module Dependabot
243
293
  end
244
294
  # rubocop:enable Metrics/PerceivedComplexity
245
295
 
296
+ sig { params(content: String).returns(String) }
246
297
  def remove_git_source(content)
247
298
  FileUpdater::GitSourceRemover.new(
248
299
  dependency: dependency
249
300
  ).rewrite(content)
250
301
  end
251
302
 
303
+ sig { params(content: String).returns(String) }
252
304
  def replace_git_pin(content)
253
305
  FileUpdater::GitPinReplacer.new(
254
306
  dependency: dependency,
255
- new_pin: replacement_git_pin
307
+ new_pin: T.must(replacement_git_pin)
256
308
  ).rewrite(content)
257
309
  end
258
310
 
311
+ sig { params(gemfile_content: String).returns(String) }
259
312
  def lock_ruby_version(gemfile_content)
260
313
  top_level_gemspecs.each do |gs|
261
314
  gemfile_content = FileUpdater::RubyRequirementSetter
@@ -265,11 +318,13 @@ module Dependabot
265
318
  gemfile_content
266
319
  end
267
320
 
321
+ sig { params(file: Dependabot::DependencyFile).returns(T::Boolean) }
268
322
  def lock_ruby_version?(file)
269
323
  @lock_ruby_version && file == gemfile
270
324
  end
271
325
 
272
326
  # rubocop:disable Metrics/PerceivedComplexity
327
+ sig { params(gemspec_content: String).returns(String) }
273
328
  def replacement_version_for_gemspec(gemspec_content)
274
329
  return "0.0.1" unless lockfile
275
330
 
@@ -282,17 +337,18 @@ module Dependabot
282
337
  .new(gemspec_content: gemspec_content)
283
338
  .dependency_name
284
339
 
285
- return gemspec_specs.first&.version || "0.0.1" unless gem_name
340
+ return gemspec_specs.first&.version&.to_s || "0.0.1" unless gem_name
286
341
 
287
342
  spec = gemspec_specs.find { |s| s.name == gem_name }
288
- spec&.version || gemspec_specs.first&.version || "0.0.1"
343
+ spec&.version&.to_s || gemspec_specs.first&.version&.to_s || "0.0.1"
289
344
  end
290
345
  # rubocop:enable Metrics/PerceivedComplexity
291
346
 
292
347
  # TODO: Stop sanitizing the lockfile once we have bundler 2 installed
348
+ sig { returns(String) }
293
349
  def sanitized_lockfile_content
294
350
  re = FileUpdater::LockfileUpdater::LOCKFILE_ENDING
295
- lockfile.content.gsub(re, "")
351
+ T.must(T.must(lockfile).content).gsub(re, "")
296
352
  end
297
353
  end
298
354
  end
@@ -33,10 +33,16 @@ module Dependabot
33
33
  update_multiple_dependencies: T::Boolean
34
34
  ).void
35
35
  end
36
- def initialize(dependency:, dependency_files:, credentials:, target_version:,
37
- requirements_update_strategy:, options:,
38
- repo_contents_path: nil,
39
- update_multiple_dependencies: true)
36
+ def initialize(
37
+ dependency:,
38
+ dependency_files:,
39
+ credentials:,
40
+ target_version:,
41
+ requirements_update_strategy:,
42
+ options:,
43
+ repo_contents_path: nil,
44
+ update_multiple_dependencies: true
45
+ )
40
46
  @dependency = dependency
41
47
  @dependency_files = dependency_files
42
48
  @repo_contents_path = repo_contents_path
@@ -186,7 +192,7 @@ module Dependabot
186
192
  )
187
193
  end
188
194
 
189
- sig { params(dependency: Dependabot::Dependency).returns(T.nilable(T::Hash[String, T.untyped])) }
195
+ sig { params(dependency: Dependabot::Dependency).returns(T.nilable(T::Hash[Symbol, T.untyped])) }
190
196
  def source_for(dependency)
191
197
  dependency.requirements
192
198
  .find { |r| r.fetch(:source) }
@@ -1,11 +1,12 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
+ require "sorbet-runtime"
5
+
4
6
  require "dependabot/registry_client"
5
7
  require "dependabot/bundler/native_helpers"
6
8
  require "dependabot/bundler/helpers"
7
9
  require "dependabot/bundler/update_checker/latest_version_finder"
8
- require "sorbet-runtime"
9
10
 
10
11
  module Dependabot
11
12
  module Bundler
@@ -22,20 +23,41 @@ module Dependabot
22
23
  GIT = "git"
23
24
  OTHER = "other"
24
25
 
26
+ sig { returns(Dependabot::Dependency) }
25
27
  attr_reader :dependency
28
+
29
+ sig { override.returns(T::Array[Dependabot::DependencyFile]) }
26
30
  attr_reader :dependency_files
31
+
32
+ sig { override.returns(T.nilable(String)) }
27
33
  attr_reader :repo_contents_path
34
+
35
+ sig { override.returns(T::Array[Dependabot::Credential]) }
28
36
  attr_reader :credentials
37
+
38
+ sig { override.returns(T::Hash[Symbol, T.untyped]) }
29
39
  attr_reader :options
30
40
 
31
- def initialize(dependency:,
32
- dependency_files:,
33
- credentials:,
34
- options:)
41
+ sig do
42
+ params(
43
+ dependency: Dependabot::Dependency,
44
+ dependency_files: T::Array[Dependabot::DependencyFile],
45
+ credentials: T::Array[Dependabot::Credential],
46
+ options: T::Hash[Symbol, T.untyped]
47
+ ).void
48
+ end
49
+ def initialize(
50
+ dependency:,
51
+ dependency_files:,
52
+ credentials:,
53
+ options:
54
+ )
35
55
  @dependency = dependency
36
56
  @dependency_files = dependency_files
57
+ @repo_contents_path = T.let(nil, T.nilable(String))
37
58
  @credentials = credentials
38
59
  @options = options
60
+ @source_type = T.let(nil, T.nilable(String))
39
61
  end
40
62
 
41
63
  # The latest version details for the dependency from a registry
@@ -58,6 +80,7 @@ module Dependabot
58
80
  # The latest version details for the dependency from a git repo
59
81
  #
60
82
  # @return [Hash{Symbol => String}, nil]
83
+ sig { returns(T.nilable(T::Hash[Symbol, T.untyped])) }
61
84
  def latest_git_version_details
62
85
  return unless git?
63
86
 
@@ -73,7 +96,7 @@ module Dependabot
73
96
  options: options,
74
97
  args: {
75
98
  dir: tmp_dir,
76
- gemfile_name: gemfile.name,
99
+ gemfile_name: T.must(gemfile).name,
77
100
  dependency_name: dependency.name,
78
101
  credentials: credentials,
79
102
  dependency_source_url: source_details[:url],
@@ -84,14 +107,16 @@ module Dependabot
84
107
  end.transform_keys(&:to_sym)
85
108
  end
86
109
 
110
+ sig { returns(T::Boolean) }
87
111
  def git?
88
112
  source_type == GIT
89
113
  end
90
114
 
91
115
  private
92
116
 
117
+ sig { returns(T::Array[Dependabot::Bundler::Version]) }
93
118
  def rubygems_versions
94
- @rubygems_versions ||=
119
+ @rubygems_versions ||= T.let(
95
120
  begin
96
121
  response = Dependabot::RegistryClient.get(
97
122
  url: dependency_rubygems_uri,
@@ -100,17 +125,21 @@ module Dependabot
100
125
 
101
126
  JSON.parse(response.body)
102
127
  .map { |d| Dependabot::Bundler::Version.new(d["number"]) }
103
- end
128
+ end,
129
+ T.nilable(T::Array[Dependabot::Bundler::Version])
130
+ )
104
131
  rescue JSON::ParserError, Excon::Error::Timeout
105
132
  @rubygems_versions = []
106
133
  end
107
134
 
135
+ sig { returns(String) }
108
136
  def dependency_rubygems_uri
109
137
  "https://rubygems.org/api/v1/versions/#{dependency.name}.json"
110
138
  end
111
139
 
140
+ sig { returns(T::Array[Dependabot::Bundler::Version]) }
112
141
  def private_registry_versions
113
- @private_registry_versions ||=
142
+ @private_registry_versions ||= T.let(
114
143
  in_a_native_bundler_context do |tmp_dir|
115
144
  NativeHelpers.run_bundler_subprocess(
116
145
  bundler_version: bundler_version,
@@ -118,18 +147,21 @@ module Dependabot
118
147
  options: options,
119
148
  args: {
120
149
  dir: tmp_dir,
121
- gemfile_name: gemfile.name,
150
+ gemfile_name: T.must(gemfile).name,
122
151
  dependency_name: dependency.name,
123
152
  credentials: credentials
124
153
  }
125
154
  ).map do |version_string|
126
155
  Dependabot::Bundler::Version.new(version_string)
127
156
  end
128
- end
157
+ end,
158
+ T.nilable(T::Array[Dependabot::Bundler::Version])
159
+ )
129
160
  end
130
161
 
162
+ sig { returns(String) }
131
163
  def source_type
132
- return @source_type if defined? @source_type
164
+ return @source_type if @source_type
133
165
  return @source_type = RUBYGEMS unless gemfile
134
166
 
135
167
  @source_type = in_a_native_bundler_context do |tmp_dir|
@@ -139,7 +171,7 @@ module Dependabot
139
171
  options: options,
140
172
  args: {
141
173
  dir: tmp_dir,
142
- gemfile_name: gemfile.name,
174
+ gemfile_name: T.must(gemfile).name,
143
175
  dependency_name: dependency.name,
144
176
  credentials: credentials
145
177
  }
@@ -147,18 +179,24 @@ module Dependabot
147
179
  end
148
180
  end
149
181
 
182
+ sig { returns(T.nilable(Dependabot::DependencyFile)) }
150
183
  def gemfile
151
184
  dependency_files.find { |f| f.name == "Gemfile" } ||
152
185
  dependency_files.find { |f| f.name == "gems.rb" }
153
186
  end
154
187
 
188
+ sig { returns(T.nilable(Dependabot::DependencyFile)) }
155
189
  def lockfile
156
190
  dependency_files.find { |f| f.name == "Gemfile.lock" } ||
157
191
  dependency_files.find { |f| f.name == "gems.locked" }
158
192
  end
159
193
 
194
+ sig { override.returns(String) }
160
195
  def bundler_version
161
- @bundler_version ||= Helpers.bundler_version(lockfile)
196
+ @bundler_version ||= T.let(
197
+ Helpers.bundler_version(lockfile),
198
+ T.nilable(String)
199
+ )
162
200
  end
163
201
  end
164
202
  end
@@ -125,7 +125,8 @@ module Dependabot
125
125
  current_version&.prerelease? || dependency.requirements.any? do |req|
126
126
  req[:requirement].match?(/[a-z]/i)
127
127
  end
128
- end, T.nilable(T::Boolean)
128
+ end,
129
+ T.nilable(T::Boolean)
129
130
  )
130
131
  end
131
132
 
@@ -137,7 +138,8 @@ module Dependabot
137
138
  dependency_files: dependency_files,
138
139
  credentials: credentials,
139
140
  options: options
140
- ), T.nilable(DependencySource)
141
+ ),
142
+ T.nilable(DependencySource)
141
143
  )
142
144
  end
143
145
  end