dependabot-core 0.93.17 → 0.94.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4 -0
  3. data/lib/dependabot/dependency.rb +16 -21
  4. data/lib/dependabot/file_fetchers.rb +1 -5
  5. data/lib/dependabot/file_parsers.rb +1 -5
  6. data/lib/dependabot/file_updaters.rb +1 -5
  7. data/lib/dependabot/metadata_finders.rb +1 -5
  8. data/lib/dependabot/pull_request_creator/labeler.rb +26 -24
  9. data/lib/dependabot/update_checkers.rb +1 -5
  10. data/lib/dependabot/utils.rb +2 -12
  11. data/lib/dependabot/version.rb +1 -1
  12. metadata +1 -28
  13. data/lib/dependabot/file_fetchers/ruby/bundler.rb +0 -215
  14. data/lib/dependabot/file_fetchers/ruby/bundler/child_gemfile_finder.rb +0 -70
  15. data/lib/dependabot/file_fetchers/ruby/bundler/gemspec_finder.rb +0 -98
  16. data/lib/dependabot/file_fetchers/ruby/bundler/path_gemspec_finder.rb +0 -114
  17. data/lib/dependabot/file_fetchers/ruby/bundler/require_relative_finder.rb +0 -67
  18. data/lib/dependabot/file_parsers/ruby/bundler.rb +0 -294
  19. data/lib/dependabot/file_parsers/ruby/bundler/file_preparer.rb +0 -86
  20. data/lib/dependabot/file_parsers/ruby/bundler/gemfile_checker.rb +0 -48
  21. data/lib/dependabot/file_updaters/ruby/bundler.rb +0 -123
  22. data/lib/dependabot/file_updaters/ruby/bundler/gemfile_updater.rb +0 -116
  23. data/lib/dependabot/file_updaters/ruby/bundler/gemspec_dependency_name_finder.rb +0 -52
  24. data/lib/dependabot/file_updaters/ruby/bundler/gemspec_sanitizer.rb +0 -298
  25. data/lib/dependabot/file_updaters/ruby/bundler/gemspec_updater.rb +0 -64
  26. data/lib/dependabot/file_updaters/ruby/bundler/git_pin_replacer.rb +0 -80
  27. data/lib/dependabot/file_updaters/ruby/bundler/git_source_remover.rb +0 -102
  28. data/lib/dependabot/file_updaters/ruby/bundler/lockfile_updater.rb +0 -389
  29. data/lib/dependabot/file_updaters/ruby/bundler/requirement_replacer.rb +0 -223
  30. data/lib/dependabot/metadata_finders/ruby/bundler.rb +0 -202
  31. data/lib/dependabot/update_checkers/ruby/bundler.rb +0 -331
  32. data/lib/dependabot/update_checkers/ruby/bundler/file_preparer.rb +0 -281
  33. data/lib/dependabot/update_checkers/ruby/bundler/force_updater.rb +0 -261
  34. data/lib/dependabot/update_checkers/ruby/bundler/latest_version_finder.rb +0 -169
  35. data/lib/dependabot/update_checkers/ruby/bundler/requirements_updater.rb +0 -283
  36. data/lib/dependabot/update_checkers/ruby/bundler/ruby_requirement_setter.rb +0 -115
  37. data/lib/dependabot/update_checkers/ruby/bundler/shared_bundler_helpers.rb +0 -246
  38. data/lib/dependabot/update_checkers/ruby/bundler/version_resolver.rb +0 -272
  39. data/lib/dependabot/utils/ruby/requirement.rb +0 -26
