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.
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dependabot
4
+ module Bundler
5
+ module NativeHelpers
6
+ def self.helper_path
7
+ "bundle exec ruby #{File.join(native_helpers_root, 'run.rb')}"
8
+ end
9
+
10
+ def self.native_helpers_root
11
+ helpers_root = ENV["DEPENDABOT_NATIVE_HELPERS_PATH"]
12
+ return File.join(helpers_root, "bundler") unless helpers_root.nil?
13
+
14
+ File.join(__dir__, "../../../helpers")
15
+ end
16
+ end
17
+ end
18
+ end
@@ -283,6 +283,7 @@ module Dependabot
283
283
  end
284
284
  # rubocop:enable Metrics/PerceivedComplexity
285
285
 
286
+ # TODO: Stop sanitizing the lockfile once we have bundler 2 installed
286
287
  def sanitized_lockfile_content
287
288
  re = FileUpdater::LockfileUpdater::LOCKFILE_ENDING
288
289
  lockfile.content.gsub(re, "")
@@ -1,20 +1,20 @@
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
-
3
+ require "dependabot/bundler/file_parser"
4
+ require "dependabot/bundler/file_updater/lockfile_updater"
5
+ require "dependabot/bundler/native_helpers"
7
6
  require "dependabot/bundler/update_checker"
8
7
  require "dependabot/bundler/update_checker/requirements_updater"
9
- require "dependabot/bundler/file_updater/lockfile_updater"
10
- require "dependabot/bundler/file_parser"
11
- require "dependabot/shared_helpers"
12
8
  require "dependabot/errors"
9
+ require "dependabot/shared_helpers"
13
10
 
14
11
  module Dependabot
15
12
  module Bundler
16
13
  class UpdateChecker
17
14
  class ForceUpdater
