dependabot-core 0.93.17 → 0.94.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/lib/dependabot/dependency.rb +16 -21
- data/lib/dependabot/file_fetchers.rb +1 -5
- data/lib/dependabot/file_parsers.rb +1 -5
- data/lib/dependabot/file_updaters.rb +1 -5
- data/lib/dependabot/metadata_finders.rb +1 -5
- data/lib/dependabot/pull_request_creator/labeler.rb +26 -24
- data/lib/dependabot/update_checkers.rb +1 -5
- data/lib/dependabot/utils.rb +2 -12
- data/lib/dependabot/version.rb +1 -1
- metadata +1 -28
- data/lib/dependabot/file_fetchers/ruby/bundler.rb +0 -215
- data/lib/dependabot/file_fetchers/ruby/bundler/child_gemfile_finder.rb +0 -70
- data/lib/dependabot/file_fetchers/ruby/bundler/gemspec_finder.rb +0 -98
- data/lib/dependabot/file_fetchers/ruby/bundler/path_gemspec_finder.rb +0 -114
- data/lib/dependabot/file_fetchers/ruby/bundler/require_relative_finder.rb +0 -67
- data/lib/dependabot/file_parsers/ruby/bundler.rb +0 -294
- data/lib/dependabot/file_parsers/ruby/bundler/file_preparer.rb +0 -86
- data/lib/dependabot/file_parsers/ruby/bundler/gemfile_checker.rb +0 -48
- data/lib/dependabot/file_updaters/ruby/bundler.rb +0 -123
- data/lib/dependabot/file_updaters/ruby/bundler/gemfile_updater.rb +0 -116
- data/lib/dependabot/file_updaters/ruby/bundler/gemspec_dependency_name_finder.rb +0 -52
- data/lib/dependabot/file_updaters/ruby/bundler/gemspec_sanitizer.rb +0 -298
- data/lib/dependabot/file_updaters/ruby/bundler/gemspec_updater.rb +0 -64
- data/lib/dependabot/file_updaters/ruby/bundler/git_pin_replacer.rb +0 -80
- data/lib/dependabot/file_updaters/ruby/bundler/git_source_remover.rb +0 -102
- data/lib/dependabot/file_updaters/ruby/bundler/lockfile_updater.rb +0 -389
- data/lib/dependabot/file_updaters/ruby/bundler/requirement_replacer.rb +0 -223
- data/lib/dependabot/metadata_finders/ruby/bundler.rb +0 -202
- data/lib/dependabot/update_checkers/ruby/bundler.rb +0 -331
- data/lib/dependabot/update_checkers/ruby/bundler/file_preparer.rb +0 -281
- data/lib/dependabot/update_checkers/ruby/bundler/force_updater.rb +0 -261
- data/lib/dependabot/update_checkers/ruby/bundler/latest_version_finder.rb +0 -169
- data/lib/dependabot/update_checkers/ruby/bundler/requirements_updater.rb +0 -283
- data/lib/dependabot/update_checkers/ruby/bundler/ruby_requirement_setter.rb +0 -115
- data/lib/dependabot/update_checkers/ruby/bundler/shared_bundler_helpers.rb +0 -246
- data/lib/dependabot/update_checkers/ruby/bundler/version_resolver.rb +0 -272
- data/lib/dependabot/utils/ruby/requirement.rb +0 -26
@@ -1,115 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "parser/current"
|
4
|
-
require "dependabot/update_checkers/ruby/bundler"
|
5
|
-
|
6
|
-
module Dependabot
|
7
|
-
module UpdateCheckers
|
8
|
-
module Ruby
|
9
|
-
class Bundler
|
10
|
-
class RubyRequirementSetter
|
11
|
-
RUBY_VERSIONS =
|
12
|
-
%w(1.8.7 1.9.3 2.0.0 2.1.10 2.2.10 2.3.8 2.4.5 2.5.3 2.6.0).freeze
|
13
|
-
|
14
|
-
attr_reader :gemspec
|
15
|
-
|
16
|
-
def initialize(gemspec:)
|
17
|
-
@gemspec = gemspec
|
18
|
-
end
|
19
|
-
|
20
|
-
def rewrite(content)
|
21
|
-
return content unless gemspec_declares_ruby_requirement?
|
22
|
-
|
23
|
-
buffer = Parser::Source::Buffer.new("(gemfile_content)")
|
24
|
-
buffer.source = content
|
25
|
-
ast = Parser::CurrentRuby.new.parse(buffer)
|
26
|
-
|
27
|
-
if declares_ruby_version?(ast)
|
28
|
-
GemfileRewriter.new(
|
29
|
-
ruby_version: ruby_version
|
30
|
-
).rewrite(buffer, ast)
|
31
|
-
else
|
32
|
-
"ruby '#{ruby_version}'\n" + content
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
private
|
37
|
-
|
38
|
-
def gemspec_declares_ruby_requirement?
|
39
|
-
!ruby_requirement.nil?
|
40
|
-
end
|
41
|
-
|
42
|
-
def declares_ruby_version?(node)
|
43
|
-
return false unless node.is_a?(Parser::AST::Node)
|
44
|
-
return true if node.type == :send && node.children[1] == :ruby
|
45
|
-
|
46
|
-
node.children.any? { |cn| declares_ruby_version?(cn) }
|
47
|
-
end
|
48
|
-
|
49
|
-
def ruby_version
|
50
|
-
requirement = Gem::Requirement.new(ruby_requirement)
|
51
|
-
|
52
|
-
ruby_version =
|
53
|
-
RUBY_VERSIONS.
|
54
|
-
map { |v| Gem::Version.new(v) }.sort.
|
55
|
-
find { |v| requirement.satisfied_by?(v) }
|
56
|
-
|
57
|
-
raise "Couldn't find Ruby version!" unless ruby_version
|
58
|
-
|
59
|
-
ruby_version
|
60
|
-
end
|
61
|
-
|
62
|
-
# rubocop:disable Security/Eval
|
63
|
-
def ruby_requirement
|
64
|
-
ast = Parser::CurrentRuby.parse(gemspec.content)
|
65
|
-
requirement_node = find_ruby_requirement_node(ast)
|
66
|
-
return unless requirement_node
|
67
|
-
|
68
|
-
eval(requirement_node.children[2].loc.expression.source)
|
69
|
-
end
|
70
|
-
# rubocop:enable Security/Eval
|
71
|
-
|
72
|
-
def find_ruby_requirement_node(node)
|
73
|
-
return unless node.is_a?(Parser::AST::Node)
|
74
|
-
return node if declares_ruby_requirement?(node)
|
75
|
-
|
76
|
-
node.children.find do |cn|
|
77
|
-
requirement_node = find_ruby_requirement_node(cn)
|
78
|
-
break requirement_node if requirement_node
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
def declares_ruby_requirement?(node)
|
83
|
-
return false unless node.is_a?(Parser::AST::Node)
|
84
|
-
|
85
|
-
node.children[1] == :required_ruby_version=
|
86
|
-
end
|
87
|
-
|
88
|
-
class GemfileRewriter < Parser::TreeRewriter
|
89
|
-
def initialize(ruby_version:)
|
90
|
-
@ruby_version = ruby_version
|
91
|
-
end
|
92
|
-
|
93
|
-
def on_send(node)
|
94
|
-
return unless declares_ruby_version?(node)
|
95
|
-
|
96
|
-
assigned_version_node = node.children[2]
|
97
|
-
replace(assigned_version_node.loc.expression, "'#{ruby_version}'")
|
98
|
-
end
|
99
|
-
|
100
|
-
private
|
101
|
-
|
102
|
-
attr_reader :ruby_version
|
103
|
-
|
104
|
-
def declares_ruby_version?(node)
|
105
|
-
return false unless node.is_a?(Parser::AST::Node)
|
106
|
-
return false unless node.type == :send
|
107
|
-
|
108
|
-
node.children[1] == :ruby
|
109
|
-
end
|
110
|
-
end
|
111
|
-
end
|
112
|
-
end
|
113
|
-
end
|
114
|
-
end
|
115
|
-
end
|
@@ -1,246 +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 "excon"
|
8
|
-
|
9
|
-
require "dependabot/update_checkers/ruby/bundler"
|
10
|
-
require "dependabot/shared_helpers"
|
11
|
-
require "dependabot/errors"
|
12
|
-
|
13
|
-
module Dependabot
|
14
|
-
module UpdateCheckers
|
15
|
-
module Ruby
|
16
|
-
class Bundler
|
17
|
-
module SharedBundlerHelpers
|
18
|
-
GIT_REGEX = /reset --hard [^\s]*` in directory (?<path>[^\s]*)/.freeze
|
19
|
-
GIT_REF_REGEX = /not exist in the repository (?<path>[^\s]*)\./.freeze
|
20
|
-
PATH_REGEX = /The path `(?<path>.*)` does not exist/.freeze
|
21
|
-
RETRYABLE_ERRORS = %w(
|
22
|
-
Bundler::HTTPError
|
23
|
-
Bundler::Fetcher::FallbackError
|
24
|
-
).freeze
|
25
|
-
RETRYABLE_PRIVATE_REGISTRY_ERRORS = %w(
|
26
|
-
Bundler::GemNotFound
|
27
|
-
Gem::InvalidSpecificationException
|
28
|
-
Bundler::VersionConflict
|
29
|
-
Bundler::HTTPError
|
30
|
-
Bundler::Fetcher::FallbackError
|
31
|
-
).freeze
|
32
|
-
|
33
|
-
attr_reader :dependency_files, :credentials
|
34
|
-
|
35
|
-
#########################
|
36
|
-
# Bundler context setup #
|
37
|
-
#########################
|
38
|
-
|
39
|
-
def in_a_temporary_bundler_context(error_handling: true)
|
40
|
-
base_directory = dependency_files.first.directory
|
41
|
-
SharedHelpers.in_a_temporary_directory(base_directory) do |tmp_dir|
|
42
|
-
write_temporary_dependency_files
|
43
|
-
|
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
|
-
|
51
|
-
# Set auth details
|
52
|
-
relevant_credentials.each do |cred|
|
53
|
-
token = cred["token"] ||
|
54
|
-
"#{cred['username']}:#{cred['password']}"
|
55
|
-
|
56
|
-
::Bundler.settings.set_command_option(
|
57
|
-
cred.fetch("host"),
|
58
|
-
token.gsub("@", "%40F").gsub("?", "%3F")
|
59
|
-
)
|
60
|
-
end
|
61
|
-
|
62
|
-
yield
|
63
|
-
end
|
64
|
-
end
|
65
|
-
rescue SharedHelpers::ChildProcessFailed => error
|
66
|
-
retry_count ||= 0
|
67
|
-
retry_count += 1
|
68
|
-
if retryable_error?(error) && retry_count <= 2
|
69
|
-
sleep(rand(1.0..5.0)) && retry
|
70
|
-
end
|
71
|
-
|
72
|
-
raise unless error_handling
|
73
|
-
|
74
|
-
# Raise more descriptive errors
|
75
|
-
handle_bundler_errors(error)
|
76
|
-
end
|
77
|
-
|
78
|
-
def retryable_error?(error)
|
79
|
-
return true if RETRYABLE_ERRORS.include?(error.error_class)
|
80
|
-
|
81
|
-
unless RETRYABLE_PRIVATE_REGISTRY_ERRORS.include?(error.error_class)
|
82
|
-
return false
|
83
|
-
end
|
84
|
-
|
85
|
-
private_registry_credentials.any?
|
86
|
-
end
|
87
|
-
|
88
|
-
# rubocop:disable Metrics/CyclomaticComplexity
|
89
|
-
# rubocop:disable Metrics/PerceivedComplexity
|
90
|
-
# rubocop:disable Metrics/AbcSize
|
91
|
-
# rubocop:disable Metrics/MethodLength
|
92
|
-
def handle_bundler_errors(error)
|
93
|
-
msg = error.error_class + " with message: " + error.error_message
|
94
|
-
|
95
|
-
case error.error_class
|
96
|
-
when "Bundler::Dsl::DSLError", "Bundler::GemspecError"
|
97
|
-
# We couldn't evaluate the Gemfile, let alone resolve it
|
98
|
-
raise Dependabot::DependencyFileNotEvaluatable, msg
|
99
|
-
when "Bundler::Source::Git::MissingGitRevisionError"
|
100
|
-
gem_name =
|
101
|
-
error.error_message.match(GIT_REF_REGEX).
|
102
|
-
named_captures["path"].
|
103
|
-
split("/").last
|
104
|
-
raise GitDependencyReferenceNotFound, gem_name
|
105
|
-
when "Bundler::PathError"
|
106
|
-
gem_name =
|
107
|
-
error.error_message.match(PATH_REGEX).
|
108
|
-
named_captures["path"].
|
109
|
-
split("/").last.split("-")[0..-2].join
|
110
|
-
raise Dependabot::PathDependenciesNotReachable, [gem_name]
|
111
|
-
when "Bundler::Source::Git::GitCommandError"
|
112
|
-
if error.error_message.match?(GIT_REGEX)
|
113
|
-
# We couldn't find the specified branch / commit (or the two
|
114
|
-
# weren't compatible).
|
115
|
-
gem_name =
|
116
|
-
error.error_message.match(GIT_REGEX).
|
117
|
-
named_captures["path"].
|
118
|
-
split("/").last.split("-")[0..-2].join
|
119
|
-
raise GitDependencyReferenceNotFound, gem_name
|
120
|
-
end
|
121
|
-
|
122
|
-
bad_uris = inaccessible_git_dependencies.map { |s| s.source.uri }
|
123
|
-
raise unless bad_uris.any?
|
124
|
-
|
125
|
-
# We don't have access to one of repos required
|
126
|
-
raise Dependabot::GitDependenciesNotReachable, bad_uris
|
127
|
-
when "Bundler::GemNotFound", "Gem::InvalidSpecificationException",
|
128
|
-
"Bundler::VersionConflict"
|
129
|
-
# Bundler threw an error during resolution. Any of:
|
130
|
-
# - the gem doesn't exist in any of the specified sources
|
131
|
-
# - the gem wasn't specified properly
|
132
|
-
# - the gem was specified at an incompatible version
|
133
|
-
raise Dependabot::DependencyFileNotResolvable, msg
|
134
|
-
when "Bundler::Fetcher::AuthenticationRequiredError"
|
135
|
-
regex = /bundle config (?<source>.*) username:password/
|
136
|
-
source = error.error_message.match(regex)[:source]
|
137
|
-
raise Dependabot::PrivateSourceAuthenticationFailure, source
|
138
|
-
when "Bundler::Fetcher::BadAuthenticationError"
|
139
|
-
regex = /Bad username or password for (?<source>.*)\.$/
|
140
|
-
source = error.error_message.match(regex)[:source]
|
141
|
-
raise Dependabot::PrivateSourceAuthenticationFailure, source
|
142
|
-
when "Bundler::Fetcher::CertificateFailureError"
|
143
|
-
regex = /verify the SSL certificate for (?<source>.*)\.$/
|
144
|
-
source = error.error_message.match(regex)[:source]
|
145
|
-
raise Dependabot::PrivateSourceCertificateFailure, source
|
146
|
-
when "Bundler::HTTPError"
|
147
|
-
regex = /Could not fetch specs from (?<source>.*)$/
|
148
|
-
if error.error_message.match?(regex)
|
149
|
-
source = error.error_message.match(regex)[:source]
|
150
|
-
raise if source.include?("rubygems.org")
|
151
|
-
|
152
|
-
raise Dependabot::PrivateSourceTimedOut, source
|
153
|
-
end
|
154
|
-
|
155
|
-
# JFrog can serve a 403 if the credentials provided are good but
|
156
|
-
# don't have access to a particular gem.
|
157
|
-
raise unless error.error_message.include?("permitted to deploy")
|
158
|
-
raise unless jfrog_source
|
159
|
-
|
160
|
-
raise Dependabot::PrivateSourceAuthenticationFailure, jfrog_source
|
161
|
-
else raise
|
162
|
-
end
|
163
|
-
end
|
164
|
-
# rubocop:enable Metrics/CyclomaticComplexity
|
165
|
-
# rubocop:enable Metrics/PerceivedComplexity
|
166
|
-
# rubocop:enable Metrics/AbcSize
|
167
|
-
# rubocop:enable Metrics/MethodLength
|
168
|
-
|
169
|
-
def inaccessible_git_dependencies
|
170
|
-
in_a_temporary_bundler_context(error_handling: false) do
|
171
|
-
::Bundler::Definition.build(gemfile.name, nil, {}).dependencies.
|
172
|
-
reject do |spec|
|
173
|
-
next true unless spec.source.is_a?(::Bundler::Source::Git)
|
174
|
-
|
175
|
-
# Piggy-back off some private Bundler methods to configure the
|
176
|
-
# URI with auth details in the same way Bundler does.
|
177
|
-
git_proxy = spec.source.send(:git_proxy)
|
178
|
-
uri = spec.source.uri.gsub("git://", "https://")
|
179
|
-
uri = git_proxy.send(:configured_uri_for, uri)
|
180
|
-
uri += ".git" unless uri.end_with?(".git")
|
181
|
-
uri += "/info/refs?service=git-upload-pack"
|
182
|
-
|
183
|
-
begin
|
184
|
-
Excon.get(
|
185
|
-
uri,
|
186
|
-
idempotent: true,
|
187
|
-
**SharedHelpers.excon_defaults
|
188
|
-
).status == 200
|
189
|
-
rescue Excon::Error::Socket, Excon::Error::Timeout
|
190
|
-
false
|
191
|
-
end
|
192
|
-
end
|
193
|
-
end
|
194
|
-
end
|
195
|
-
|
196
|
-
def jfrog_source
|
197
|
-
in_a_temporary_bundler_context(error_handling: false) do
|
198
|
-
::Bundler::Definition.build(gemfile.name, nil, {}).
|
199
|
-
send(:sources).
|
200
|
-
rubygems_remotes.
|
201
|
-
find { |uri| uri.host.include?("jfrog") }&.
|
202
|
-
host
|
203
|
-
end
|
204
|
-
end
|
205
|
-
|
206
|
-
def write_temporary_dependency_files
|
207
|
-
dependency_files.each do |file|
|
208
|
-
path = file.name
|
209
|
-
FileUtils.mkdir_p(Pathname.new(path).dirname)
|
210
|
-
File.write(path, file.content)
|
211
|
-
end
|
212
|
-
|
213
|
-
File.write(lockfile.name, sanitized_lockfile_body) if lockfile
|
214
|
-
end
|
215
|
-
|
216
|
-
def relevant_credentials
|
217
|
-
private_registry_credentials + git_source_credentials
|
218
|
-
end
|
219
|
-
|
220
|
-
def private_registry_credentials
|
221
|
-
credentials.select { |cred| cred["type"] == "rubygems_server" }
|
222
|
-
end
|
223
|
-
|
224
|
-
def git_source_credentials
|
225
|
-
credentials.select { |cred| cred["type"] == "git_source" }
|
226
|
-
end
|
227
|
-
|
228
|
-
def gemfile
|
229
|
-
dependency_files.find { |f| f.name == "Gemfile" } ||
|
230
|
-
dependency_files.find { |f| f.name == "gems.rb" }
|
231
|
-
end
|
232
|
-
|
233
|
-
def lockfile
|
234
|
-
dependency_files.find { |f| f.name == "Gemfile.lock" } ||
|
235
|
-
dependency_files.find { |f| f.name == "gems.locked" }
|
236
|
-
end
|
237
|
-
|
238
|
-
def sanitized_lockfile_body
|
239
|
-
re = FileUpdaters::Ruby::Bundler::LockfileUpdater::LOCKFILE_ENDING
|
240
|
-
lockfile.content.gsub(re, "")
|
241
|
-
end
|
242
|
-
end
|
243
|
-
end
|
244
|
-
end
|
245
|
-
end
|
246
|
-
end
|
@@ -1,272 +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 "excon"
|
8
|
-
|
9
|
-
require "dependabot/update_checkers/ruby/bundler"
|
10
|
-
require "dependabot/file_updaters/ruby/bundler/lockfile_updater"
|
11
|
-
require "dependabot/utils/ruby/requirement"
|
12
|
-
require "dependabot/shared_helpers"
|
13
|
-
require "dependabot/errors"
|
14
|
-
|
15
|
-
module Dependabot
|
16
|
-
module UpdateCheckers
|
17
|
-
module Ruby
|
18
|
-
class Bundler
|
19
|
-
class VersionResolver
|
20
|
-
require_relative "file_preparer"
|
21
|
-
require_relative "latest_version_finder"
|
22
|
-
require_relative "shared_bundler_helpers"
|
23
|
-
include SharedBundlerHelpers
|
24
|
-
|
25
|
-
GEM_NOT_FOUND_ERROR_REGEX = /locked to (?<name>[^\s]+) \(/.freeze
|
26
|
-
|
27
|
-
def initialize(dependency:, unprepared_dependency_files:,
|
28
|
-
credentials:, ignored_versions:,
|
29
|
-
replacement_git_pin: nil, remove_git_source: false,
|
30
|
-
unlock_requirement: true,
|
31
|
-
latest_allowable_version: nil)
|
32
|
-
@dependency = dependency
|
33
|
-
@unprepared_dependency_files = unprepared_dependency_files
|
34
|
-
@credentials = credentials
|
35
|
-
@ignored_versions = ignored_versions
|
36
|
-
@replacement_git_pin = replacement_git_pin
|
37
|
-
@remove_git_source = remove_git_source
|
38
|
-
@unlock_requirement = unlock_requirement
|
39
|
-
@latest_allowable_version = latest_allowable_version
|
40
|
-
end
|
41
|
-
|
42
|
-
def latest_resolvable_version_details
|
43
|
-
@latest_resolvable_version_details ||=
|
44
|
-
fetch_latest_resolvable_version_details
|
45
|
-
end
|
46
|
-
|
47
|
-
private
|
48
|
-
|
49
|
-
attr_reader :dependency, :unprepared_dependency_files, :credentials,
|
50
|
-
:ignored_versions, :replacement_git_pin,
|
51
|
-
:latest_allowable_version
|
52
|
-
|
53
|
-
def remove_git_source?
|
54
|
-
@remove_git_source
|
55
|
-
end
|
56
|
-
|
57
|
-
def unlock_requirement?
|
58
|
-
@unlock_requirement
|
59
|
-
end
|
60
|
-
|
61
|
-
def dependency_files
|
62
|
-
@dependency_files ||=
|
63
|
-
FilePreparer.new(
|
64
|
-
dependency: dependency,
|
65
|
-
dependency_files: unprepared_dependency_files,
|
66
|
-
replacement_git_pin: replacement_git_pin,
|
67
|
-
remove_git_source: remove_git_source?,
|
68
|
-
unlock_requirement: unlock_requirement?,
|
69
|
-
latest_allowable_version: latest_allowable_version
|
70
|
-
).prepared_dependency_files
|
71
|
-
end
|
72
|
-
|
73
|
-
# rubocop:disable Metrics/CyclomaticComplexity
|
74
|
-
# rubocop:disable Metrics/PerceivedComplexity
|
75
|
-
def fetch_latest_resolvable_version_details
|
76
|
-
return latest_version_details unless gemfile
|
77
|
-
|
78
|
-
in_a_temporary_bundler_context do
|
79
|
-
dep = dependency_from_definition
|
80
|
-
|
81
|
-
# If the dependency wasn't found in the definition, but *is*
|
82
|
-
# included in a gemspec, it's because the Gemfile didn't import
|
83
|
-
# the gemspec. This is unusual, but the correct behaviour if/when
|
84
|
-
# it happens is to behave as if the repo was gemspec-only.
|
85
|
-
if dep.nil? && dependency.requirements.any?
|
86
|
-
next latest_version_details
|
87
|
-
end
|
88
|
-
|
89
|
-
# Otherwise, if the dependency wasn't found it's because it is a
|
90
|
-
# subdependency that was removed when attempting to update it.
|
91
|
-
next nil if dep.nil?
|
92
|
-
|
93
|
-
# If the old Gemfile index was used then it won't have checked
|
94
|
-
# Ruby compatibility. Fix that by doing the check manually (and
|
95
|
-
# saying no update is possible if the Ruby version is a mismatch)
|
96
|
-
next nil if ruby_version_incompatible?(dep)
|
97
|
-
|
98
|
-
details = { version: dep.version }
|
99
|
-
if dep.source.instance_of?(::Bundler::Source::Git)
|
100
|
-
details[:commit_sha] = dep.source.revision
|
101
|
-
end
|
102
|
-
details
|
103
|
-
end
|
104
|
-
rescue Dependabot::DependencyFileNotResolvable => error
|
105
|
-
return if ignored_versions.any? && !dependency.appears_in_lockfile?
|
106
|
-
raise unless ruby_lock_error?(error)
|
107
|
-
|
108
|
-
@gemspec_ruby_unlocked = true
|
109
|
-
regenerate_dependency_files_without_ruby_lock && retry
|
110
|
-
end
|
111
|
-
# rubocop:enable Metrics/CyclomaticComplexity
|
112
|
-
# rubocop:enable Metrics/PerceivedComplexity
|
113
|
-
|
114
|
-
def ruby_lock_error?(error)
|
115
|
-
return false unless error.message.include?(" for gem \"ruby\0\"")
|
116
|
-
return false if @gemspec_ruby_unlocked
|
117
|
-
|
118
|
-
dependency_files.any? { |f| f.name.end_with?(".gemspec") }
|
119
|
-
end
|
120
|
-
|
121
|
-
def regenerate_dependency_files_without_ruby_lock
|
122
|
-
@dependency_files =
|
123
|
-
FilePreparer.new(
|
124
|
-
dependency: dependency,
|
125
|
-
dependency_files: unprepared_dependency_files,
|
126
|
-
replacement_git_pin: replacement_git_pin,
|
127
|
-
remove_git_source: remove_git_source?,
|
128
|
-
unlock_requirement: unlock_requirement?,
|
129
|
-
latest_allowable_version: latest_allowable_version,
|
130
|
-
lock_ruby_version: false
|
131
|
-
).prepared_dependency_files
|
132
|
-
end
|
133
|
-
|
134
|
-
# rubocop:disable Metrics/CyclomaticComplexity
|
135
|
-
# rubocop:disable Metrics/PerceivedComplexity
|
136
|
-
def dependency_from_definition(unlock_subdependencies: true)
|
137
|
-
dependencies_to_unlock = [dependency.name]
|
138
|
-
dependencies_to_unlock += subdependencies if unlock_subdependencies
|
139
|
-
begin
|
140
|
-
definition = build_definition(dependencies_to_unlock)
|
141
|
-
definition.resolve_remotely!
|
142
|
-
rescue ::Bundler::GemNotFound => error
|
143
|
-
unlock_yanked_gem(dependencies_to_unlock, error) && retry
|
144
|
-
rescue ::Bundler::HTTPError => error
|
145
|
-
# Retry network errors
|
146
|
-
attempt ||= 1
|
147
|
-
attempt += 1
|
148
|
-
raise if attempt > 3 || !error.message.include?("Network error")
|
149
|
-
|
150
|
-
retry
|
151
|
-
end
|
152
|
-
|
153
|
-
dep = definition.resolve.find { |d| d.name == dependency.name }
|
154
|
-
return dep if dep
|
155
|
-
return if dependency.requirements.any? || !unlock_subdependencies
|
156
|
-
|
157
|
-
# If no definition was found and we're updating a sub-dependency,
|
158
|
-
# try again but without unlocking any other sub-dependencies
|
159
|
-
dependency_from_definition(unlock_subdependencies: false)
|
160
|
-
end
|
161
|
-
# rubocop:enable Metrics/CyclomaticComplexity
|
162
|
-
# rubocop:enable Metrics/PerceivedComplexity
|
163
|
-
|
164
|
-
def unlock_yanked_gem(dependencies_to_unlock, error)
|
165
|
-
raise unless error.message.match?(GEM_NOT_FOUND_ERROR_REGEX)
|
166
|
-
|
167
|
-
gem_name = error.message.match(GEM_NOT_FOUND_ERROR_REGEX).
|
168
|
-
named_captures["name"]
|
169
|
-
raise if dependencies_to_unlock.include?(gem_name)
|
170
|
-
|
171
|
-
dependencies_to_unlock << gem_name
|
172
|
-
end
|
173
|
-
|
174
|
-
def subdependencies
|
175
|
-
# If there's no lockfile we don't need to worry about
|
176
|
-
# subdependencies
|
177
|
-
return [] unless lockfile
|
178
|
-
|
179
|
-
all_deps = ::Bundler::LockfileParser.new(sanitized_lockfile_body).
|
180
|
-
specs.map(&:name).map(&:to_s)
|
181
|
-
top_level = build_definition([]).dependencies.
|
182
|
-
map(&:name).map(&:to_s)
|
183
|
-
|
184
|
-
all_deps - top_level
|
185
|
-
end
|
186
|
-
|
187
|
-
def ruby_version_incompatible?(dep)
|
188
|
-
return false unless dep.source.is_a?(::Bundler::Source::Rubygems)
|
189
|
-
|
190
|
-
fetcher = dep.source.fetchers.first.fetchers.first
|
191
|
-
|
192
|
-
# It's only the old index we have a problem with
|
193
|
-
return false unless fetcher.is_a?(::Bundler::Fetcher::Dependency)
|
194
|
-
|
195
|
-
# If no Ruby version is specified, we don't have a problem
|
196
|
-
return false unless ruby_version
|
197
|
-
|
198
|
-
versions = Excon.get(
|
199
|
-
"#{fetcher.fetch_uri}api/v1/versions/#{dependency.name}.json",
|
200
|
-
idempotent: true,
|
201
|
-
**SharedHelpers.excon_defaults
|
202
|
-
)
|
203
|
-
|
204
|
-
# Give the benefit of the doubt if something goes wrong fetching
|
205
|
-
# version details (could be that it's a private index, etc.)
|
206
|
-
return false unless versions.status == 200
|
207
|
-
|
208
|
-
ruby_requirement =
|
209
|
-
JSON.parse(versions.body).
|
210
|
-
find { |details| details["number"] == dep.version.to_s }&.
|
211
|
-
fetch("ruby_version", nil)
|
212
|
-
|
213
|
-
# Give the benefit of the doubt if we can't find the version's
|
214
|
-
# required Ruby version.
|
215
|
-
return false unless ruby_requirement
|
216
|
-
|
217
|
-
ruby_requirement = Utils::Ruby::Requirement.new(ruby_requirement)
|
218
|
-
|
219
|
-
!ruby_requirement.satisfied_by?(ruby_version)
|
220
|
-
rescue JSON::ParserError, Excon::Error::Socket, Excon::Error::Timeout
|
221
|
-
# Give the benefit of the doubt if something goes wrong fetching
|
222
|
-
# version details (could be that it's a private index, etc.)
|
223
|
-
false
|
224
|
-
end
|
225
|
-
|
226
|
-
def build_definition(dependencies_to_unlock)
|
227
|
-
# Note: we lock shared dependencies to avoid any top-level
|
228
|
-
# dependencies getting unlocked (which would happen if they were
|
229
|
-
# also subdependencies of the dependency being unlocked)
|
230
|
-
::Bundler::Definition.build(
|
231
|
-
gemfile.name,
|
232
|
-
lockfile&.name,
|
233
|
-
gems: dependencies_to_unlock,
|
234
|
-
lock_shared_dependencies: true
|
235
|
-
)
|
236
|
-
end
|
237
|
-
|
238
|
-
def ruby_version
|
239
|
-
return nil unless gemfile
|
240
|
-
|
241
|
-
@ruby_version ||= build_definition([]).ruby_version&.gem_version
|
242
|
-
end
|
243
|
-
|
244
|
-
def latest_version_details
|
245
|
-
@latest_version_details ||=
|
246
|
-
LatestVersionFinder.new(
|
247
|
-
dependency: dependency,
|
248
|
-
dependency_files: dependency_files,
|
249
|
-
credentials: credentials,
|
250
|
-
ignored_versions: ignored_versions
|
251
|
-
).latest_version_details
|
252
|
-
end
|
253
|
-
|
254
|
-
def gemfile
|
255
|
-
dependency_files.find { |f| f.name == "Gemfile" } ||
|
256
|
-
dependency_files.find { |f| f.name == "gems.rb" }
|
257
|
-
end
|
258
|
-
|
259
|
-
def lockfile
|
260
|
-
dependency_files.find { |f| f.name == "Gemfile.lock" } ||
|
261
|
-
dependency_files.find { |f| f.name == "gems.locked" }
|
262
|
-
end
|
263
|
-
|
264
|
-
def sanitized_lockfile_body
|
265
|
-
re = FileUpdaters::Ruby::Bundler::LockfileUpdater::LOCKFILE_ENDING
|
266
|
-
lockfile.content.gsub(re, "")
|
267
|
-
end
|
268
|
-
end
|
269
|
-
end
|
270
|
-
end
|
271
|
-
end
|
272
|
-
end
|