dependabot-bundler 0.95.5 → 0.95.6
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
- 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
|