dependabot-bundler 0.121.1 → 0.124.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.
@@ -1,9 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "dependabot/monkey_patches/bundler/definition_ruby_version_patch"
4
- require "dependabot/monkey_patches/bundler/definition_bundler_version_patch"
5
- require "dependabot/monkey_patches/bundler/git_source_patch"
6
-
7
3
  require "excon"
8
4
 
9
5
  require "dependabot/bundler/update_checker"
@@ -21,8 +17,6 @@ module Dependabot
21
17
  require_relative "shared_bundler_helpers"
22
18
  include SharedBundlerHelpers
23
19
 
24
- GEM_NOT_FOUND_ERROR_REGEX = /locked to (?<name>[^\s]+) \(/.freeze
25
-
26
20
  def initialize(dependency:, unprepared_dependency_files:,
27
21
  repo_contents_path: nil, credentials:, ignored_versions:,
28
22
  raise_on_ignored: false,
@@ -77,42 +71,47 @@ module Dependabot
77
71
  return latest_version_details unless gemfile
78
72
 
79
73
  SharedHelpers.with_git_configured(credentials: credentials) do
80
- in_a_temporary_bundler_context do
81
- dep = dependency_from_definition
82
-
83
- # If the dependency wasn't found in the definition, but *is*
84
- # included in a gemspec, it's because the Gemfile didn't import
85
- # the gemspec. This is unusual, but the correct behaviour if/when
86
- # it happens is to behave as if the repo was gemspec-only.
87
- if dep.nil? && dependency.requirements.any?
88
- next latest_version_details
89
- end
90
-
91
- # Otherwise, if the dependency wasn't found it's because it is a
92
- # subdependency that was removed when attempting to update it.
93
- next nil if dep.nil?
94
-
95
- # If the dependency is Bundler itself then we can't trust the
96
- # version that has been returned (it's the version Dependabot is
97
- # running on, rather than the true latest resolvable version).
98
- next nil if dep.name == "bundler"
99
-
100
- # If the old Gemfile index was used then it won't have checked
101
- # Ruby compatibility. Fix that by doing the check manually (and
102
- # saying no update is possible if the Ruby version is a mismatch)
103
- next nil if ruby_version_incompatible?(dep)
104
-
105
- details = { version: dep.version }
106
- if dep.source.instance_of?(::Bundler::Source::Git)
107
- details[:commit_sha] = dep.source.revision
74
+ # We do not want the helper to handle errors for us as there are
75
+ # some errors we want to handle specifically ourselves, including
76
+ # potentially retrying in the case of the Ruby version being locked
77
+ in_a_native_bundler_context(error_handling: false) do |tmp_dir|
78
+ details = SharedHelpers.run_helper_subprocess(
79
+ command: NativeHelpers.helper_path,
80
+ function: "resolve_version",
81
+ args: {
82
+ dependency_name: dependency.name,
83
+ dependency_requirements: dependency.requirements,
84
+ gemfile_name: gemfile.name,
85
+ lockfile_name: lockfile&.name,
86
+ using_bundler_2: using_bundler_2?,
87
+ dir: tmp_dir,
88
+ credentials: credentials
89
+ }
90
+ )
91
+
92
+ return latest_version_details if details == "latest"
93
+
94
+ if details
95
+ details.transform_keys!(&:to_sym)
96
+
97
+ # If the old Gemfile index was used then it won't have checked
98
+ # Ruby compatibility. Fix that by doing the check manually and
99
+ # saying no update is possible if the Ruby version is a
100
+ # mismatch
101
+ return nil if ruby_version_incompatible?(details)
102
+
103
+ details[:version] = Gem::Version.new(details[:version])
108
104
  end
109
105
  details
110
106
  end
111
107
  end
112
- rescue Dependabot::DependencyFileNotResolvable => e
108
+ rescue Dependabot::SharedHelpers::HelperSubprocessFailed => e
113
109
  return if error_due_to_restrictive_upper_bound?(e)
114
110
  return if circular_dependency_at_new_version?(e)
115
- raise unless ruby_lock_error?(e)
111
+
112
+ # If we are unable to handle the error ourselves, pass it on to the
113
+ # general bundler error handling.
114
+ handle_bundler_errors(e) unless ruby_lock_error?(e)
116
115
 
117
116
  @gemspec_ruby_unlocked = true
118
117
  regenerate_dependency_files_without_ruby_lock && retry
@@ -120,7 +119,9 @@ module Dependabot
120
119
  # rubocop:enable Metrics/PerceivedComplexity
121
120
 
122
121
  def circular_dependency_at_new_version?(error)
123
- return false unless error.message.include?("CyclicDependencyError")
122
+ unless error.error_class.include?("CyclicDependencyError")
123
+ return false
124
+ end
124
125
 
125
126
  error.message.include?("'#{dependency.name}'")
126
127
  end
@@ -155,71 +156,30 @@ module Dependabot
155
156
  ).prepared_dependency_files
156
157
  end
157
158
 
158
- # rubocop:disable Metrics/PerceivedComplexity
159
- def dependency_from_definition(unlock_subdependencies: true)
160
- dependencies_to_unlock = [dependency.name]
161
- dependencies_to_unlock += subdependencies if unlock_subdependencies
162
- begin
163
- definition = build_definition(dependencies_to_unlock)
164
- definition.resolve_remotely!
165
- rescue ::Bundler::GemNotFound => e
166
- unlock_yanked_gem(dependencies_to_unlock, e) && retry
167
- rescue ::Bundler::HTTPError => e
168
- # Retry network errors
169
- attempt ||= 1
170
- attempt += 1
171
- raise if attempt > 3 || !e.message.include?("Network error")
172
-
173
- retry
174
- end
175
-
176
- dep = definition.resolve.find { |d| d.name == dependency.name }
177
- return dep if dep
178
- return if dependency.requirements.any? || !unlock_subdependencies
179
-
180
- # If no definition was found and we're updating a sub-dependency,
181
- # try again but without unlocking any other sub-dependencies
182
- dependency_from_definition(unlock_subdependencies: false)
183
- end
184
-
185
- # rubocop:enable Metrics/PerceivedComplexity
186
-
187
- def unlock_yanked_gem(dependencies_to_unlock, error)
188
- raise unless error.message.match?(GEM_NOT_FOUND_ERROR_REGEX)
189
-
190
- gem_name = error.message.match(GEM_NOT_FOUND_ERROR_REGEX).
191
- named_captures["name"]
192
- raise if dependencies_to_unlock.include?(gem_name)
193
-
194
- dependencies_to_unlock << gem_name
195
- end
196
-
197
- def subdependencies
198
- # If there's no lockfile we don't need to worry about
199
- # subdependencies
200
- return [] unless lockfile
201
-
202
- all_deps = ::Bundler::LockfileParser.new(sanitized_lockfile_body).
203
- specs.map(&:name).map(&:to_s).uniq
204
- top_level = build_definition([]).dependencies.
205
- map(&:name).map(&:to_s)
206
-
207
- all_deps - top_level
159
+ def latest_version_details
160
+ @latest_version_details ||=
161
+ LatestVersionFinder.new(
162
+ dependency: dependency,
163
+ dependency_files: dependency_files,
164
+ repo_contents_path: repo_contents_path,
165
+ credentials: credentials,
166
+ ignored_versions: ignored_versions,
167
+ raise_on_ignored: @raise_on_ignored,
168
+ security_advisories: []
169
+ ).latest_version_details
208
170
  end
209
171
 
210
- def ruby_version_incompatible?(dep)
211
- return false unless dep.source.is_a?(::Bundler::Source::Rubygems)
212
-
213
- fetcher = dep.source.fetchers.first.fetchers.first
214
-
172
+ def ruby_version_incompatible?(details)
215
173
  # It's only the old index we have a problem with
216
- return false unless fetcher.is_a?(::Bundler::Fetcher::Dependency)
174
+ unless details[:fetcher] == "Bundler::Fetcher::Dependency"
175
+ return false
176
+ end
217
177
 
218
178
  # If no Ruby version is specified, we don't have a problem
219
- return false unless ruby_version
179
+ return false unless details[:ruby_version]
220
180
 
221
181
  versions = Excon.get(
222
- "#{fetcher.fetch_uri}api/v1/versions/#{dependency.name}.json",
182
+ "https://rubygems.org/api/v1/versions/#{dependency.name}.json",
223
183
  idempotent: true,
224
184
  **SharedHelpers.excon_defaults
225
185
  )
@@ -230,53 +190,23 @@ module Dependabot
230
190
 
231
191
  ruby_requirement =
232
192
  JSON.parse(versions.body).
233
- find { |details| details["number"] == dep.version.to_s }&.
193
+ find { |version| version["number"] == details[:version] }&.
234
194
  fetch("ruby_version", nil)
235
195
 
236
196
  # Give the benefit of the doubt if we can't find the version's
237
197
  # required Ruby version.
238
198
  return false unless ruby_requirement
239
199
 
240
- ruby_requirement = Requirement.new(ruby_requirement)
200
+ ruby_requirement = Gem::Requirement.new(ruby_requirement)
201
+ current_ruby_version = Gem::Version.new(details[:ruby_version])
241
202
 
242
- !ruby_requirement.satisfied_by?(ruby_version)
203
+ !ruby_requirement.satisfied_by?(current_ruby_version)
243
204
  rescue JSON::ParserError, Excon::Error::Socket, Excon::Error::Timeout
244
205
  # Give the benefit of the doubt if something goes wrong fetching
245
206
  # version details (could be that it's a private index, etc.)
246
207
  false
247
208
  end
248
209
 
249
- def build_definition(dependencies_to_unlock)
250
- # Note: we lock shared dependencies to avoid any top-level
251
- # dependencies getting unlocked (which would happen if they were
252
- # also subdependencies of the dependency being unlocked)
253
- ::Bundler::Definition.build(
254
- gemfile.name,
255
- lockfile&.name,
256
- gems: dependencies_to_unlock,
257
- lock_shared_dependencies: true
258
- )
259
- end
260
-
261
- def ruby_version
262
- return nil unless gemfile
263
-
264
- @ruby_version ||= build_definition([]).ruby_version&.gem_version
265
- end
266
-
267
- def latest_version_details
268
- @latest_version_details ||=
269
- LatestVersionFinder.new(
270
- dependency: dependency,
271
- dependency_files: dependency_files,
272
- repo_contents_path: repo_contents_path,
273
- credentials: credentials,
274
- ignored_versions: ignored_versions,
275
- raise_on_ignored: @raise_on_ignored,
276
- security_advisories: []
277
- ).latest_version_details
278
- end
279
-
280
210
  def gemfile
281
211
  dependency_files.find { |f| f.name == "Gemfile" } ||
282
212
  dependency_files.find { |f| f.name == "gems.rb" }
@@ -287,9 +217,10 @@ module Dependabot
287
217
  dependency_files.find { |f| f.name == "gems.locked" }
288
218
  end
289
219
 
290
- def sanitized_lockfile_body
291
- re = FileUpdater::LockfileUpdater::LOCKFILE_ENDING
292
- lockfile.content.gsub(re, "")
220
+ def using_bundler_2?
221
+ return unless lockfile
222
+
223
+ lockfile.content.match?(/BUNDLED WITH\s+2/m)
293
224
  end
294
225
  end
295
226
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dependabot-bundler
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.121.1
4
+ version: 0.124.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dependabot
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-10-07 00:00:00.000000000 Z
11
+ date: 2020-10-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dependabot-common
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 0.121.1
19
+ version: 0.124.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - '='
25
25
  - !ruby/object:Gem::Version
26
- version: 0.121.1
26
+ version: 0.124.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: byebug
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -100,14 +100,42 @@ dependencies:
100
100
  requirements:
101
101
  - - "~>"
102
102
  - !ruby/object:Gem::Version
103
- version: 0.92.0
103
+ version: 0.93.0
104
104
  type: :development
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
108
  - - "~>"
109
109
  - !ruby/object:Gem::Version
110
- version: 0.92.0
110
+ version: 0.93.0
111
+ - !ruby/object:Gem::Dependency
112
+ name: simplecov
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: 0.19.0
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: 0.19.0
125
+ - !ruby/object:Gem::Dependency
126
+ name: simplecov-console
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: 0.7.2
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: 0.7.2
111
139
  - !ruby/object:Gem::Dependency
112
140
  name: vcr
113
141
  requirement: !ruby/object:Gem::Requirement
@@ -163,6 +191,7 @@ files:
163
191
  - lib/dependabot/bundler/file_updater/requirement_replacer.rb
164
192
  - lib/dependabot/bundler/file_updater/ruby_requirement_setter.rb
165
193
  - lib/dependabot/bundler/metadata_finder.rb
194
+ - lib/dependabot/bundler/native_helpers.rb
166
195
  - lib/dependabot/bundler/requirement.rb
167
196
  - lib/dependabot/bundler/update_checker.rb
168
197
  - lib/dependabot/bundler/update_checker/file_preparer.rb
@@ -173,9 +202,6 @@ files:
173
202
  - lib/dependabot/bundler/update_checker/shared_bundler_helpers.rb
174
203
  - lib/dependabot/bundler/update_checker/version_resolver.rb
175
204
  - lib/dependabot/bundler/version.rb
176
- - lib/dependabot/monkey_patches/bundler/definition_bundler_version_patch.rb
177
- - lib/dependabot/monkey_patches/bundler/definition_ruby_version_patch.rb
178
- - lib/dependabot/monkey_patches/bundler/git_source_patch.rb
179
205
  homepage: https://github.com/dependabot/dependabot-core
180
206
  licenses:
181
207
  - Nonstandard
@@ -1,15 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "bundler/definition"
4
-
5
- # Ignore the Bundler version specified in the Gemfile (since the only Bundler
6
- # version available to us is the one we're using).
7
- module Bundler
8
- class Definition
9
- def expanded_dependencies
10
- @expanded_dependencies ||=
11
- expand_dependencies(dependencies + metadata_dependencies, @remote).
12
- reject { |d| d.name == "bundler" }
13
- end
14
- end
15
- end
@@ -1,17 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module BundlerDefinitionRubyVersionPatch
4
- def index
5
- @index ||= super.tap do
6
- if ruby_version
7
- requested_version = ruby_version.to_gem_version_with_patchlevel
8
- sources.metadata_source.specs <<
9
- Gem::Specification.new("ruby\0", requested_version)
10
- end
11
-
12
- sources.metadata_source.specs <<
13
- Gem::Specification.new("ruby\0", "2.5.3p105")
14
- end
15
- end
16
- end
17
- Bundler::Definition.prepend(BundlerDefinitionRubyVersionPatch)
@@ -1,61 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Bundler
4
- class Source
5
- class Git
6
- class GitProxy
7
- private
8
-
9
- # Bundler allows ssh authentication when talking to GitHub but there's
10
- # no way for Dependabot to do so (it doesn't have any ssh keys).
11
- # Instead, we convert all `git@github.com:` URLs to use HTTPS.
12
- def configured_uri_for(uri)
13
- uri = uri.gsub(%r{git@(.*?):/?}, 'https://\1/')
14
- if uri.match?(/https?:/)
15
- remote = URI(uri)
16
- config_auth =
17
- Bundler.settings[remote.to_s] || Bundler.settings[remote.host]
18
- remote.userinfo ||= config_auth
19
- remote.to_s
20
- else
21
- uri
22
- end
23
- end
24
- end
25
- end
26
- end
27
- end
28
-
29
- module Bundler
30
- class Source
31
- class Git < Path
32
- private
33
-
34
- def serialize_gemspecs_in(destination)
35
- original_load_paths = $LOAD_PATH.dup
36
- reduced_load_paths = original_load_paths.
37
- reject { |p| p.include?("/gems/") }
38
-
39
- $LOAD_PATH.shift until $LOAD_PATH.empty?
40
- reduced_load_paths.each { |p| $LOAD_PATH << p }
41
-
42
- if destination.relative?
43
- destination = destination.expand_path(Bundler.root)
44
- end
45
- Dir["#{destination}/#{@glob}"].each do |spec_path|
46
- # Evaluate gemspecs and cache the result. Gemspecs
47
- # in git might require git or other dependencies.
48
- # The gemspecs we cache should already be evaluated.
49
- spec = Bundler.load_gemspec(spec_path)
50
- next unless spec
51
-
52
- Bundler.rubygems.set_installed_by_version(spec)
53
- Bundler.rubygems.validate(spec)
54
- File.open(spec_path, "wb") { |file| file.write(spec.to_ruby) }
55
- end
56
- $LOAD_PATH.shift until $LOAD_PATH.empty?
57
- original_load_paths.each { |p| $LOAD_PATH << p }
58
- end
59
- end
60
- end
61
- end