@@ -1,281 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "dependabot/dependency_file"
4
- require "dependabot/update_checkers/ruby/bundler"
5
- require "dependabot/file_updaters/ruby/bundler/gemspec_sanitizer"
6
- require "dependabot/file_updaters/ruby/bundler/git_pin_replacer"
7
- require "dependabot/file_updaters/ruby/bundler/git_source_remover"
8
- require "dependabot/file_updaters/ruby/bundler/requirement_replacer"
9
- require "dependabot/file_updaters/ruby/bundler/gemspec_dependency_name_finder"
10
- require "dependabot/file_updaters/ruby/bundler/lockfile_updater"
11
- require "dependabot/update_checkers/ruby/bundler/ruby_requirement_setter"
12
-
13
- module Dependabot
14
- module UpdateCheckers
15
- module Ruby
16
- class Bundler
17
- # This class takes a set of dependency files and sanitizes them for use
18
- # in UpdateCheckers::Ruby::Bundler. In particular, it:
19
- # - Removes any version requirement on the dependency being updated
20
- # (in the Gemfile)
21
- # - Sanitizes any provided gemspecs to remove file imports etc. (since
22
- # Dependabot doesn't pull down the entire repo). This process is
23
- # imperfect - an alternative would be to clone the repo
24
- # - Sets the ruby version in the Gemfile to be the lowest possible
25
- # version allowed by the gemspec, if the gemspec has a required ruby
26
- # version range
27
- class FilePreparer
28
- VERSION_REGEX = /[0-9]+(?:\.[A-Za-z0-9\-_]+)*/.freeze
29
-
30
- # Can't be a constant because some of these don't exist in bundler
31
- # 1.15, which Heroku uses, which causes an exception on boot.
32
- def gemspec_sources
33
- [
34
- ::Bundler::Source::Path,
35
- ::Bundler::Source::Gemspec
36
- ]
37
- end
38
-
39
- def initialize(dependency_files:, dependency:,
40
- remove_git_source: false,
41
- unlock_requirement: true,
42
- replacement_git_pin: nil,
43
- latest_allowable_version: nil,
44
- lock_ruby_version: true)
45
- @dependency_files = dependency_files
46
- @dependency = dependency
47
- @remove_git_source = remove_git_source
48
- @unlock_requirement = unlock_requirement
49
- @replacement_git_pin = replacement_git_pin
50
- @latest_allowable_version = latest_allowable_version
51
- @lock_ruby_version = lock_ruby_version
52
- end
53
-
54
- # rubocop:disable Metrics/AbcSize
55
- # rubocop:disable Metrics/MethodLength
56
- def prepared_dependency_files
57
- files = []
58
-
59
- if gemfile
60
- files << DependencyFile.new(
61
- name: gemfile.name,
62
- content: gemfile_content_for_update_check(gemfile),
63
- directory: gemfile.directory
64
- )
65
- end
66
-
67
- top_level_gemspecs.each do |gemspec|
68
- files << DependencyFile.new(
69
- name: gemspec.name,
70
- content: gemspec_content_for_update_check(gemspec),
71
- directory: gemspec.directory
72
- )
73
- end
74
-
75
- path_gemspecs.each do |file|
76
- files << DependencyFile.new(
77
- name: file.name,
78
- content: sanitize_gemspec_content(file.content),
79
- directory: file.directory,
80
- support_file: file.support_file?
81
- )
82
- end
83
-
84
- evaled_gemfiles.each do |file|
85
- files << DependencyFile.new(
86
- name: file.name,
87
- content: gemfile_content_for_update_check(file),
88
- directory: file.directory
89
- )
90
- end
91
-
92
- # No editing required for lockfile or Ruby version file
93
- files += [lockfile, ruby_version_file, *imported_ruby_files].compact
94
- end
95
- # rubocop:enable Metrics/AbcSize
96
- # rubocop:enable Metrics/MethodLength
97
-
98
- private
99
-
100
- attr_reader :dependency_files, :dependency, :replacement_git_pin,
101
- :latest_allowable_version
102
-
103
- def remove_git_source?
104
- @remove_git_source
105
- end
106
-
107
- def unlock_requirement?
108
- @unlock_requirement
109
- end
110
-
111
- def replace_git_pin?
112
- !replacement_git_pin.nil?
113
- end
114
-
115
- def gemfile
116
- dependency_files.find { |f| f.name == "Gemfile" } ||
117
- dependency_files.find { |f| f.name == "gems.rb" }
118
- end
119
-
120
- def evaled_gemfiles
121
- dependency_files.
122
- reject { |f| f.name.end_with?(".gemspec") }.
123
- reject { |f| f.name.end_with?(".lock") }.
124
- reject { |f| f.name.end_with?(".ruby-version") }.
125
- reject { |f| f.name == "Gemfile" }.
126
- reject { |f| f.name == "gems.rb" }.
127
- reject { |f| f.name == "gems.locked" }
128
- end
129
-
130
- def lockfile
131
- dependency_files.find { |f| f.name == "Gemfile.lock" } ||
132
- dependency_files.find { |f| f.name == "gems.locked" }
133
- end
134
-
135
- def top_level_gemspecs
136
- dependency_files.
137
- select { |f| f.name.end_with?(".gemspec") }.
138
- reject(&:support_file?)
139
- end
140
-
141
- def ruby_version_file
142
- dependency_files.find { |f| f.name == ".ruby-version" }
143
- end
144
-
145
- def path_gemspecs
146
- all = dependency_files.select { |f| f.name.end_with?(".gemspec") }
147
- all - top_level_gemspecs
148
- end
149
-
150
- def imported_ruby_files
151
- dependency_files.
152
- select { |f| f.name.end_with?(".rb") }.
153
- reject { |f| f.name == "gems.rb" }
154
- end
155
-
156
- def gemfile_content_for_update_check(file)
157
- content = file.content
158
- content = replace_gemfile_constraint(content, file.name)
159
- content = remove_git_source(content) if remove_git_source?
160
- content = replace_git_pin(content) if replace_git_pin?
161
- content = lock_ruby_version(content) if lock_ruby_version?(file)
162
- content
163
- end
164
-
165
- def gemspec_content_for_update_check(gemspec)
166
- content = gemspec.content
167
- content = replace_gemspec_constraint(content, gemspec.name)
168
- sanitize_gemspec_content(content)
169
- end
170
-
171
- def replace_gemfile_constraint(content, filename)
172
- FileUpdaters::Ruby::Bundler::RequirementReplacer.new(
173
- dependency: dependency,
174
- file_type: :gemfile,
175
- updated_requirement: updated_version_requirement_string(filename),
176
- insert_if_bare: true
177
- ).rewrite(content)
178
- end
179
-
180
- def replace_gemspec_constraint(content, filename)
181
- FileUpdaters::Ruby::Bundler::RequirementReplacer.new(
182
- dependency: dependency,
183
- file_type: :gemspec,
184
- updated_requirement: updated_version_requirement_string(filename),
185
- insert_if_bare: true
186
- ).rewrite(content)
187
- end
188
-
189
- def sanitize_gemspec_content(gemspec_content)
190
- new_version = replacement_version_for_gemspec(gemspec_content)
191
-
192
- FileUpdaters::Ruby::Bundler::GemspecSanitizer.
193
- new(replacement_version: new_version).
194
- rewrite(gemspec_content)
195
- end
196
-
197
- def updated_version_requirement_string(filename)
198
- lower_bound_req = updated_version_req_lower_bound(filename)
199
-
200
- return lower_bound_req if latest_allowable_version.nil?
201
- unless Gem::Version.correct?(latest_allowable_version)
202
- return lower_bound_req
203
- end
204
-
205
- lower_bound_req + ", <= #{latest_allowable_version}"
206
- end
207
-
208
- def updated_version_req_lower_bound(filename)
209
- original_req = dependency.requirements.
210
- find { |r| r.fetch(:file) == filename }&.
211
- fetch(:requirement)
212
-
213
- if original_req && !unlock_requirement? then original_req
214
- elsif dependency.version&.match?(/^[0-9a-f]{40}$/) then ">= 0"
215
- elsif dependency.version then ">= #{dependency.version}"
216
- else
217
- version_for_requirement =
218
- dependency.requirements.map { |r| r[:requirement] }.
219
- reject { |req_string| req_string.start_with?("<") }.
220
- select { |req_string| req_string.match?(VERSION_REGEX) }.
221
- map { |req_string| req_string.match(VERSION_REGEX) }.
222
- select { |version| Gem::Version.correct?(version) }.
223
- max_by { |version| Gem::Version.new(version) }
224
-
225
- ">= #{version_for_requirement || 0}"
226
- end
227
- end
228
-
229
- def remove_git_source(content)
230
- FileUpdaters::Ruby::Bundler::GitSourceRemover.new(
231
- dependency: dependency
232
- ).rewrite(content)
233
- end
234
-
235
- def replace_git_pin(content)
236
- FileUpdaters::Ruby::Bundler::GitPinReplacer.new(
237
- dependency: dependency,
238
- new_pin: replacement_git_pin
239
- ).rewrite(content)
240
- end
241
-
242
- def lock_ruby_version(gemfile_content)
243
- top_level_gemspecs.each do |gs|
244
- gemfile_content =
245
- RubyRequirementSetter.new(gemspec: gs).rewrite(gemfile_content)
246
- end
247
-
248
- gemfile_content
249
- end
250
-
251
- def lock_ruby_version?(file)
252
- @lock_ruby_version && file == gemfile
253
- end
254
-
255
- def replacement_version_for_gemspec(gemspec_content)
256
- return "0.0.1" unless lockfile
257
-
258
- gemspec_specs =
259
- ::Bundler::LockfileParser.new(sanitized_lockfile_content).specs.
260
- select { |s| gemspec_sources.include?(s.source.class) }
261
-
262
- gem_name =
263
- FileUpdaters::Ruby::Bundler::GemspecDependencyNameFinder.
264
- new(gemspec_content: gemspec_content).
265
- dependency_name
266
-
267
- return gemspec_specs.first&.version || "0.0.1" unless gem_name
268
-
269
- spec = gemspec_specs.find { |s| s.name == gem_name }
270
- spec&.version || gemspec_specs.first&.version || "0.0.1"
271
- end
272
-
273
- def sanitized_lockfile_content
274
- re = FileUpdaters::Ruby::Bundler::LockfileUpdater::LOCKFILE_ENDING
275
- lockfile.content.gsub(re, "")
276
- end
277
- end
278
- end
279
- end
280
- end
281
- end
@@ -1,261 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "bundler_definition_ruby_version_patch"
4
- require "bundler_definition_bundler_version_patch"
5
- require "bundler_git_source_patch"
6
-
7
- require "dependabot/update_checkers/ruby/bundler"
8
- require "dependabot/update_checkers/ruby/bundler/requirements_updater"
9
- require "dependabot/file_updaters/ruby/bundler/lockfile_updater"
10
- require "dependabot/file_parsers/ruby/bundler"
11
- require "dependabot/shared_helpers"
12
- require "dependabot/errors"
13
-
14
- module Dependabot
15
- module UpdateCheckers
16
- module Ruby
17
- class Bundler
18
- class ForceUpdater
19
- def initialize(dependency:, dependency_files:, credentials:,
20
- target_version:, requirements_update_strategy:)
21
- @dependency = dependency
22
- @dependency_files = dependency_files
23
- @credentials = credentials
24
- @target_version = target_version
25
- @requirements_update_strategy = requirements_update_strategy
26
- end
27
-
28
- def updated_dependencies
29
- @updated_dependencies ||= force_update
30
- end
31
-
32
- private
33
-
34
- attr_reader :dependency, :dependency_files, :credentials,
35
- :target_version, :requirements_update_strategy
36
-
37
- def force_update
38
- in_a_temporary_bundler_context do
39
- other_updates = []
40
-
41
- begin
42
- definition = build_definition(other_updates: other_updates)
43
- definition.resolve_remotely!
44
- specs = definition.resolve
45
- dependencies_from([dependency] + other_updates, specs)
46
- rescue ::Bundler::VersionConflict => error
47
- # TODO: Not sure this won't unlock way too many things...
48
- new_dependencies_to_unlock =
49
- new_dependencies_to_unlock_from(
50
- error: error,
51
- already_unlocked: other_updates
52
- )
53
-
54
- raise if new_dependencies_to_unlock.none?
55
-
56
- other_updates += new_dependencies_to_unlock
57
- retry
58
- end
59
- end
60
- rescue SharedHelpers::ChildProcessFailed => error
61
- raise_unresolvable_error(error)
62
- end
63
-
64
- #########################
65
- # Bundler context setup #
66
- #########################
67
-
68
- def in_a_temporary_bundler_context
69
- SharedHelpers.in_a_temporary_directory do
70
- write_temporary_dependency_files
71
-
72
- SharedHelpers.in_a_forked_process do
73
- # Remove installed gems from the default Rubygems index
74
- ::Gem::Specification.all = []
75
-
76
- # Set auth details
77
- relevant_credentials.each do |cred|
78
- token = cred["token"] ||
79
- "#{cred['username']}:#{cred['password']}"
80
-
81
- ::Bundler.settings.set_command_option(
82
- cred.fetch("host"),
83
- token.gsub("@", "%40F").gsub("?", "%3F")
84
- )
85
- end
86
-
87
- # Only allow upgrades. Othewise it's unlikely that this
88
- # resolution will be found by the FileUpdater
89
- ::Bundler.settings.set_command_option(
90
- "only_update_to_newer_versions",
91
- true
92
- )
93
-
94
- yield
95
- end
96
- end
97
- end
98
-
99
- def new_dependencies_to_unlock_from(error:, already_unlocked:)
100
- potentials_deps =
101
- error.cause.conflicts.values.
102
- flat_map(&:requirement_trees).
103
- reject do |tree|
104
- next true unless tree.last.requirement.specific?
105
- next false unless tree.last.name == dependency.name
106
-
107
- tree.last.requirement.satisfied_by?(
108
- Gem::Version.new(target_version)
109
- )
110
- end.map(&:first)
111
-
112
- potentials_deps.
113
- reject { |dep| already_unlocked.map(&:name).include?(dep.name) }.
114
- reject { |dep| [dependency.name, "ruby\0"].include?(dep.name) }.
115
- uniq
116
- end
117
-
118
- def raise_unresolvable_error(error)
119
- msg = error.error_class + " with message: " + error.error_message
120
- raise Dependabot::DependencyFileNotResolvable, msg
121
- end
122
-
123
- def build_definition(other_updates:)
124
- gems_to_unlock = other_updates.map(&:name) + [dependency.name]
125
- definition = ::Bundler::Definition.build(
126
- gemfile.name,
127
- lockfile&.name,
128
- gems: gems_to_unlock + subdependencies,
129
- lock_shared_dependencies: true
130
- )
131
-
132
- # Remove the Gemfile / gemspec requirements on the gems we're
133
- # unlocking (i.e., completely unlock them)
134
- gems_to_unlock.each do |gem_name|
135
- unlock_gem(definition: definition, gem_name: gem_name)
136
- end
137
-
138
- # Set the requirement for the gem we're forcing an update of
139
- new_req = Gem::Requirement.create("= #{target_version}")
140
- definition.dependencies.
141
- find { |d| d.name == dependency.name }.
142
- instance_variable_set(:@requirement, new_req)
143
-
144
- definition
145
- end
146
-
147
- def subdependencies
148
- # If there's no lockfile we don't need to worry about
149
- # subdependencies
150
- return [] unless lockfile
151
-
152
- all_deps = ::Bundler::LockfileParser.new(sanitized_lockfile_body).
153
- specs.map(&:name).map(&:to_s)
154
- top_level = ::Bundler::Definition.
155
- build(gemfile.name, lockfile.name, {}).
156
- dependencies.map(&:name).map(&:to_s)
157
-
158
- all_deps - top_level
159
- end
160
-
161
- def unlock_gem(definition:, gem_name:)
162
- dep = definition.dependencies.find { |d| d.name == gem_name }
163
- version = definition.locked_gems.specs.
164
- find { |d| d.name == gem_name }.version
165
-
166
- dep&.instance_variable_set(
167
- :@requirement,
168
- Gem::Requirement.create(">= #{version}")
169
- )
170
- end
171
-
172
- def original_dependencies
173
- @original_dependencies ||=
174
- FileParsers::Ruby::Bundler.new(
175
- dependency_files: dependency_files,
176
- credentials: credentials,
177
- source: nil
178
- ).parse
179
- end
180
-
181
- def dependencies_from(updated_deps, specs)
182
- # You might think we'd want to remove dependencies whose version
183
- # hadn't changed from this array. We don't. We still need to unlock
184
- # them to get Bundler to resolve, because unlocking them is what
185
- # updates their subdependencies.
186
- #
187
- # This is kind of a bug in Bundler, and we should try to fix it,
188
- # but resolving it won't necessarily be easy.
189
- updated_deps.map do |dep|
190
- original_dep =
191
- original_dependencies.find { |d| d.name == dep.name }
192
- spec = specs.find { |d| d.name == dep.name }
193
-
194
- next if spec.version.to_s == original_dep.version
195
-
196
- build_dependency(original_dep, spec)
197
- end.compact
198
- end
199
-
200
- def build_dependency(original_dep, updated_spec)
201
- Dependency.new(
202
- name: updated_spec.name,
203
- version: updated_spec.version.to_s,
204
- requirements:
205
- RequirementsUpdater.new(
206
- requirements: original_dep.requirements,
207
- update_strategy: requirements_update_strategy,
208
- updated_source: source_for(original_dep),
209
- latest_version: updated_spec.version.to_s,
210
- latest_resolvable_version: updated_spec.version.to_s
211
- ).updated_requirements,
212
- previous_version: original_dep.version,
213
- previous_requirements: original_dep.requirements,
214
- package_manager: original_dep.package_manager
215
- )
216
- end
217
-
218
- def source_for(dependency)
219
- dependency.requirements.
220
- find { |r| r.fetch(:source) }&.
221
- fetch(:source)
222
- end
223
-
224
- def gemfile
225
- dependency_files.find { |f| f.name == "Gemfile" } ||
226
- dependency_files.find { |f| f.name == "gems.rb" }
227
- end
228
-
229
- def lockfile
230
- dependency_files.find { |f| f.name == "Gemfile.lock" } ||
231
- dependency_files.find { |f| f.name == "gems.locked" }
232
- end
233
-
234
- def sanitized_lockfile_body
235
- re = FileUpdaters::Ruby::Bundler::LockfileUpdater::LOCKFILE_ENDING
236
- lockfile.content.gsub(re, "")
237
- end
238
-
239
- def write_temporary_dependency_files
240
- dependency_files.each do |file|
241
- path = file.name
242
- FileUtils.mkdir_p(Pathname.new(path).dirname)
243
- File.write(path, file.content)
244
- end
245
-
246
- File.write(lockfile.name, sanitized_lockfile_body) if lockfile
247
- end
248
-
249
- def relevant_credentials
250
- credentials.select do |cred|
251
- next true if cred["type"] == "git_source"
252
- next true if cred["type"] == "rubygems_server"
253
-
254
- false
255
- end
256
- end
257
- end
258
- end
259
- end
260
- end
261
- end