dependabot-bundler 0.95.5 → 0.95.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. metadata +4 -38
  3. data/helpers/Makefile +0 -9
  4. data/helpers/build +0 -26
  5. data/lib/dependabot/bundler.rb +0 -27
  6. data/lib/dependabot/bundler/file_fetcher.rb +0 -216
  7. data/lib/dependabot/bundler/file_fetcher/child_gemfile_finder.rb +0 -68
  8. data/lib/dependabot/bundler/file_fetcher/gemspec_finder.rb +0 -96
  9. data/lib/dependabot/bundler/file_fetcher/path_gemspec_finder.rb +0 -112
  10. data/lib/dependabot/bundler/file_fetcher/require_relative_finder.rb +0 -65
  11. data/lib/dependabot/bundler/file_parser.rb +0 -297
  12. data/lib/dependabot/bundler/file_parser/file_preparer.rb +0 -84
  13. data/lib/dependabot/bundler/file_parser/gemfile_checker.rb +0 -46
  14. data/lib/dependabot/bundler/file_updater.rb +0 -125
  15. data/lib/dependabot/bundler/file_updater/gemfile_updater.rb +0 -114
  16. data/lib/dependabot/bundler/file_updater/gemspec_dependency_name_finder.rb +0 -50
  17. data/lib/dependabot/bundler/file_updater/gemspec_sanitizer.rb +0 -298
  18. data/lib/dependabot/bundler/file_updater/gemspec_updater.rb +0 -62
  19. data/lib/dependabot/bundler/file_updater/git_pin_replacer.rb +0 -78
  20. data/lib/dependabot/bundler/file_updater/git_source_remover.rb +0 -100
  21. data/lib/dependabot/bundler/file_updater/lockfile_updater.rb +0 -387
  22. data/lib/dependabot/bundler/file_updater/requirement_replacer.rb +0 -221
  23. data/lib/dependabot/bundler/metadata_finder.rb +0 -204
  24. data/lib/dependabot/bundler/requirement.rb +0 -29
  25. data/lib/dependabot/bundler/update_checker.rb +0 -334
  26. data/lib/dependabot/bundler/update_checker/file_preparer.rb +0 -279
  27. data/lib/dependabot/bundler/update_checker/force_updater.rb +0 -259
  28. data/lib/dependabot/bundler/update_checker/latest_version_finder.rb +0 -165
  29. data/lib/dependabot/bundler/update_checker/requirements_updater.rb +0 -281
  30. data/lib/dependabot/bundler/update_checker/ruby_requirement_setter.rb +0 -113
  31. data/lib/dependabot/bundler/update_checker/shared_bundler_helpers.rb +0 -244
  32. data/lib/dependabot/bundler/update_checker/version_resolver.rb +0 -272
  33. data/lib/dependabot/bundler/version.rb +0 -13
  34. data/lib/dependabot/monkey_patches/bundler/definition_bundler_version_patch.rb +0 -15
  35. data/lib/dependabot/monkey_patches/bundler/definition_ruby_version_patch.rb +0 -14
  36. data/lib/dependabot/monkey_patches/bundler/git_source_patch.rb +0 -27
