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.
- checksums.yaml +4 -4
- metadata +4 -38
- data/helpers/Makefile +0 -9
- data/helpers/build +0 -26
- data/lib/dependabot/bundler.rb +0 -27
- data/lib/dependabot/bundler/file_fetcher.rb +0 -216
- data/lib/dependabot/bundler/file_fetcher/child_gemfile_finder.rb +0 -68
- data/lib/dependabot/bundler/file_fetcher/gemspec_finder.rb +0 -96
- data/lib/dependabot/bundler/file_fetcher/path_gemspec_finder.rb +0 -112
- data/lib/dependabot/bundler/file_fetcher/require_relative_finder.rb +0 -65
- data/lib/dependabot/bundler/file_parser.rb +0 -297
- data/lib/dependabot/bundler/file_parser/file_preparer.rb +0 -84
- data/lib/dependabot/bundler/file_parser/gemfile_checker.rb +0 -46
- data/lib/dependabot/bundler/file_updater.rb +0 -125
- data/lib/dependabot/bundler/file_updater/gemfile_updater.rb +0 -114
- data/lib/dependabot/bundler/file_updater/gemspec_dependency_name_finder.rb +0 -50
- data/lib/dependabot/bundler/file_updater/gemspec_sanitizer.rb +0 -298
- data/lib/dependabot/bundler/file_updater/gemspec_updater.rb +0 -62
- data/lib/dependabot/bundler/file_updater/git_pin_replacer.rb +0 -78
- data/lib/dependabot/bundler/file_updater/git_source_remover.rb +0 -100
- data/lib/dependabot/bundler/file_updater/lockfile_updater.rb +0 -387
- data/lib/dependabot/bundler/file_updater/requirement_replacer.rb +0 -221
- data/lib/dependabot/bundler/metadata_finder.rb +0 -204
- data/lib/dependabot/bundler/requirement.rb +0 -29
- data/lib/dependabot/bundler/update_checker.rb +0 -334
- data/lib/dependabot/bundler/update_checker/file_preparer.rb +0 -279
- data/lib/dependabot/bundler/update_checker/force_updater.rb +0 -259
- data/lib/dependabot/bundler/update_checker/latest_version_finder.rb +0 -165
- data/lib/dependabot/bundler/update_checker/requirements_updater.rb +0 -281
- data/lib/dependabot/bundler/update_checker/ruby_requirement_setter.rb +0 -113
- data/lib/dependabot/bundler/update_checker/shared_bundler_helpers.rb +0 -244
- data/lib/dependabot/bundler/update_checker/version_resolver.rb +0 -272
- data/lib/dependabot/bundler/version.rb +0 -13
- data/lib/dependabot/monkey_patches/bundler/definition_bundler_version_patch.rb +0 -15
- data/lib/dependabot/monkey_patches/bundler/definition_ruby_version_patch.rb +0 -14
- data/lib/dependabot/monkey_patches/bundler/git_source_patch.rb +0 -27
@@ -1,259 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
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
|
-
require "dependabot/bundler/update_checker"
|
8
|
-
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
|
-
require "dependabot/errors"
|
13
|
-
|
14
|
-
module Dependabot
|
15
|
-
module Bundler
|
16
|
-
class UpdateChecker
|
17
|
-
class ForceUpdater
|
18
|
-
def initialize(dependency:, dependency_files:, credentials:,
|
19
|
-
target_version:, requirements_update_strategy:)
|
20
|
-
@dependency = dependency
|
21
|
-
@dependency_files = dependency_files
|
22
|
-
@credentials = credentials
|
23
|
-
@target_version = target_version
|
24
|
-
@requirements_update_strategy = requirements_update_strategy
|
25
|
-
end
|
26
|
-
|
27
|
-
def updated_dependencies
|
28
|
-
@updated_dependencies ||= force_update
|
29
|
-
end
|
30
|
-
|
31
|
-
private
|
32
|
-
|
33
|
-
attr_reader :dependency, :dependency_files, :credentials,
|
34
|
-
:target_version, :requirements_update_strategy
|
35
|
-
|
36
|
-
def force_update
|
37
|
-
in_a_temporary_bundler_context do
|
38
|
-
other_updates = []
|
39
|
-
|
40
|
-
begin
|
41
|
-
definition = build_definition(other_updates: other_updates)
|
42
|
-
definition.resolve_remotely!
|
43
|
-
specs = definition.resolve
|
44
|
-
dependencies_from([dependency] + other_updates, specs)
|
45
|
-
rescue ::Bundler::VersionConflict => error
|
46
|
-
# TODO: Not sure this won't unlock way too many things...
|
47
|
-
new_dependencies_to_unlock =
|
48
|
-
new_dependencies_to_unlock_from(
|
49
|
-
error: error,
|
50
|
-
already_unlocked: other_updates
|
51
|
-
)
|
52
|
-
|
53
|
-
raise if new_dependencies_to_unlock.none?
|
54
|
-
|
55
|
-
other_updates += new_dependencies_to_unlock
|
56
|
-
retry
|
57
|
-
end
|
58
|
-
end
|
59
|
-
rescue SharedHelpers::ChildProcessFailed => error
|
60
|
-
raise_unresolvable_error(error)
|
61
|
-
end
|
62
|
-
|
63
|
-
#########################
|
64
|
-
# Bundler context setup #
|
65
|
-
#########################
|
66
|
-
|
67
|
-
def in_a_temporary_bundler_context
|
68
|
-
SharedHelpers.in_a_temporary_directory do
|
69
|
-
write_temporary_dependency_files
|
70
|
-
|
71
|
-
SharedHelpers.in_a_forked_process do
|
72
|
-
# Remove installed gems from the default Rubygems index
|
73
|
-
::Gem::Specification.all = []
|
74
|
-
|
75
|
-
# Set auth details
|
76
|
-
relevant_credentials.each do |cred|
|
77
|
-
token = cred["token"] ||
|
78
|
-
"#{cred['username']}:#{cred['password']}"
|
79
|
-
|
80
|
-
::Bundler.settings.set_command_option(
|
81
|
-
cred.fetch("host"),
|
82
|
-
token.gsub("@", "%40F").gsub("?", "%3F")
|
83
|
-
)
|
84
|
-
end
|
85
|
-
|
86
|
-
# Only allow upgrades. Othewise it's unlikely that this
|
87
|
-
# resolution will be found by the FileUpdater
|
88
|
-
::Bundler.settings.set_command_option(
|
89
|
-
"only_update_to_newer_versions",
|
90
|
-
true
|
91
|
-
)
|
92
|
-
|
93
|
-
yield
|
94
|
-
end
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
def new_dependencies_to_unlock_from(error:, already_unlocked:)
|
99
|
-
potentials_deps =
|
100
|
-
error.cause.conflicts.values.
|
101
|
-
flat_map(&:requirement_trees).
|
102
|
-
reject do |tree|
|
103
|
-
next true unless tree.last.requirement.specific?
|
104
|
-
next false unless tree.last.name == dependency.name
|
105
|
-
|
106
|
-
tree.last.requirement.satisfied_by?(
|
107
|
-
Gem::Version.new(target_version)
|
108
|
-
)
|
109
|
-
end.map(&:first)
|
110
|
-
|
111
|
-
potentials_deps.
|
112
|
-
reject { |dep| already_unlocked.map(&:name).include?(dep.name) }.
|
113
|
-
reject { |dep| [dependency.name, "ruby\0"].include?(dep.name) }.
|
114
|
-
uniq
|
115
|
-
end
|
116
|
-
|
117
|
-
def raise_unresolvable_error(error)
|
118
|
-
msg = error.error_class + " with message: " + error.error_message
|
119
|
-
raise Dependabot::DependencyFileNotResolvable, msg
|
120
|
-
end
|
121
|
-
|
122
|
-
def build_definition(other_updates:)
|
123
|
-
gems_to_unlock = other_updates.map(&:name) + [dependency.name]
|
124
|
-
definition = ::Bundler::Definition.build(
|
125
|
-
gemfile.name,
|
126
|
-
lockfile&.name,
|
127
|
-
gems: gems_to_unlock + subdependencies,
|
128
|
-
lock_shared_dependencies: true
|
129
|
-
)
|
130
|
-
|
131
|
-
# Remove the Gemfile / gemspec requirements on the gems we're
|
132
|
-
# unlocking (i.e., completely unlock them)
|
133
|
-
gems_to_unlock.each do |gem_name|
|
134
|
-
unlock_gem(definition: definition, gem_name: gem_name)
|
135
|
-
end
|
136
|
-
|
137
|
-
# Set the requirement for the gem we're forcing an update of
|
138
|
-
new_req = Gem::Requirement.create("= #{target_version}")
|
139
|
-
definition.dependencies.
|
140
|
-
find { |d| d.name == dependency.name }.
|
141
|
-
instance_variable_set(:@requirement, new_req)
|
142
|
-
|
143
|
-
definition
|
144
|
-
end
|
145
|
-
|
146
|
-
def subdependencies
|
147
|
-
# If there's no lockfile we don't need to worry about
|
148
|
-
# subdependencies
|
149
|
-
return [] unless lockfile
|
150
|
-
|
151
|
-
all_deps = ::Bundler::LockfileParser.new(sanitized_lockfile_body).
|
152
|
-
specs.map(&:name).map(&:to_s)
|
153
|
-
top_level = ::Bundler::Definition.
|
154
|
-
build(gemfile.name, lockfile.name, {}).
|
155
|
-
dependencies.map(&:name).map(&:to_s)
|
156
|
-
|
157
|
-
all_deps - top_level
|
158
|
-
end
|
159
|
-
|
160
|
-
def unlock_gem(definition:, gem_name:)
|
161
|
-
dep = definition.dependencies.find { |d| d.name == gem_name }
|
162
|
-
version = definition.locked_gems.specs.
|
163
|
-
find { |d| d.name == gem_name }.version
|
164
|
-
|
165
|
-
dep&.instance_variable_set(
|
166
|
-
:@requirement,
|
167
|
-
Gem::Requirement.create(">= #{version}")
|
168
|
-
)
|
169
|
-
end
|
170
|
-
|
171
|
-
def original_dependencies
|
172
|
-
@original_dependencies ||=
|
173
|
-
FileParser.new(
|
174
|
-
dependency_files: dependency_files,
|
175
|
-
credentials: credentials,
|
176
|
-
source: nil
|
177
|
-
).parse
|
178
|
-
end
|
179
|
-
|
180
|
-
def dependencies_from(updated_deps, specs)
|
181
|
-
# You might think we'd want to remove dependencies whose version
|
182
|
-
# hadn't changed from this array. We don't. We still need to unlock
|
183
|
-
# them to get Bundler to resolve, because unlocking them is what
|
184
|
-
# updates their subdependencies.
|
185
|
-
#
|
186
|
-
# This is kind of a bug in Bundler, and we should try to fix it,
|
187
|
-
# but resolving it won't necessarily be easy.
|
188
|
-
updated_deps.map do |dep|
|
189
|
-
original_dep =
|
190
|
-
original_dependencies.find { |d| d.name == dep.name }
|
191
|
-
spec = specs.find { |d| d.name == dep.name }
|
192
|
-
|
193
|
-
next if spec.version.to_s == original_dep.version
|
194
|
-
|
195
|
-
build_dependency(original_dep, spec)
|
196
|
-
end.compact
|
197
|
-
end
|
198
|
-
|
199
|
-
def build_dependency(original_dep, updated_spec)
|
200
|
-
Dependency.new(
|
201
|
-
name: updated_spec.name,
|
202
|
-
version: updated_spec.version.to_s,
|
203
|
-
requirements:
|
204
|
-
RequirementsUpdater.new(
|
205
|
-
requirements: original_dep.requirements,
|
206
|
-
update_strategy: requirements_update_strategy,
|
207
|
-
updated_source: source_for(original_dep),
|
208
|
-
latest_version: updated_spec.version.to_s,
|
209
|
-
latest_resolvable_version: updated_spec.version.to_s
|
210
|
-
).updated_requirements,
|
211
|
-
previous_version: original_dep.version,
|
212
|
-
previous_requirements: original_dep.requirements,
|
213
|
-
package_manager: original_dep.package_manager
|
214
|
-
)
|
215
|
-
end
|
216
|
-
|
217
|
-
def source_for(dependency)
|
218
|
-
dependency.requirements.
|
219
|
-
find { |r| r.fetch(:source) }&.
|
220
|
-
fetch(:source)
|
221
|
-
end
|
222
|
-
|
223
|
-
def gemfile
|
224
|
-
dependency_files.find { |f| f.name == "Gemfile" } ||
|
225
|
-
dependency_files.find { |f| f.name == "gems.rb" }
|
226
|
-
end
|
227
|
-
|
228
|
-
def lockfile
|
229
|
-
dependency_files.find { |f| f.name == "Gemfile.lock" } ||
|
230
|
-
dependency_files.find { |f| f.name == "gems.locked" }
|
231
|
-
end
|
232
|
-
|
233
|
-
def sanitized_lockfile_body
|
234
|
-
re = FileUpdater::LockfileUpdater::LOCKFILE_ENDING
|
235
|
-
lockfile.content.gsub(re, "")
|
236
|
-
end
|
237
|
-
|
238
|
-
def write_temporary_dependency_files
|
239
|
-
dependency_files.each do |file|
|
240
|
-
path = file.name
|
241
|
-
FileUtils.mkdir_p(Pathname.new(path).dirname)
|
242
|
-
File.write(path, file.content)
|
243
|
-
end
|
244
|
-
|
245
|
-
File.write(lockfile.name, sanitized_lockfile_body) if lockfile
|
246
|
-
end
|
247
|
-
|
248
|
-
def relevant_credentials
|
249
|
-
credentials.select do |cred|
|
250
|
-
next true if cred["type"] == "git_source"
|
251
|
-
next true if cred["type"] == "rubygems_server"
|
252
|
-
|
253
|
-
false
|
254
|
-
end
|
255
|
-
end
|
256
|
-
end
|
257
|
-
end
|
258
|
-
end
|
259
|
-
end
|
@@ -1,165 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
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
|
-
require "excon"
|
8
|
-
|
9
|
-
require "dependabot/bundler/update_checker"
|
10
|
-
require "dependabot/bundler/requirement"
|
11
|
-
require "dependabot/shared_helpers"
|
12
|
-
require "dependabot/errors"
|
13
|
-
|
14
|
-
module Dependabot
|
15
|
-
module Bundler
|
16
|
-
class UpdateChecker
|
17
|
-
class LatestVersionFinder
|
18
|
-
require_relative "shared_bundler_helpers"
|
19
|
-
include SharedBundlerHelpers
|
20
|
-
|
21
|
-
def initialize(dependency:, dependency_files:, credentials:,
|
22
|
-
ignored_versions:)
|
23
|
-
@dependency = dependency
|
24
|
-
@dependency_files = dependency_files
|
25
|
-
@credentials = credentials
|
26
|
-
@ignored_versions = ignored_versions
|
27
|
-
end
|
28
|
-
|
29
|
-
def latest_version_details
|
30
|
-
@latest_version_details ||= fetch_latest_version_details
|
31
|
-
end
|
32
|
-
|
33
|
-
private
|
34
|
-
|
35
|
-
attr_reader :dependency, :dependency_files, :credentials,
|
36
|
-
:ignored_versions
|
37
|
-
|
38
|
-
def fetch_latest_version_details
|
39
|
-
return latest_rubygems_version_details if dependency.name == "bundler"
|
40
|
-
|
41
|
-
case dependency_source
|
42
|
-
when NilClass then latest_rubygems_version_details
|
43
|
-
when ::Bundler::Source::Rubygems
|
44
|
-
if dependency_source.remotes.none? ||
|
45
|
-
dependency_source.remotes.first.to_s == "https://rubygems.org/"
|
46
|
-
latest_rubygems_version_details
|
47
|
-
else
|
48
|
-
latest_private_version_details
|
49
|
-
end
|
50
|
-
when ::Bundler::Source::Git then latest_git_version_details
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
def latest_rubygems_version_details
|
55
|
-
response = Excon.get(
|
56
|
-
"https://rubygems.org/api/v1/versions/#{dependency.name}.json",
|
57
|
-
idempotent: true,
|
58
|
-
**SharedHelpers.excon_defaults
|
59
|
-
)
|
60
|
-
|
61
|
-
relevant_versions =
|
62
|
-
JSON.parse(response.body).
|
63
|
-
reject do |d|
|
64
|
-
version = Gem::Version.new(d["number"])
|
65
|
-
next true if version.prerelease? && !wants_prerelease?
|
66
|
-
next true if ignore_reqs.any? { |r| r.satisfied_by?(version) }
|
67
|
-
|
68
|
-
false
|
69
|
-
end
|
70
|
-
|
71
|
-
dep = relevant_versions.max_by { |d| Gem::Version.new(d["number"]) }
|
72
|
-
return unless dep
|
73
|
-
|
74
|
-
{
|
75
|
-
version: Gem::Version.new(dep["number"]),
|
76
|
-
sha: dep["sha"]
|
77
|
-
}
|
78
|
-
rescue JSON::ParserError, Excon::Error::Timeout
|
79
|
-
nil
|
80
|
-
end
|
81
|
-
|
82
|
-
def latest_private_version_details
|
83
|
-
in_a_temporary_bundler_context do
|
84
|
-
spec =
|
85
|
-
dependency_source.
|
86
|
-
fetchers.flat_map do |fetcher|
|
87
|
-
fetcher.
|
88
|
-
specs_with_retry([dependency.name], dependency_source).
|
89
|
-
search_all(dependency.name).
|
90
|
-
reject { |s| s.version.prerelease? && !wants_prerelease? }.
|
91
|
-
reject do |s|
|
92
|
-
ignore_reqs.any? { |r| r.satisfied_by?(s.version) }
|
93
|
-
end
|
94
|
-
end.
|
95
|
-
max_by(&:version)
|
96
|
-
spec.nil? ? nil : { version: spec.version }
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
def latest_git_version_details
|
101
|
-
dependency_source_details =
|
102
|
-
dependency.requirements.map { |r| r.fetch(:source) }.
|
103
|
-
uniq.compact.first
|
104
|
-
|
105
|
-
in_a_temporary_bundler_context do
|
106
|
-
SharedHelpers.with_git_configured(credentials: credentials) do
|
107
|
-
# Note: we don't set `ref`, as we want to unpin the dependency
|
108
|
-
source = ::Bundler::Source::Git.new(
|
109
|
-
"uri" => dependency_source_details[:url],
|
110
|
-
"branch" => dependency_source_details[:branch],
|
111
|
-
"name" => dependency.name,
|
112
|
-
"submodules" => true
|
113
|
-
)
|
114
|
-
|
115
|
-
# Tell Bundler we're fine with fetching the source remotely
|
116
|
-
source.instance_variable_set(:@allow_remote, true)
|
117
|
-
|
118
|
-
spec = source.specs.first
|
119
|
-
{ version: spec.version, commit_sha: spec.source.revision }
|
120
|
-
end
|
121
|
-
end
|
122
|
-
end
|
123
|
-
|
124
|
-
def wants_prerelease?
|
125
|
-
@wants_prerelease ||=
|
126
|
-
begin
|
127
|
-
current_version = dependency.version
|
128
|
-
if current_version && Gem::Version.correct?(current_version) &&
|
129
|
-
Gem::Version.new(current_version).prerelease?
|
130
|
-
return true
|
131
|
-
end
|
132
|
-
|
133
|
-
dependency.requirements.any? do |req|
|
134
|
-
req[:requirement].match?(/[a-z]/i)
|
135
|
-
end
|
136
|
-
end
|
137
|
-
end
|
138
|
-
|
139
|
-
def dependency_source
|
140
|
-
return nil unless gemfile
|
141
|
-
|
142
|
-
@dependency_source ||=
|
143
|
-
in_a_temporary_bundler_context do
|
144
|
-
definition = ::Bundler::Definition.build(gemfile.name, nil, {})
|
145
|
-
|
146
|
-
specified_source =
|
147
|
-
definition.dependencies.
|
148
|
-
find { |dep| dep.name == dependency.name }&.source
|
149
|
-
|
150
|
-
specified_source || definition.send(:sources).default_source
|
151
|
-
end
|
152
|
-
end
|
153
|
-
|
154
|
-
def ignore_reqs
|
155
|
-
ignored_versions.map { |req| Gem::Requirement.new(req.split(",")) }
|
156
|
-
end
|
157
|
-
|
158
|
-
def gemfile
|
159
|
-
dependency_files.find { |f| f.name == "Gemfile" } ||
|
160
|
-
dependency_files.find { |f| f.name == "gems.rb" }
|
161
|
-
end
|
162
|
-
end
|
163
|
-
end
|
164
|
-
end
|
165
|
-
end
|
@@ -1,281 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "dependabot/bundler/update_checker"
|
4
|
-
|
5
|
-
module Dependabot
|
6
|
-
module Bundler
|
7
|
-
class UpdateChecker
|
8
|
-
class RequirementsUpdater
|
9
|
-
class UnfixableRequirement < StandardError; end
|
10
|
-
|
11
|
-
ALLOWED_UPDATE_STRATEGIES =
|
12
|
-
%i(bump_versions bump_versions_if_necessary).freeze
|
13
|
-
|
14
|
-
def initialize(requirements:, update_strategy:, updated_source:,
|
15
|
-
latest_version:, latest_resolvable_version:)
|
16
|
-
@requirements = requirements
|
17
|
-
@latest_version = Gem::Version.new(latest_version) if latest_version
|
18
|
-
@updated_source = updated_source
|
19
|
-
@update_strategy = update_strategy
|
20
|
-
|
21
|
-
check_update_strategy
|
22
|
-
|
23
|
-
return unless latest_resolvable_version
|
24
|
-
|
25
|
-
@latest_resolvable_version =
|
26
|
-
Gem::Version.new(latest_resolvable_version)
|
27
|
-
end
|
28
|
-
|
29
|
-
def updated_requirements
|
30
|
-
requirements.map do |req|
|
31
|
-
if req[:file].match?(/\.gemspec/)
|
32
|
-
update_gemspec_requirement(req)
|
33
|
-
else
|
34
|
-
# If a requirement doesn't come from a gemspec, it must be from
|
35
|
-
# a Gemfile.
|
36
|
-
update_gemfile_requirement(req)
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
private
|
42
|
-
|
43
|
-
attr_reader :requirements, :updated_source,
|
44
|
-
:latest_version, :latest_resolvable_version,
|
45
|
-
:update_strategy
|
46
|
-
|
47
|
-
def check_update_strategy
|
48
|
-
return if ALLOWED_UPDATE_STRATEGIES.include?(update_strategy)
|
49
|
-
|
50
|
-
raise "Unknown update strategy: #{update_strategy}"
|
51
|
-
end
|
52
|
-
|
53
|
-
def update_gemfile_requirement(req)
|
54
|
-
req = req.merge(source: updated_source)
|
55
|
-
return req unless latest_resolvable_version
|
56
|
-
|
57
|
-
case update_strategy
|
58
|
-
when :bump_versions
|
59
|
-
update_version_requirement(req)
|
60
|
-
when :bump_versions_if_necessary
|
61
|
-
update_version_requirement_if_needed(req)
|
62
|
-
else raise "Unexpected update strategy: #{update_strategy}"
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
def update_version_requirement_if_needed(req)
|
67
|
-
return req if new_version_satisfies?(req)
|
68
|
-
|
69
|
-
update_version_requirement(req)
|
70
|
-
end
|
71
|
-
|
72
|
-
def update_version_requirement(req)
|
73
|
-
requirements =
|
74
|
-
req[:requirement].split(",").map { |r| Gem::Requirement.new(r) }
|
75
|
-
|
76
|
-
new_requirement =
|
77
|
-
if requirements.any?(&:exact?) then latest_resolvable_version.to_s
|
78
|
-
elsif requirements.any? { |r| r.to_s.start_with?("~>") }
|
79
|
-
tw_req = requirements.find { |r| r.to_s.start_with?("~>") }
|
80
|
-
update_twiddle_version(tw_req, latest_resolvable_version).to_s
|
81
|
-
else
|
82
|
-
update_gemfile_range(requirements).map(&:to_s).join(", ")
|
83
|
-
end
|
84
|
-
|
85
|
-
req.merge(requirement: new_requirement)
|
86
|
-
end
|
87
|
-
|
88
|
-
def new_version_satisfies?(req)
|
89
|
-
original_req = Gem::Requirement.new(req[:requirement].split(","))
|
90
|
-
original_req.satisfied_by?(latest_resolvable_version)
|
91
|
-
end
|
92
|
-
|
93
|
-
def update_gemfile_range(requirements)
|
94
|
-
updated_requirements =
|
95
|
-
requirements.flat_map do |r|
|
96
|
-
next r if r.satisfied_by?(latest_resolvable_version)
|
97
|
-
|
98
|
-
case op = r.requirements.first.first
|
99
|
-
when "<", "<="
|
100
|
-
[update_greatest_version(r, latest_resolvable_version)]
|
101
|
-
when "!="
|
102
|
-
[]
|
103
|
-
else
|
104
|
-
raise "Unexpected operation for unsatisfied Gemfile "\
|
105
|
-
"requirement: #{op}"
|
106
|
-
end
|
107
|
-
end
|
108
|
-
|
109
|
-
binding_requirements(updated_requirements)
|
110
|
-
end
|
111
|
-
|
112
|
-
def at_same_precision(new_version, old_version)
|
113
|
-
release_precision =
|
114
|
-
old_version.to_s.split(".").select { |i| i.match?(/^\d+$/) }.count
|
115
|
-
prerelease_precision =
|
116
|
-
old_version.to_s.split(".").count - release_precision
|
117
|
-
|
118
|
-
new_release =
|
119
|
-
new_version.to_s.split(".").first(release_precision)
|
120
|
-
new_prerelease =
|
121
|
-
new_version.to_s.split(".").
|
122
|
-
drop_while { |i| i.match?(/^\d+$/) }.
|
123
|
-
first([prerelease_precision, 1].max)
|
124
|
-
|
125
|
-
[*new_release, *new_prerelease].join(".")
|
126
|
-
end
|
127
|
-
|
128
|
-
# rubocop:disable Metrics/PerceivedComplexity
|
129
|
-
def update_gemspec_requirement(req)
|
130
|
-
return req unless latest_version && latest_resolvable_version
|
131
|
-
|
132
|
-
requirements =
|
133
|
-
req[:requirement].split(",").map { |r| Gem::Requirement.new(r) }
|
134
|
-
|
135
|
-
return req if requirements.all? do |r|
|
136
|
-
requirement_satisfied?(r, req[:groups])
|
137
|
-
end
|
138
|
-
|
139
|
-
updated_requirements =
|
140
|
-
requirements.flat_map do |r|
|
141
|
-
next r if requirement_satisfied?(r, req[:groups])
|
142
|
-
|
143
|
-
if req[:groups] == ["development"] then bumped_requirements(r)
|
144
|
-
else widened_requirements(r)
|
145
|
-
end
|
146
|
-
end
|
147
|
-
|
148
|
-
updated_requirements = binding_requirements(updated_requirements)
|
149
|
-
req.merge(requirement: updated_requirements.map(&:to_s).join(", "))
|
150
|
-
rescue UnfixableRequirement
|
151
|
-
req.merge(requirement: :unfixable)
|
152
|
-
end
|
153
|
-
# rubocop:enable Metrics/PerceivedComplexity
|
154
|
-
|
155
|
-
def requirement_satisfied?(req, groups)
|
156
|
-
if groups == ["development"]
|
157
|
-
req.satisfied_by?(latest_resolvable_version)
|
158
|
-
else
|
159
|
-
req.satisfied_by?(latest_version)
|
160
|
-
end
|
161
|
-
end
|
162
|
-
|
163
|
-
def binding_requirements(requirements)
|
164
|
-
grouped_by_operator =
|
165
|
-
requirements.group_by { |r| r.requirements.first.first }
|
166
|
-
|
167
|
-
binding_reqs = grouped_by_operator.flat_map do |operator, reqs|
|
168
|
-
case operator
|
169
|
-
when "<", "<=" then reqs.min_by { |r| r.requirements.first.last }
|
170
|
-
when ">", ">=" then reqs.max_by { |r| r.requirements.first.last }
|
171
|
-
else requirements
|
172
|
-
end
|
173
|
-
end.uniq
|
174
|
-
|
175
|
-
binding_reqs << Gem::Requirement.new if binding_reqs.empty?
|
176
|
-
binding_reqs.sort_by { |r| r.requirements.first.last }
|
177
|
-
end
|
178
|
-
|
179
|
-
def widened_requirements(req)
|
180
|
-
op, version = req.requirements.first
|
181
|
-
|
182
|
-
case op
|
183
|
-
when "=", nil
|
184
|
-
if version < latest_resolvable_version
|
185
|
-
[Gem::Requirement.new("#{op} #{latest_resolvable_version}")]
|
186
|
-
else
|
187
|
-
req
|
188
|
-
end
|
189
|
-
when "<", "<=" then [update_greatest_version(req, latest_version)]
|
190
|
-
when "~>" then convert_twidle_to_range(req, latest_version)
|
191
|
-
when "!=" then []
|
192
|
-
when ">", ">=" then raise UnfixableRequirement
|
193
|
-
else raise "Unexpected operation for requirement: #{op}"
|
194
|
-
end
|
195
|
-
end
|
196
|
-
|
197
|
-
def bumped_requirements(req)
|
198
|
-
op, version = req.requirements.first
|
199
|
-
|
200
|
-
case op
|
201
|
-
when "=", nil
|
202
|
-
if version < latest_resolvable_version
|
203
|
-
[Gem::Requirement.new("#{op} #{latest_resolvable_version}")]
|
204
|
-
else
|
205
|
-
req
|
206
|
-
end
|
207
|
-
when "~>"
|
208
|
-
[update_twiddle_version(req, latest_resolvable_version)]
|
209
|
-
when "<", "<=" then [update_greatest_version(req, latest_version)]
|
210
|
-
when "!=" then []
|
211
|
-
when ">", ">=" then raise UnfixableRequirement
|
212
|
-
else raise "Unexpected operation for requirement: #{op}"
|
213
|
-
end
|
214
|
-
end
|
215
|
-
|
216
|
-
# rubocop:disable Metrics/AbcSize
|
217
|
-
def convert_twidle_to_range(requirement, version_to_be_permitted)
|
218
|
-
version = requirement.requirements.first.last
|
219
|
-
version = version.release if version.prerelease?
|
220
|
-
|
221
|
-
index_to_update = [version.segments.count - 2, 0].max
|
222
|
-
|
223
|
-
ub_segments = version_to_be_permitted.segments
|
224
|
-
ub_segments << 0 while ub_segments.count <= index_to_update
|
225
|
-
ub_segments = ub_segments[0..index_to_update]
|
226
|
-
ub_segments[index_to_update] += 1
|
227
|
-
|
228
|
-
lb_segments = version.segments
|
229
|
-
lb_segments.pop while lb_segments.any? && lb_segments.last.zero?
|
230
|
-
|
231
|
-
if lb_segments.none?
|
232
|
-
return [Gem::Requirement.new("< #{ub_segments.join('.')}")]
|
233
|
-
end
|
234
|
-
|
235
|
-
# Ensure versions have the same length as each other (cosmetic)
|
236
|
-
length = [lb_segments.count, ub_segments.count].max
|
237
|
-
lb_segments.fill(0, lb_segments.count...length)
|
238
|
-
ub_segments.fill(0, ub_segments.count...length)
|
239
|
-
|
240
|
-
[
|
241
|
-
Gem::Requirement.new(">= #{lb_segments.join('.')}"),
|
242
|
-
Gem::Requirement.new("< #{ub_segments.join('.')}")
|
243
|
-
]
|
244
|
-
end
|
245
|
-
# rubocop:enable Metrics/AbcSize
|
246
|
-
|
247
|
-
# Updates the version in a "~>" constraint to allow the given version
|
248
|
-
def update_twiddle_version(requirement, version_to_be_permitted)
|
249
|
-
old_version = requirement.requirements.first.last
|
250
|
-
updated_v = at_same_precision(version_to_be_permitted, old_version)
|
251
|
-
Gem::Requirement.new("~> #{updated_v}")
|
252
|
-
end
|
253
|
-
|
254
|
-
# Updates the version in a "<" or "<=" constraint to allow the given
|
255
|
-
# version
|
256
|
-
def update_greatest_version(requirement, version_to_be_permitted)
|
257
|
-
if version_to_be_permitted.is_a?(String)
|
258
|
-
version_to_be_permitted =
|
259
|
-
Gem::Version.new(version_to_be_permitted)
|
260
|
-
end
|
261
|
-
op, version = requirement.requirements.first
|
262
|
-
version = version.release if version.prerelease?
|
263
|
-
|
264
|
-
index_to_update =
|
265
|
-
version.segments.map.with_index { |seg, i| seg.zero? ? 0 : i }.max
|
266
|
-
|
267
|
-
new_segments = version.segments.map.with_index do |_, index|
|
268
|
-
if index < index_to_update
|
269
|
-
version_to_be_permitted.segments[index]
|
270
|
-
elsif index == index_to_update
|
271
|
-
version_to_be_permitted.segments[index] + 1
|
272
|
-
else 0
|
273
|
-
end
|
274
|
-
end
|
275
|
-
|
276
|
-
Gem::Requirement.new("#{op} #{new_segments.join('.')}")
|
277
|
-
end
|
278
|
-
end
|
279
|
-
end
|
280
|
-
end
|
281
|
-
end
|