15
+ require_relative "shared_bundler_helpers"
16
+ include SharedBundlerHelpers
17
+
18
18
  def initialize(dependency:, dependency_files:, repo_contents_path: nil,
19
19
  credentials:, target_version:,
20
20
  requirements_update_strategy:,
@@ -42,154 +42,28 @@ module Dependabot
42
42
  end
43
43
 
44
44
  def force_update
45
- in_a_temporary_bundler_context do
46
- other_updates = []
47
-
48
- begin
49
- definition = build_definition(other_updates: other_updates)
50
- definition.resolve_remotely!
51
- specs = definition.resolve
52
- dependencies_from([dependency] + other_updates, specs)
53
- rescue ::Bundler::VersionConflict => e
54
- raise unless update_multiple_dependencies?
55
-
56
- # TODO: Not sure this won't unlock way too many things...
57
- new_dependencies_to_unlock =
58
- new_dependencies_to_unlock_from(
59
- error: e,
60
- already_unlocked: other_updates
61
- )
62
-
63
- raise if new_dependencies_to_unlock.none?
64
-
65
- other_updates += new_dependencies_to_unlock
66
- retry
67
- end
68
- end
69
- rescue SharedHelpers::ChildProcessFailed => e
70
- raise_unresolvable_error(e)
71
- end
72
-
73
- #########################
74
- # Bundler context setup #
75
- #########################
76
-
77
- def in_a_temporary_bundler_context
78
- base_directory = dependency_files.first.directory
79
- SharedHelpers.in_a_temporary_repo_directory(base_directory,
80
- repo_contents_path) do
81
- write_temporary_dependency_files
82
-
83
- SharedHelpers.in_a_forked_process do
84
- # Remove installed gems from the default Rubygems index
85
- ::Gem::Specification.all =
86
- ::Gem::Specification.send(:default_stubs, "*.gemspec")
87
-
88
- # Set flags and credentials
89
- set_bundler_flags_and_credentials
90
-
91
- yield
92
- end
45
+ in_a_native_bundler_context(error_handling: false) do |tmp_dir|
46
+ updated_deps, specs = SharedHelpers.run_helper_subprocess(
47
+ command: NativeHelpers.helper_path,
48
+ function: "force_update",
49
+ args: {
50
+ dir: tmp_dir,
51
+ dependency_name: dependency.name,
52
+ target_version: target_version,
53
+ credentials: relevant_credentials,
54
+ gemfile_name: gemfile.name,
55
+ lockfile_name: lockfile.name,
56
+ using_bundler_2: using_bundler_2?,
57
+ update_multiple_dependencies: update_multiple_dependencies?
58
+ }
59
+ )
60
+ dependencies_from(updated_deps, specs)
93
61
  end
94
- end
95
-
96
- def new_dependencies_to_unlock_from(error:, already_unlocked:)
97
- potentials_deps =
98
- relevant_conflicts(error, already_unlocked).
99
- flat_map(&:requirement_trees).
100
- reject do |tree|
101
- # If the final requirement wasn't specific, it can't be binding
102
- next true if tree.last.requirement == Gem::Requirement.new(">= 0")
103
-
104
- # If the conflict wasn't for the dependency we're updating then
105
- # we don't have enough info to reject it
106
- next false unless tree.last.name == dependency.name
107
-
108
- # If the final requirement *was* for the dependency we're updating
109
- # then we can ignore the tree if it permits the target version
110
- tree.last.requirement.satisfied_by?(
111
- Gem::Version.new(target_version)
112
- )
113
- end.map(&:first)
114
-
115
- potentials_deps.
116
- reject { |dep| already_unlocked.map(&:name).include?(dep.name) }.
117
- reject { |dep| [dependency.name, "ruby\0"].include?(dep.name) }.
118
- uniq
119
- end
120
-
121
- def relevant_conflicts(error, dependencies_being_unlocked)
122
- names = [*dependencies_being_unlocked.map(&:name), dependency.name]
123
-
124
- # For a conflict to be relevant to the updates we're making it must be
125
- # 1) caused by a new requirement introduced by our unlocking, or
126
- # 2) caused by an old requirement that prohibits the update.
127
- # Hence, we look at the beginning and end of the requirement trees
128
- error.cause.conflicts.values.
129
- select do |conflict|
130
- conflict.requirement_trees.any? do |t|
131
- names.include?(t.last.name) || names.include?(t.first.name)
132
- end
133
- end
134
- end
135
-
136
- def raise_unresolvable_error(error)
137
- msg = error.error_class + " with message: " + error.error_message
62
+ rescue SharedHelpers::HelperSubprocessFailed => e
63
+ msg = e.error_class + " with message: " + e.message
138
64
  raise Dependabot::DependencyFileNotResolvable, msg
139
65
  end
140
66
 
141
- def build_definition(other_updates:)
142
- gems_to_unlock = other_updates.map(&:name) + [dependency.name]
143
- definition = ::Bundler::Definition.build(
144
- gemfile.name,
145
- lockfile&.name,
146
- gems: gems_to_unlock + subdependencies,
147
- lock_shared_dependencies: true
148
- )
149
-
150
- # Remove the Gemfile / gemspec requirements on the gems we're
151
- # unlocking (i.e., completely unlock them)
152
- gems_to_unlock.each do |gem_name|
153
- unlock_gem(definition: definition, gem_name: gem_name)
154
- end
155
-
156
- # Set the requirement for the gem we're forcing an update of
157
- new_req = Gem::Requirement.create("= #{target_version}")
158
- definition.dependencies.
159
- find { |d| d.name == dependency.name }.
160
- tap do |dep|
161
- dep.instance_variable_set(:@requirement, new_req)
162
- dep.source = nil if dep.source.is_a?(::Bundler::Source::Git)
163
- end
164
-
165
- definition
166
- end
167
-
168
- def subdependencies
169
- # If there's no lockfile we don't need to worry about
170
- # subdependencies
171
- return [] unless lockfile
172
-
173
- all_deps = ::Bundler::LockfileParser.new(sanitized_lockfile_body).
174
- specs.map(&:name).map(&:to_s)
175
- top_level = ::Bundler::Definition.
176
- build(gemfile.name, lockfile.name, {}).
177
- dependencies.map(&:name).map(&:to_s)
178
-
179
- all_deps - top_level
180
- end
181
-
182
- def unlock_gem(definition:, gem_name:)
183
- dep = definition.dependencies.find { |d| d.name == gem_name }
184
- version = definition.locked_gems.specs.
185
- find { |d| d.name == gem_name }.version
186
-
187
- dep&.instance_variable_set(
188
- :@requirement,
189
- Gem::Requirement.create(">= #{version}")
190
- )
191
- end
192
-
193
67
  def original_dependencies
194
68
  @original_dependencies ||=
195
69
  FileParser.new(
@@ -209,10 +83,10 @@ module Dependabot
209
83
  # but resolving it won't necessarily be easy.
210
84
  updated_deps.map do |dep|
211
85
  original_dep =
212
- original_dependencies.find { |d| d.name == dep.name }
213
- spec = specs.find { |d| d.name == dep.name }
86
+ original_dependencies.find { |d| d.name == dep.fetch("name") }
87
+ spec = specs.find { |d| d.fetch("name") == dep.fetch("name") }
214
88
 
215
- next if spec.version.to_s == original_dep.version
89
+ next if spec.fetch("version") == original_dep.version
216
90
 
217
91
  build_dependency(original_dep, spec)
218
92
  end.compact
@@ -220,15 +94,15 @@ module Dependabot
220
94
 
221
95
  def build_dependency(original_dep, updated_spec)
222
96
  Dependency.new(
223
- name: updated_spec.name,
224
- version: updated_spec.version.to_s,
97
+ name: updated_spec.fetch("name"),
98
+ version: updated_spec.fetch("version"),
225
99
  requirements:
226
100
  RequirementsUpdater.new(
227
101
  requirements: original_dep.requirements,
228
102
  update_strategy: requirements_update_strategy,
229
103
  updated_source: source_for(original_dep),
230
- latest_version: updated_spec.version.to_s,
231
- latest_resolvable_version: updated_spec.version.to_s
104
+ latest_version: updated_spec.fetch("version"),
105
+ latest_resolvable_version: updated_spec.fetch("version")
232
106
  ).updated_requirements,
233
107
  previous_version: original_dep.version,
234
108
  previous_requirements: original_dep.requirements,
@@ -267,34 +141,6 @@ module Dependabot
267
141
  File.write(lockfile.name, sanitized_lockfile_body) if lockfile
268
142
  end
269
143
 
270
- def set_bundler_flags_and_credentials
271
- # Set auth details
272
- relevant_credentials.each do |cred|
273
- token = cred["token"] ||
274
- "#{cred['username']}:#{cred['password']}"
275
-
276
- ::Bundler.settings.set_command_option(
277
- cred.fetch("host"),
278
- token.gsub("@", "%40F").gsub("?", "%3F")
279
- )
280
- end
281
-
282
- # Only allow upgrades. Otherwise it's unlikely that this
283
- # resolution will be found by the FileUpdater
284
- ::Bundler.settings.set_command_option(
285
- "only_update_to_newer_versions",
286
- true
287
- )
288
-
289
- # Use HTTPS for GitHub if lockfile was generated by Bundler 2
290
- set_bundler_2_flags if using_bundler_2?
291
- end
292
-
293
- def set_bundler_2_flags
294
- ::Bundler.settings.set_command_option("forget_cli_options", "true")
295
- ::Bundler.settings.set_command_option("github.https", "true")
296
- end
297
-
298
144
  def relevant_credentials
299
145
  credentials.
300
146
  select { |cred| cred["password"] || cred["token"] }.
@@ -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"
@@ -47,27 +47,26 @@ module Dependabot
47
47
  def latest_git_version_details
48
48
  return unless git?
49
49
 
50
- dependency_source_details =
50
+ source_details =
51
51
  dependency.requirements.map { |r| r.fetch(:source) }.
52
52
  uniq.compact.first
53
53
 
54
- in_a_temporary_bundler_context do
55
- SharedHelpers.with_git_configured(credentials: credentials) do
56
- # Note: we don't set `ref`, as we want to unpin the dependency
57
- source = ::Bundler::Source::Git.new(
58
- "uri" => dependency_source_details[:url],
59
- "branch" => dependency_source_details[:branch],
60
- "name" => dependency.name,
61
- "submodules" => true
54
+ SharedHelpers.with_git_configured(credentials: credentials) do
55
+ in_a_native_bundler_context do |tmp_dir|
56
+ SharedHelpers.run_helper_subprocess(
57
+ command: NativeHelpers.helper_path,
58
+ function: "depencency_source_latest_git_version",
59
+ args: {
60
+ dir: tmp_dir,
61
+ gemfile_name: gemfile.name,
62
+ dependency_name: dependency.name,
63
+ credentials: credentials,
64
+ dependency_source_url: source_details[:url],
65
+ dependency_source_branch: source_details[:branch]
66
+ }
62
67
  )
63
-
64
- # Tell Bundler we're fine with fetching the source remotely
65
- source.instance_variable_set(:@allow_remote, true)
66
-
67
- spec = source.specs.first
68
- { version: spec.version, commit_sha: spec.source.revision }
69
68
  end
70
- end
69
+ end.transform_keys(&:to_sym)
71
70
  end
72
71
 
73
72
  def git?
@@ -98,46 +97,38 @@ module Dependabot
98
97
 
99
98
  def private_registry_versions
100
99
  @private_registry_versions ||=
101
- in_a_temporary_bundler_context do
102
- bundler_source.
103
- fetchers.flat_map do |fetcher|
104
- fetcher.
105
- specs_with_retry([dependency.name], bundler_source).
106
- search_all(dependency.name)
107
- end.
108
- map(&:version)
109
- end
110
- end
111
-
112
- def bundler_source
113
- return nil unless gemfile
114
-
115
- @bundler_source ||=
116
- in_a_temporary_bundler_context do
117
- definition = ::Bundler::Definition.build(gemfile.name, nil, {})
118
-
119
- specified_source =
120
- definition.dependencies.
121
- find { |dep| dep.name == dependency.name }&.source
122
-
123
- specified_source || definition.send(:sources).default_source
100
+ in_a_native_bundler_context do |tmp_dir|
101
+ SharedHelpers.run_helper_subprocess(
102
+ command: NativeHelpers.helper_path,
103
+ function: "private_registry_versions",
104
+ args: {
105
+ dir: tmp_dir,
106
+ gemfile_name: gemfile.name,
107
+ dependency_name: dependency.name,
108
+ credentials: credentials
109
+ }
110
+ ).map do |version_string|
111
+ Gem::Version.new(version_string)
112
+ end
124
113
  end
125
114
  end
126
115
 
127
116
  def source_type
128
- @source_type ||= case bundler_source
129
- when ::Bundler::Source::Rubygems
130
- remote = bundler_source.remotes.first
131
- if remote.nil? || remote.to_s == "https://rubygems.org/"
132
- RUBYGEMS
133
- else
134
- PRIVATE_REGISTRY
135
- end
136
- when ::Bundler::Source::Git
137
- GIT
138
- else
139
- OTHER
140
- end
117
+ return @source_type if defined? @source_type
118
+ return @source_type = RUBYGEMS unless gemfile
119
+
120
+ @source_type = in_a_native_bundler_context do |tmp_dir|
121
+ SharedHelpers.run_helper_subprocess(
122
+ command: NativeHelpers.helper_path,
123
+ function: "dependency_source_type",
124
+ args: {
125
+ dir: tmp_dir,
126
+ gemfile_name: gemfile.name,
127
+ dependency_name: dependency.name,
128
+ credentials: credentials
129
+ }
130
+ )
131
+ end
141
132
  end
142
133
 
143
134
  def gemfile
@@ -1,12 +1,9 @@
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"
6
+ require "dependabot/bundler/native_helpers"
10
7
  require "dependabot/shared_helpers"
11
8
  require "dependabot/errors"
12
9
 
@@ -17,6 +14,18 @@ module Dependabot
17
14
  GIT_REGEX = /reset --hard [^\s]*` in directory (?<path>[^\s]*)/.freeze
18
15
  GIT_REF_REGEX = /not exist in the repository (?<path>[^\s]*)\./.freeze
19
16
  PATH_REGEX = /The path `(?<path>.*)` does not exist/.freeze
17
+
18
+ module BundlerErrorPatterns
19
+ MISSING_AUTH_REGEX =
20
+ /bundle config (?<source>.*) username:password/.freeze
21
+ BAD_AUTH_REGEX =
22
+ /Bad username or password for (?<source>.*)\.$/.freeze
23
+ BAD_CERT_REGEX =
24
+ /verify the SSL certificate for (?<source>.*)\.$/.freeze
25
+ HTTP_ERR_REGEX =
26
+ /Could not fetch specs from (?<source>.*)$/.freeze
27
+ end
28
+
20
29
  RETRYABLE_ERRORS = %w(
21
30
  Bundler::HTTPError
22
31
  Bundler::Fetcher::FallbackError
@@ -35,27 +44,15 @@ module Dependabot
35
44
  # Bundler context setup #
36
45
  #########################
37
46
 
38
- def in_a_temporary_bundler_context(error_handling: true)
47
+ def in_a_native_bundler_context(error_handling: true)
39
48
  SharedHelpers.
40
49
  in_a_temporary_repo_directory(base_directory,
41
50
  repo_contents_path) do |tmp_dir|
42
51
  write_temporary_dependency_files
43
52
 
44
- SharedHelpers.in_a_forked_process do
45
- # Set the path for path gemspec correctly
46
- ::Bundler.instance_variable_set(:@root, tmp_dir)
47
-
48
- # Remove installed gems from the default Rubygems index
49
- ::Gem::Specification.all =
50
- ::Gem::Specification.send(:default_stubs, "*.gemspec")
51
-
52
- # Set flags and credentials
53
- set_bundler_flags_and_credentials
54
-
55
- yield
56
- end
53
+ yield(tmp_dir)
57
54
  end
58
- rescue SharedHelpers::ChildProcessFailed, ArgumentError => e
55
+ rescue SharedHelpers::HelperSubprocessFailed => e
59
56
  retry_count ||= 0
60
57
  retry_count += 1
61
58
  if retryable_error?(e) && retry_count <= 2
@@ -70,8 +67,7 @@ module Dependabot
70
67
  end
71
68
 
72
69
  def retryable_error?(error)
73
- return true if error.message == "marshal data too short"
74
- return false if error.is_a?(ArgumentError)
70
+ return true if error.error_class == "JSON::ParserError"
75
71
  return true if RETRYABLE_ERRORS.include?(error.error_class)
76
72
 
77
73
  unless RETRYABLE_PRIVATE_REGISTRY_ERRORS.include?(error.error_class)
@@ -86,13 +82,12 @@ module Dependabot
86
82
  # rubocop:disable Metrics/AbcSize
87
83
  # rubocop:disable Metrics/MethodLength
88
84
  def handle_bundler_errors(error)
89
- if error.message == "marshal data too short"
85
+ if error.error_class == "JSON::ParserError"
90
86
  msg = "Error evaluating your dependency files: #{error.message}"
91
87
  raise Dependabot::DependencyFileNotEvaluatable, msg
92
88
  end
93
- raise if error.is_a?(ArgumentError)
94
89
 
95
- msg = error.error_class + " with message: " + error.error_message
90
+ msg = error.error_class + " with message: " + error.message
96
91
 
97
92
  case error.error_class
98
93
  when "Bundler::Dsl::DSLError", "Bundler::GemspecError"
@@ -100,28 +95,30 @@ module Dependabot
100
95
  raise Dependabot::DependencyFileNotEvaluatable, msg
101
96
  when "Bundler::Source::Git::MissingGitRevisionError"
102
97
  gem_name =
103
- error.error_message.match(GIT_REF_REGEX).
98
+ error.message.match(GIT_REF_REGEX).
104
99
  named_captures["path"].
105
100
  split("/").last
106
101
  raise GitDependencyReferenceNotFound, gem_name
107
102
  when "Bundler::PathError"
108
103
  gem_name =
109
- error.error_message.match(PATH_REGEX).
104
+ error.message.match(PATH_REGEX).
110
105
  named_captures["path"].
111
106
  split("/").last.split("-")[0..-2].join
112
107
  raise Dependabot::PathDependenciesNotReachable, [gem_name]
113
108
  when "Bundler::Source::Git::GitCommandError"
114
- if error.error_message.match?(GIT_REGEX)
109
+ if error.message.match?(GIT_REGEX)
115
110
  # We couldn't find the specified branch / commit (or the two
116
111
  # weren't compatible).
117
112
  gem_name =
118
- error.error_message.match(GIT_REGEX).
113
+ error.message.match(GIT_REGEX).
119
114
  named_captures["path"].
120
115
  split("/").last.split("-")[0..-2].join
121
116
  raise GitDependencyReferenceNotFound, gem_name
122
117
  end
123
118
 
124
- bad_uris = inaccessible_git_dependencies.map { |s| s.source.uri }
119
+ bad_uris = inaccessible_git_dependencies.map do |spec|
120
+ spec.fetch("uri")
121
+ end
125
122
  raise unless bad_uris.any?
126
123
 
127
124
  # We don't have access to one of repos required
@@ -134,21 +131,21 @@ module Dependabot
134
131
  # - the gem was specified at an incompatible version
135
132
  raise Dependabot::DependencyFileNotResolvable, msg
136
133
  when "Bundler::Fetcher::AuthenticationRequiredError"
137
- regex = /bundle config (?<source>.*) username:password/
138
- source = error.error_message.match(regex)[:source]
134
+ regex = BundlerErrorPatterns::MISSING_AUTH_REGEX
135
+ source = error.message.match(regex)[:source]
139
136
  raise Dependabot::PrivateSourceAuthenticationFailure, source
140
137
  when "Bundler::Fetcher::BadAuthenticationError"
141
- regex = /Bad username or password for (?<source>.*)\.$/
142
- source = error.error_message.match(regex)[:source]
138
+ regex = BundlerErrorPatterns::BAD_AUTH_REGEX
139
+ source = error.message.match(regex)[:source]
143
140
  raise Dependabot::PrivateSourceAuthenticationFailure, source
144
141
  when "Bundler::Fetcher::CertificateFailureError"
145
- regex = /verify the SSL certificate for (?<source>.*)\.$/
146
- source = error.error_message.match(regex)[:source]
142
+ regex = BundlerErrorPatterns::BAD_CERT_REGEX
143
+ source = error.message.match(regex)[:source]
147
144
  raise Dependabot::PrivateSourceCertificateFailure, source
148
145
  when "Bundler::HTTPError"
149
- regex = /Could not fetch specs from (?<source>.*)$/
150
- if error.error_message.match?(regex)
151
- source = error.error_message.match(regex)[:source]
146
+ regex = BundlerErrorPatterns::HTTP_ERR_REGEX
147
+ if error.message.match?(regex)
148
+ source = error.message.match(regex)[:source]
152
149
  raise if source.end_with?("rubygems.org/")
153
150
 
154
151
  raise Dependabot::PrivateSourceTimedOut, source
@@ -156,7 +153,7 @@ module Dependabot
156
153
 
157
154
  # JFrog can serve a 403 if the credentials provided are good but
158
155
  # don't have access to a particular gem.
159
- raise unless error.error_message.include?("permitted to deploy")
156
+ raise unless error.message.include?("permitted to deploy")
160
157
  raise unless jfrog_source
161
158
 
162
159
  raise Dependabot::PrivateSourceAuthenticationFailure, jfrog_source
@@ -169,39 +166,41 @@ module Dependabot
169
166
  # rubocop:enable Metrics/MethodLength
170
167
 
171
168
  def inaccessible_git_dependencies
172
- in_a_temporary_bundler_context(error_handling: false) do
173
- ::Bundler::Definition.build(gemfile.name, nil, {}).dependencies.
174
- reject do |spec|
175
- next true unless spec.source.is_a?(::Bundler::Source::Git)
176
-
177
- # Piggy-back off some private Bundler methods to configure the
178
- # URI with auth details in the same way Bundler does.
179
- git_proxy = spec.source.send(:git_proxy)
180
- uri = spec.source.uri.gsub("git://", "https://")
181
- uri = git_proxy.send(:configured_uri_for, uri)
182
- uri += ".git" unless uri.end_with?(".git")
183
- uri += "/info/refs?service=git-upload-pack"
184
-
185
- begin
186
- Excon.get(
187
- uri,
188
- idempotent: true,
189
- **SharedHelpers.excon_defaults
190
- ).status == 200
191
- rescue Excon::Error::Socket, Excon::Error::Timeout
192
- false
193
- end
194
- end
169
+ in_a_native_bundler_context(error_handling: false) do |tmp_dir|
170
+ git_specs = SharedHelpers.run_helper_subprocess(
171
+ command: NativeHelpers.helper_path,
172
+ function: "git_specs",
173
+ args: {
174
+ dir: tmp_dir,
175
+ gemfile_name: gemfile.name,
176
+ credentials: relevant_credentials,
177
+ using_bundler_2: using_bundler_2?
178
+ }
179
+ )
180
+ git_specs.reject do |spec|
181
+ Excon.get(
182
+ spec.fetch("auth_uri"),
183
+ idempotent: true,
184
+ **SharedHelpers.excon_defaults
185
+ ).status == 200
186
+ rescue Excon::Error::Socket, Excon::Error::Timeout
187
+ false
188
+ end
195
189
  end
196
190
  end
197
191
 
198
192
  def jfrog_source
199
- in_a_temporary_bundler_context(error_handling: false) do
200
- ::Bundler::Definition.build(gemfile.name, nil, {}).
201
- send(:sources).
202
- rubygems_remotes.
203
- find { |uri| uri.host.include?("jfrog") }&.
204
- host
193
+ in_a_native_bundler_context(error_handling: false) do |dir|
194
+ SharedHelpers.run_helper_subprocess(
195
+ command: NativeHelpers.helper_path,
196
+ function: "jfrog_source",
197
+ args: {
198
+ dir: dir,
199
+ gemfile_name: gemfile.name,
200
+ credentials: relevant_credentials,
201
+ using_bundler_2: using_bundler_2?
202
+ }
203
+ )
205
204
  end
206
205
  end
207
206
 
@@ -215,27 +214,6 @@ module Dependabot
215
214
  File.write(lockfile.name, sanitized_lockfile_body) if lockfile
216
215
  end
217
216
 
218
- def set_bundler_flags_and_credentials
219
- # Set auth details
220
- relevant_credentials.each do |cred|
221
- token = cred["token"] ||
222
- "#{cred['username']}:#{cred['password']}"
223
-
224
- ::Bundler.settings.set_command_option(
225
- cred.fetch("host"),
226
- token.gsub("@", "%40F").gsub("?", "%3F")
227
- )
228
- end
229
-
230
- # Use HTTPS for GitHub if lockfile was generated by Bundler 2
231
- set_bundler_2_flags if using_bundler_2?
232
- end
233
-
234
- def set_bundler_2_flags
235
- ::Bundler.settings.set_command_option("forget_cli_options", "true")
236
- ::Bundler.settings.set_command_option("github.https", "true")
237
- end
238
-
239
217
  def relevant_credentials
240
218
  [
241
219
  *git_source_credentials,
@@ -264,6 +242,7 @@ module Dependabot
264
242
  dependency_files.find { |f| f.name == "gems.locked" }
265
243
  end
266
244
 
245
+ # TODO: Stop sanitizing the lockfile once we have bundler 2 installed
267
246
  def sanitized_lockfile_body
268
247
  re = FileUpdater::LockfileUpdater::LOCKFILE_ENDING
269
248
  lockfile.content.gsub(re, "")