@@ -1,62 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "dependabot/bundler/file_updater"
4
-
5
- module Dependabot
6
- module Bundler
7
- class FileUpdater
8
- class GemspecUpdater
9
- require_relative "requirement_replacer"
10
-
11
- def initialize(dependencies:, gemspec:)
12
- @dependencies = dependencies
13
- @gemspec = gemspec
14
- end
15
-
16
- def updated_gemspec_content
17
- content = gemspec.content
18
-
19
- dependencies.each do |dependency|
20
- content = replace_gemspec_version_requirement(
21
- gemspec, dependency, content
22
- )
23
- end
24
-
25
- content
26
- end
27
-
28
- private
29
-
30
- attr_reader :dependencies, :gemspec
31
-
32
- def replace_gemspec_version_requirement(gemspec, dependency, content)
33
- return content unless requirement_changed?(gemspec, dependency)
34
-
35
- updated_requirement =
36
- dependency.requirements.
37
- find { |r| r[:file] == gemspec.name }.
38
- fetch(:requirement)
39
-
40
- previous_requirement =
41
- dependency.previous_requirements.
42
- find { |r| r[:file] == gemspec.name }.
43
- fetch(:requirement)
44
-
45
- RequirementReplacer.new(
46
- dependency: dependency,
47
- file_type: :gemspec,
48
- updated_requirement: updated_requirement,
49
- previous_requirement: previous_requirement
50
- ).rewrite(content)
51
- end
52
-
53
- def requirement_changed?(file, dependency)
54
- changed_requirements =
55
- dependency.requirements - dependency.previous_requirements
56
-
57
- changed_requirements.any? { |f| f[:file] == file.name }
58
- end
59
- end
60
- end
61
- end
62
- end
@@ -1,78 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "parser/current"
4
- require "dependabot/bundler/file_updater"
5
-
6
- module Dependabot
7
- module Bundler
8
- class FileUpdater
9
- class GitPinReplacer
10
- attr_reader :dependency, :new_pin
11
-
12
- def initialize(dependency:, new_pin:)
13
- @dependency = dependency
14
- @new_pin = new_pin
15
- end
16
-
17
- def rewrite(content)
18
- buffer = Parser::Source::Buffer.new("(gemfile_content)")
19
- buffer.source = content
20
- ast = Parser::CurrentRuby.new.parse(buffer)
21
-
22
- Rewriter.
23
- new(dependency: dependency, new_pin: new_pin).
24
- rewrite(buffer, ast)
25
- end
26
-
27
- class Rewriter < Parser::TreeRewriter
28
- PIN_KEYS = %i(ref tag).freeze
29
- attr_reader :dependency, :new_pin
30
-
31
- def initialize(dependency:, new_pin:)
32
- @dependency = dependency
33
- @new_pin = new_pin
34
- end
35
-
36
- def on_send(node)
37
- return unless declares_targeted_gem?(node)
38
- return unless node.children.last.type == :hash
39
-
40
- kwargs_node = node.children.last
41
- kwargs_node.children.each do |hash_pair|
42
- next unless PIN_KEYS.include?(key_from_hash_pair(hash_pair))
43
-
44
- update_value(hash_pair)
45
- end
46
- end
47
-
48
- private
49
-
50
- def declares_targeted_gem?(node)
51
- return false unless node.children[1] == :gem
52
-
53
- node.children[2].children.first == dependency.name
54
- end
55
-
56
- def key_from_hash_pair(node)
57
- node.children.first.children.first.to_sym
58
- end
59
-
60
- def update_value(hash_pair)
61
- value_node = hash_pair.children.last
62
- open_quote_character, close_quote_character =
63
- extract_quote_characters_from(value_node)
64
-
65
- replace(
66
- value_node.loc.expression,
67
- %(#{open_quote_character}#{new_pin}#{close_quote_character})
68
- )
69
- end
70
-
71
- def extract_quote_characters_from(value_node)
72
- [value_node.loc.begin.source, value_node.loc.end.source]
73
- end
74
- end
75
- end
76
- end
77
- end
78
- end
@@ -1,100 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "parser/current"
4
- require "dependabot/bundler/file_updater"
5
-
6
- module Dependabot
7
- module Bundler
8
- class FileUpdater
9
- class GitSourceRemover
10
- attr_reader :dependency
11
-
12
- def initialize(dependency:)
13
- @dependency = dependency
14
- end
15
-
16
- def rewrite(content)
17
- buffer = Parser::Source::Buffer.new("(gemfile_content)")
18
- buffer.source = content
19
- ast = Parser::CurrentRuby.new.parse(buffer)
20
-
21
- Rewriter.new(dependency: dependency).rewrite(buffer, ast)
22
- end
23
-
24
- class Rewriter < Parser::TreeRewriter
25
- # TODO: Hack until Bundler 1.16.0 is available on Heroku
26
- GOOD_KEYS = %i(
27
- group groups path glob name require platform platforms type
28
- source install_if
29
- ).freeze
30
-
31
- attr_reader :dependency
32
-
33
- def initialize(dependency:)
34
- @dependency = dependency
35
- end
36
-
37
- def on_send(node)
38
- return unless declares_targeted_gem?(node)
39
- return unless node.children.last.type == :hash
40
-
41
- kwargs_node = node.children.last
42
- keys = kwargs_node.children.map do |hash_pair|
43
- key_from_hash_pair(hash_pair)
44
- end
45
-
46
- if keys.none? { |key| GOOD_KEYS.include?(key) }
47
- remove_all_kwargs(node)
48
- else
49
- remove_git_related_kwargs(kwargs_node)
50
- end
51
- end
52
-
53
- private
54
-
55
- def declares_targeted_gem?(node)
56
- return false unless node.children[1] == :gem
57
-
58
- node.children[2].children.first == dependency.name
59
- end
60
-
61
- def key_from_hash_pair(node)
62
- node.children.first.children.first.to_sym
63
- end
64
-
65
- def remove_all_kwargs(node)
66
- kwargs_node = node.children.last
67
-
68
- range_to_remove =
69
- kwargs_node.loc.expression.join(node.children[-2].loc.end.end)
70
-
71
- remove(range_to_remove)
72
- end
73
-
74
- def remove_git_related_kwargs(kwargs_node)
75
- good_key_index = nil
76
- hash_pairs = kwargs_node.children
77
-
78
- hash_pairs.each_with_index do |hash_pair, index|
79
- if GOOD_KEYS.include?(key_from_hash_pair(hash_pair))
80
- good_key_index = index
81
- next
82
- end
83
-
84
- range_to_remove =
85
- if good_key_index.nil?
86
- next_arg_start = hash_pairs[index + 1].loc.expression.begin
87
- hash_pair.loc.expression.join(next_arg_start)
88
- else
89
- last_arg_end = hash_pairs[good_key_index].loc.expression.end
90
- hash_pair.loc.expression.join(last_arg_end)
91
- end
92
-
93
- remove(range_to_remove)
94
- end
95
- end
96
- end
97
- end
98
- end
99
- end
100
- end
@@ -1,387 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "bundler"
4
-
5
- require "dependabot/monkey_patches/bundler/definition_ruby_version_patch"
6
- require "dependabot/monkey_patches/bundler/definition_bundler_version_patch"
7
- require "dependabot/monkey_patches/bundler/git_source_patch"
8
-
9
- require "dependabot/shared_helpers"
10
- require "dependabot/errors"
11
- require "dependabot/bundler/file_updater"
12
- require "dependabot/git_commit_checker"
13
-
14
- # rubocop:disable Metrics/ClassLength
15
- module Dependabot
16
- module Bundler
17
- class FileUpdater
18
- class LockfileUpdater
19
- require_relative "gemfile_updater"
20
- require_relative "gemspec_updater"
21
- require_relative "gemspec_sanitizer"
22
- require_relative "gemspec_dependency_name_finder"
23
-
24
- LOCKFILE_ENDING =
25
- /(?<ending>\s*(?:RUBY VERSION|BUNDLED WITH).*)/m.freeze
26
- GIT_DEPENDENCIES_SECTION = /GIT\n.*?\n\n(?!GIT)/m.freeze
27
- GIT_DEPENDENCY_DETAILS = /GIT\n.*?\n\n/m.freeze
28
- GEM_NOT_FOUND_ERROR_REGEX =
29
- /locked to (?<name>[^\s]+) \(|not find (?<name>[^\s]+)-\d/.freeze
30
- RETRYABLE_ERRORS = [::Bundler::HTTPError].freeze
31
-
32
- # Can't be a constant because some of these don't exist in bundler
33
- # 1.15, which Heroku uses, which causes an exception on boot.
34
- def gemspec_sources
35
- [
36
- ::Bundler::Source::Path,
37
- ::Bundler::Source::Gemspec
38
- ]
39
- end
40
-
41
- def initialize(dependencies:, dependency_files:, credentials:)
42
- @dependencies = dependencies
43
- @dependency_files = dependency_files
44
- @credentials = credentials
45
- end
46
-
47
- def updated_lockfile_content
48
- @updated_lockfile_content ||=
49
- begin
50
- updated_content = build_updated_lockfile
51
-
52
- if lockfile.content == updated_content
53
- raise "Expected content to change!"
54
- end
55
-
56
- updated_content
57
- end
58
- end
59
-
60
- private
61
-
62
- attr_reader :dependencies, :dependency_files, :credentials
63
-
64
- def build_updated_lockfile
65
- base_dir = dependency_files.first.directory
66
- lockfile_body =
67
- SharedHelpers.in_a_temporary_directory(base_dir) do |tmp_dir|
68
- write_temporary_dependency_files
69
-
70
- SharedHelpers.in_a_forked_process do
71
- # Set the path for path gemspec correctly
72
- ::Bundler.instance_variable_set(:@root, tmp_dir)
73
-
74
- # Remove installed gems from the default Rubygems index
75
- ::Gem::Specification.all = []
76
-
77
- # Set auth details
78
- relevant_credentials.each do |cred|
79
- token = cred["token"] ||
80
- "#{cred['username']}:#{cred['password']}"
81
-
82
- ::Bundler.settings.set_command_option(
83
- cred.fetch("host"),
84
- token.gsub("@", "%40F").gsub("?", "%3F")
85
- )
86
- end
87
-
88
- generate_lockfile
89
- end
90
- end
91
- post_process_lockfile(lockfile_body)
92
- end
93
-
94
- def write_temporary_dependency_files
95
- File.write(gemfile.name, updated_gemfile_content(gemfile))
96
- File.write(lockfile.name, sanitized_lockfile_body)
97
-
98
- top_level_gemspecs.each do |gemspec|
99
- path = gemspec.name
100
- FileUtils.mkdir_p(Pathname.new(path).dirname)
101
- updated_content = updated_gemspec_content(gemspec)
102
- File.write(path, sanitized_gemspec_content(updated_content))
103
- end
104
-
105
- write_ruby_version_file
106
- write_path_gemspecs
107
- write_imported_ruby_files
108
-
109
- evaled_gemfiles.each do |file|
110
- path = file.name
111
- FileUtils.mkdir_p(Pathname.new(path).dirname)
112
- File.write(path, updated_gemfile_content(file))
113
- end
114
- end
115
-
116
- def generate_lockfile
117
- dependencies_to_unlock = dependencies.map(&:name)
118
-
119
- begin
120
- definition = build_definition(dependencies_to_unlock)
121
-
122
- old_reqs = lock_deps_being_updated_to_exact_versions(definition)
123
-
124
- definition.resolve_remotely!
125
-
126
- old_reqs.each do |dep_name, old_req|
127
- d_dep = definition.dependencies.find { |d| d.name == dep_name }
128
- if old_req == :none then definition.dependencies.delete(d_dep)
129
- else d_dep.instance_variable_set(:@requirement, old_req)
130
- end
131
- end
132
-
133
- definition.to_lock
134
- rescue ::Bundler::GemNotFound => error
135
- unlock_yanked_gem(dependencies_to_unlock, error) && retry
136
- rescue ::Bundler::VersionConflict => error
137
- unlock_blocking_subdeps(dependencies_to_unlock, error) && retry
138
- rescue *RETRYABLE_ERRORS
139
- raise if @retrying
140
-
141
- @retrying = true
142
- sleep(rand(1.0..5.0))
143
- retry
144
- end
145
- end
146
-
147
- def unlock_yanked_gem(dependencies_to_unlock, error)
148
- raise unless error.message.match?(GEM_NOT_FOUND_ERROR_REGEX)
149
-
150
- gem_name = error.message.match(GEM_NOT_FOUND_ERROR_REGEX).
151
- named_captures["name"]
152
- raise if dependencies_to_unlock.include?(gem_name)
153
-
154
- dependencies_to_unlock << gem_name
155
- end
156
-
157
- def unlock_blocking_subdeps(dependencies_to_unlock, error)
158
- all_deps = ::Bundler::LockfileParser.new(sanitized_lockfile_body).
159
- specs.map(&:name).map(&:to_s)
160
- top_level = build_definition([]).dependencies.
161
- map(&:name).map(&:to_s)
162
- allowed_new_unlocks = all_deps - top_level - dependencies_to_unlock
163
-
164
- # Unlock any sub-dependencies that Bundler reports caused the
165
- # conflict
166
- potentials_deps =
167
- error.cause.conflicts.values.
168
- flat_map(&:requirement_trees).
169
- map do |tree|
170
- tree.find { |req| allowed_new_unlocks.include?(req.name) }
171
- end.compact.map(&:name)
172
-
173
- # If there's nothing more we can unlock, give up
174
- raise if potentials_deps.none?
175
-
176
- dependencies_to_unlock.append(*potentials_deps)
177
- end
178
-
179
- def build_definition(dependencies_to_unlock)
180
- defn = ::Bundler::Definition.build(
181
- gemfile.name,
182
- lockfile.name,
183
- gems: dependencies_to_unlock
184
- )
185
-
186
- # Bundler unlocks the sub-dependencies of gems it is passed even
187
- # if those sub-deps are top-level dependencies. We only want true
188
- # subdeps unlocked, like they were in the UpdateChecker, so we
189
- # mutate the unlocked gems array.
190
- unlocked = defn.instance_variable_get(:@unlock).fetch(:gems)
191
- must_not_unlock = defn.dependencies.map(&:name).map(&:to_s) -
192
- dependencies_to_unlock
193
- unlocked.reject! { |n| must_not_unlock.include?(n) }
194
-
195
- defn
196
- end
197
-
198
- def lock_deps_being_updated_to_exact_versions(definition)
199
- dependencies.each_with_object({}) do |dep, old_reqs|
200
- defn_dep = definition.dependencies.find { |d| d.name == dep.name }
201
-
202
- if defn_dep.nil?
203
- definition.dependencies <<
204
- ::Bundler::Dependency.new(dep.name, dep.version)
205
- old_reqs[dep.name] = :none
206
- elsif git_dependency?(dep) &&
207
- defn_dep.source.is_a?(::Bundler::Source::Git)
208
- defn_dep.source.unlock!
209
- elsif Gem::Version.correct?(dep.version)
210
- new_req = Gem::Requirement.create("= #{dep.version}")
211
- old_reqs[dep.name] = defn_dep.requirement
212
- defn_dep.instance_variable_set(:@requirement, new_req)
213
- end
214
- end
215
- end
216
-
217
- def write_ruby_version_file
218
- return unless ruby_version_file
219
-
220
- path = ruby_version_file.name
221
- FileUtils.mkdir_p(Pathname.new(path).dirname)
222
- File.write(path, ruby_version_file.content)
223
- end
224
-
225
- def write_path_gemspecs
226
- path_gemspecs.each do |file|
227
- path = file.name
228
- FileUtils.mkdir_p(Pathname.new(path).dirname)
229
- File.write(path, sanitized_gemspec_content(file.content))
230
- end
231
- end
232
-
233
- def write_imported_ruby_files
234
- imported_ruby_files.each do |file|
235
- path = file.name
236
- FileUtils.mkdir_p(Pathname.new(path).dirname)
237
- File.write(path, file.content)
238
- end
239
- end
240
-
241
- def path_gemspecs
242
- all = dependency_files.select { |f| f.name.end_with?(".gemspec") }
243
- all - top_level_gemspecs
244
- end
245
-
246
- def imported_ruby_files
247
- dependency_files.
248
- select { |f| f.name.end_with?(".rb") }.
249
- reject { |f| f.name == "gems.rb" }
250
- end
251
-
252
- def top_level_gemspecs
253
- dependency_files.
254
- select { |file| file.name.end_with?(".gemspec") }.
255
- reject(&:support_file?)
256
- end
257
-
258
- def ruby_version_file
259
- dependency_files.find { |f| f.name == ".ruby-version" }
260
- end
261
-
262
- def post_process_lockfile(lockfile_body)
263
- lockfile_body = reorder_git_dependencies(lockfile_body)
264
- replace_lockfile_ending(lockfile_body)
265
- end
266
-
267
- def reorder_git_dependencies(lockfile_body)
268
- new_section = lockfile_body.match(GIT_DEPENDENCIES_SECTION)&.to_s
269
- old_section = lockfile.content.match(GIT_DEPENDENCIES_SECTION)&.to_s
270
-
271
- return lockfile_body unless new_section && old_section
272
-
273
- new_deps = new_section.scan(GIT_DEPENDENCY_DETAILS)
274
- old_deps = old_section.scan(GIT_DEPENDENCY_DETAILS)
275
-
276
- return lockfile_body unless new_deps.count == old_deps.count
277
-
278
- reordered_new_section = new_deps.sort_by do |new_dep_details|
279
- remote = new_dep_details.match(/remote: (?<remote>.*\n)/)[:remote]
280
- i = old_deps.index { |details| details.include?(remote) }
281
-
282
- # If this dependency isn't in the old lockfile then we can't rely
283
- # on that (presumably outdated) lockfile to do reordering.
284
- # Instead, we just return the default-ordered content just
285
- # generated.
286
- return lockfile_body unless i
287
-
288
- i
289
- end.join
290
-
291
- lockfile_body.gsub(new_section, reordered_new_section)
292
- end
293
-
294
- def replace_lockfile_ending(lockfile_body)
295
- # Re-add the old `BUNDLED WITH` version (and remove the RUBY VERSION
296
- # if it wasn't previously present in the lockfile)
297
- lockfile_body.gsub(
298
- LOCKFILE_ENDING,
299
- lockfile.content.match(LOCKFILE_ENDING)&.[](:ending) || "\n"
300
- )
301
- end
302
-
303
- def sanitized_gemspec_content(gemspec_content)
304
- new_version = replacement_version_for_gemspec(gemspec_content)
305
-
306
- GemspecSanitizer.
307
- new(replacement_version: new_version).
308
- rewrite(gemspec_content)
309
- end
310
-
311
- def replacement_version_for_gemspec(gemspec_content)
312
- return "0.0.1" unless lockfile
313
-
314
- gemspec_specs =
315
- ::Bundler::LockfileParser.new(sanitized_lockfile_body).specs.
316
- select { |s| gemspec_sources.include?(s.source.class) }
317
-
318
- gem_name =
319
- GemspecDependencyNameFinder.new(gemspec_content: gemspec_content).
320
- dependency_name
321
-
322
- return gemspec_specs.first&.version || "0.0.1" unless gem_name
323
-
324
- spec = gemspec_specs.find { |s| s.name == gem_name }
325
- spec&.version || gemspec_specs.first&.version || "0.0.1"
326
- end
327
-
328
- def relevant_credentials
329
- credentials.select do |cred|
330
- next true if cred["type"] == "git_source"
331
- next true if cred["type"] == "rubygems_server"
332
-
333
- false
334
- end
335
- end
336
-
337
- def updated_gemfile_content(file)
338
- GemfileUpdater.new(
339
- dependencies: dependencies,
340
- gemfile: file
341
- ).updated_gemfile_content
342
- end
343
-
344
- def updated_gemspec_content(gemspec)
345
- GemspecUpdater.new(
346
- dependencies: dependencies,
347
- gemspec: gemspec
348
- ).updated_gemspec_content
349
- end
350
-
351
- def gemfile
352
- @gemfile ||= dependency_files.find { |f| f.name == "Gemfile" } ||
353
- dependency_files.find { |f| f.name == "gems.rb" }
354
- end
355
-
356
- def lockfile
357
- @lockfile ||=
358
- dependency_files.find { |f| f.name == "Gemfile.lock" } ||
359
- dependency_files.find { |f| f.name == "gems.locked" }
360
- end
361
-
362
- def sanitized_lockfile_body
363
- lockfile.content.gsub(LOCKFILE_ENDING, "")
364
- end
365
-
366
- def evaled_gemfiles
367
- @evaled_gemfiles ||=
368
- dependency_files.
369
- reject { |f| f.name.end_with?(".gemspec") }.
370
- reject { |f| f.name.end_with?(".lock") }.
371
- reject { |f| f.name.end_with?(".ruby-version") }.
372
- reject { |f| f.name == "Gemfile" }.
373
- reject { |f| f.name == "gems.rb" }.
374
- reject { |f| f.name == "gems.locked" }
375
- end
376
-
377
- def git_dependency?(dep)
378
- GitCommitChecker.new(
379
- dependency: dep,
380
- credentials: credentials
381
- ).git_dependency?
382
- end
383
- end
384
- end
385
- end
386
- end
387
- # rubocop:enable Metrics/ClassLength