dependabot-dep 0.90.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 +7 -0
- data/helpers/Makefile +9 -0
- data/helpers/build +26 -0
- data/helpers/go.mod +8 -0
- data/helpers/go.sum +2 -0
- data/helpers/importresolver/go.mod +1 -0
- data/helpers/importresolver/main.go +34 -0
- data/helpers/main.go +67 -0
- data/lib/dependabot/dep.rb +11 -0
- data/lib/dependabot/dep/file_fetcher.rb +70 -0
- data/lib/dependabot/dep/file_parser.rb +189 -0
- data/lib/dependabot/dep/file_updater.rb +78 -0
- data/lib/dependabot/dep/file_updater/lockfile_updater.rb +220 -0
- data/lib/dependabot/dep/file_updater/manifest_updater.rb +151 -0
- data/lib/dependabot/dep/metadata_finder.rb +57 -0
- data/lib/dependabot/dep/native_helpers.rb +20 -0
- data/lib/dependabot/dep/path_converter.rb +72 -0
- data/lib/dependabot/dep/requirement.rb +152 -0
- data/lib/dependabot/dep/update_checker.rb +312 -0
- data/lib/dependabot/dep/update_checker/file_preparer.rb +219 -0
- data/lib/dependabot/dep/update_checker/latest_version_finder.rb +167 -0
- data/lib/dependabot/dep/update_checker/requirements_updater.rb +221 -0
- data/lib/dependabot/dep/update_checker/version_resolver.rb +166 -0
- data/lib/dependabot/dep/version.rb +43 -0
- metadata +192 -0
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "toml-rb"
|
|
4
|
+
require "dependabot/dependency_file"
|
|
5
|
+
require "dependabot/dep/file_parser"
|
|
6
|
+
require "dependabot/dep/update_checker"
|
|
7
|
+
|
|
8
|
+
module Dependabot
|
|
9
|
+
module Dep
|
|
10
|
+
class UpdateChecker
|
|
11
|
+
# This class takes a set of dependency files and prepares them for use
|
|
12
|
+
# in Dep::UpdateChecker.
|
|
13
|
+
class FilePreparer
|
|
14
|
+
def initialize(dependency_files:, dependency:,
|
|
15
|
+
remove_git_source: false,
|
|
16
|
+
unlock_requirement: true,
|
|
17
|
+
replacement_git_pin: nil,
|
|
18
|
+
latest_allowable_version: nil)
|
|
19
|
+
@dependency_files = dependency_files
|
|
20
|
+
@dependency = dependency
|
|
21
|
+
@unlock_requirement = unlock_requirement
|
|
22
|
+
@remove_git_source = remove_git_source
|
|
23
|
+
@replacement_git_pin = replacement_git_pin
|
|
24
|
+
@latest_allowable_version = latest_allowable_version
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def prepared_dependency_files
|
|
28
|
+
files = []
|
|
29
|
+
|
|
30
|
+
files << manifest_for_update_check
|
|
31
|
+
files << lockfile if lockfile
|
|
32
|
+
|
|
33
|
+
files
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
attr_reader :dependency_files, :dependency, :replacement_git_pin,
|
|
39
|
+
:latest_allowable_version
|
|
40
|
+
|
|
41
|
+
def unlock_requirement?
|
|
42
|
+
@unlock_requirement
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def remove_git_source?
|
|
46
|
+
@remove_git_source
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def replace_git_pin?
|
|
50
|
+
!replacement_git_pin.nil?
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def manifest_for_update_check
|
|
54
|
+
DependencyFile.new(
|
|
55
|
+
name: manifest.name,
|
|
56
|
+
content: manifest_content_for_update_check(manifest),
|
|
57
|
+
directory: manifest.directory
|
|
58
|
+
)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def manifest_content_for_update_check(file)
|
|
62
|
+
content = file.content
|
|
63
|
+
|
|
64
|
+
content = remove_git_source(content) if remove_git_source?
|
|
65
|
+
content = replace_git_pin(content) if replace_git_pin?
|
|
66
|
+
content = replace_version_constraint(content, file.name)
|
|
67
|
+
content = add_fsnotify_override(content)
|
|
68
|
+
|
|
69
|
+
content
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def remove_git_source(content)
|
|
73
|
+
parsed_manifest = TomlRB.parse(content)
|
|
74
|
+
|
|
75
|
+
Dep::FileParser::REQUIREMENT_TYPES.each do |type|
|
|
76
|
+
(parsed_manifest[type] || []).each do |details|
|
|
77
|
+
next unless details["name"] == dependency.name
|
|
78
|
+
|
|
79
|
+
details.delete("revision")
|
|
80
|
+
details.delete("branch")
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
TomlRB.dump(parsed_manifest)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def replace_git_pin(content)
|
|
88
|
+
parsed_manifest = TomlRB.parse(content)
|
|
89
|
+
|
|
90
|
+
Dep::FileParser::REQUIREMENT_TYPES.each do |type|
|
|
91
|
+
(parsed_manifest[type] || []).each do |details|
|
|
92
|
+
next unless details["name"] == dependency.name
|
|
93
|
+
|
|
94
|
+
raise "Invalid details! #{details}" if details["branch"]
|
|
95
|
+
|
|
96
|
+
if details["version"]
|
|
97
|
+
details["version"] = replacement_git_pin
|
|
98
|
+
else
|
|
99
|
+
details["revision"] = replacement_git_pin
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
TomlRB.dump(parsed_manifest)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Note: We don't need to care about formatting in this method, since
|
|
108
|
+
# we're only using the manifest to find the latest resolvable version
|
|
109
|
+
def replace_version_constraint(content, filename)
|
|
110
|
+
parsed_manifest = TomlRB.parse(content)
|
|
111
|
+
|
|
112
|
+
Dep::FileParser::REQUIREMENT_TYPES.each do |type|
|
|
113
|
+
(parsed_manifest[type] || []).each do |details|
|
|
114
|
+
next unless details["name"] == dependency.name
|
|
115
|
+
next if details["revision"] || details["branch"]
|
|
116
|
+
next if replacement_git_pin
|
|
117
|
+
|
|
118
|
+
updated_req = temporary_requirement_for_resolution(filename)
|
|
119
|
+
|
|
120
|
+
details["version"] = updated_req
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
TomlRB.dump(parsed_manifest)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# A dep bug means we have to specify a source for gopkg.in/fsnotify.v1
|
|
128
|
+
# or we get `panic: version queue is empty` errors
|
|
129
|
+
def add_fsnotify_override(content)
|
|
130
|
+
parsed_manifest = TomlRB.parse(content)
|
|
131
|
+
|
|
132
|
+
overrides = parsed_manifest.fetch("override", [])
|
|
133
|
+
dep_name = "gopkg.in/fsnotify.v1"
|
|
134
|
+
|
|
135
|
+
override = overrides.find { |s| s["name"] == dep_name }
|
|
136
|
+
if override.nil?
|
|
137
|
+
override = { "name" => dep_name }
|
|
138
|
+
overrides << override
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
unless override["source"]
|
|
142
|
+
override["source"] = "gopkg.in/fsnotify/fsnotify.v1"
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
parsed_manifest["override"] = overrides
|
|
146
|
+
TomlRB.dump(parsed_manifest)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def temporary_requirement_for_resolution(filename)
|
|
150
|
+
original_req = dependency.requirements.
|
|
151
|
+
find { |r| r.fetch(:file) == filename }&.
|
|
152
|
+
fetch(:requirement)
|
|
153
|
+
|
|
154
|
+
lower_bound_req =
|
|
155
|
+
if original_req && !unlock_requirement?
|
|
156
|
+
original_req
|
|
157
|
+
else
|
|
158
|
+
">= #{lower_bound_version}"
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
unless latest_allowable_version &&
|
|
162
|
+
version_class.correct?(latest_allowable_version) &&
|
|
163
|
+
version_class.new(latest_allowable_version) >=
|
|
164
|
+
version_class.new(lower_bound_version)
|
|
165
|
+
return lower_bound_req
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
lower_bound_req + ", <= #{latest_allowable_version}"
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def lower_bound_version
|
|
172
|
+
@lower_bound_version ||=
|
|
173
|
+
if version_from_lockfile
|
|
174
|
+
version_from_lockfile
|
|
175
|
+
else
|
|
176
|
+
version_from_requirement =
|
|
177
|
+
dependency.requirements.map { |r| r.fetch(:requirement) }.
|
|
178
|
+
compact.
|
|
179
|
+
flat_map { |req_str| requirement_class.new(req_str) }.
|
|
180
|
+
flat_map(&:requirements).
|
|
181
|
+
reject { |req_array| req_array.first.start_with?("<") }.
|
|
182
|
+
map(&:last).
|
|
183
|
+
max&.to_s
|
|
184
|
+
|
|
185
|
+
version_from_requirement || 0
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def version_from_lockfile
|
|
190
|
+
return unless lockfile
|
|
191
|
+
|
|
192
|
+
TomlRB.parse(lockfile.content).
|
|
193
|
+
fetch("projects", []).
|
|
194
|
+
find { |p| p["name"] == dependency.name }&.
|
|
195
|
+
fetch("version", nil)&.
|
|
196
|
+
sub(/^v?/, "")
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def version_class
|
|
200
|
+
Utils.version_class_for_package_manager(dependency.package_manager)
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def requirement_class
|
|
204
|
+
Utils.requirement_class_for_package_manager(
|
|
205
|
+
dependency.package_manager
|
|
206
|
+
)
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def manifest
|
|
210
|
+
@manifest ||= dependency_files.find { |f| f.name == "Gopkg.toml" }
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def lockfile
|
|
214
|
+
@lockfile ||= dependency_files.find { |f| f.name == "Gopkg.lock" }
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
end
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "excon"
|
|
4
|
+
require "toml-rb"
|
|
5
|
+
|
|
6
|
+
require "dependabot/source"
|
|
7
|
+
require "dependabot/dep/update_checker"
|
|
8
|
+
require "dependabot/git_commit_checker"
|
|
9
|
+
require "dependabot/dep/path_converter"
|
|
10
|
+
|
|
11
|
+
module Dependabot
|
|
12
|
+
module Dep
|
|
13
|
+
class UpdateChecker
|
|
14
|
+
class LatestVersionFinder
|
|
15
|
+
def initialize(dependency:, dependency_files:, credentials:,
|
|
16
|
+
ignored_versions:)
|
|
17
|
+
@dependency = dependency
|
|
18
|
+
@dependency_files = dependency_files
|
|
19
|
+
@credentials = credentials
|
|
20
|
+
@ignored_versions = ignored_versions
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def latest_version
|
|
24
|
+
@latest_version ||=
|
|
25
|
+
if git_dependency? then latest_version_for_git_dependency
|
|
26
|
+
else latest_release_tag_version
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
attr_reader :dependency, :dependency_files, :credentials,
|
|
33
|
+
:ignored_versions
|
|
34
|
+
|
|
35
|
+
def latest_release_tag_version
|
|
36
|
+
if @latest_release_tag_lookup_attempted
|
|
37
|
+
return @latest_release_tag_version
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
@latest_release_tag_lookup_attempted = true
|
|
41
|
+
|
|
42
|
+
latest_release_str = fetch_latest_release_tag&.sub(/^v?/, "")
|
|
43
|
+
return unless latest_release_str
|
|
44
|
+
return unless version_class.correct?(latest_release_str)
|
|
45
|
+
|
|
46
|
+
@latest_release_tag_version =
|
|
47
|
+
version_class.new(latest_release_str)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def fetch_latest_release_tag
|
|
51
|
+
# If this is a git dependency then getting the latest tag is trivial
|
|
52
|
+
if git_dependency?
|
|
53
|
+
return git_commit_checker.
|
|
54
|
+
local_tag_for_latest_version&.fetch(:tag)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# If not, we need to find the URL for the source code.
|
|
58
|
+
path = dependency.requirements.
|
|
59
|
+
map { |r| r.dig(:source, :source) }.compact.first
|
|
60
|
+
path ||= dependency.name
|
|
61
|
+
|
|
62
|
+
source_url = git_source(path)
|
|
63
|
+
return unless source_url
|
|
64
|
+
|
|
65
|
+
# Given a source, we want to find the latest tag. Piggy-back off the
|
|
66
|
+
# logic in GitCommitChecker to do so.
|
|
67
|
+
git_dep = Dependency.new(
|
|
68
|
+
name: dependency.name,
|
|
69
|
+
version: dependency.version,
|
|
70
|
+
requirements: [{
|
|
71
|
+
file: "Gopkg.toml",
|
|
72
|
+
groups: [],
|
|
73
|
+
requirement: nil,
|
|
74
|
+
source: { type: "git", url: source_url }
|
|
75
|
+
}],
|
|
76
|
+
package_manager: dependency.package_manager
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
GitCommitChecker.
|
|
80
|
+
new(dependency: git_dep, credentials: credentials).
|
|
81
|
+
local_tag_for_latest_version&.fetch(:tag)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def latest_version_for_git_dependency
|
|
85
|
+
latest_release = latest_release_tag_version
|
|
86
|
+
|
|
87
|
+
# If there's been a release that includes the current pinned ref or
|
|
88
|
+
# that the current branch is behind, we switch to that release.
|
|
89
|
+
return latest_release if branch_or_ref_in_release?(latest_release)
|
|
90
|
+
|
|
91
|
+
# Otherwise, if the gem isn't pinned, the latest version is just the
|
|
92
|
+
# latest commit for the specified branch.
|
|
93
|
+
unless git_commit_checker.pinned?
|
|
94
|
+
return git_commit_checker.head_commit_for_current_branch
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# If the dependency is pinned to a tag that looks like a version
|
|
98
|
+
# then we want to update that tag.
|
|
99
|
+
if git_commit_checker.pinned_ref_looks_like_version?
|
|
100
|
+
latest_tag = git_commit_checker.local_tag_for_latest_version
|
|
101
|
+
return version_from_tag(latest_tag)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# If the dependency is pinned to a tag that doesn't look like a
|
|
105
|
+
# version then there's nothing we can do.
|
|
106
|
+
nil
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def git_source(path)
|
|
110
|
+
Dependabot::Dep::PathConverter.git_url_for_path(path)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def version_from_tag(tag)
|
|
114
|
+
# To compare with the current version we either use the commit SHA
|
|
115
|
+
# (if that's what the parser picked up) of the tag name.
|
|
116
|
+
if dependency.version&.match?(/^[0-9a-f]{40}$/)
|
|
117
|
+
return tag&.fetch(:commit_sha)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
tag&.fetch(:tag)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def branch_or_ref_in_release?(release)
|
|
124
|
+
return false unless release
|
|
125
|
+
|
|
126
|
+
git_commit_checker.branch_or_ref_in_release?(release)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def git_dependency?
|
|
130
|
+
git_commit_checker.git_dependency?
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def git_commit_checker
|
|
134
|
+
@git_commit_checker ||=
|
|
135
|
+
GitCommitChecker.new(
|
|
136
|
+
dependency: dependency,
|
|
137
|
+
credentials: credentials,
|
|
138
|
+
ignored_versions: ignored_versions
|
|
139
|
+
)
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def parsed_file(file)
|
|
143
|
+
@parsed_file ||= {}
|
|
144
|
+
@parsed_file[file.name] ||= TomlRB.parse(file.content)
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def version_class
|
|
148
|
+
Utils.version_class_for_package_manager(dependency.package_manager)
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def manifest
|
|
152
|
+
@manifest ||= dependency_files.find { |f| f.name == "Gopkg.toml" }
|
|
153
|
+
raise "No Gopkg.lock!" unless @manifest
|
|
154
|
+
|
|
155
|
+
@manifest
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def lockfile
|
|
159
|
+
@lockfile = dependency_files.find { |f| f.name == "Gopkg.lock" }
|
|
160
|
+
raise "No Gopkg.lock!" unless @lockfile
|
|
161
|
+
|
|
162
|
+
@lockfile
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "dependabot/dep/update_checker"
|
|
4
|
+
require "dependabot/dep/requirement"
|
|
5
|
+
require "dependabot/dep/version"
|
|
6
|
+
|
|
7
|
+
module Dependabot
|
|
8
|
+
module Dep
|
|
9
|
+
class UpdateChecker
|
|
10
|
+
class RequirementsUpdater
|
|
11
|
+
class UnfixableRequirement < StandardError; end
|
|
12
|
+
|
|
13
|
+
VERSION_REGEX = /[0-9]+(?:\.[A-Za-z0-9\-*]+)*/.freeze
|
|
14
|
+
ALLOWED_UPDATE_STRATEGIES = %i(widen_ranges bump_versions).freeze
|
|
15
|
+
|
|
16
|
+
def initialize(requirements:, updated_source:, update_strategy:,
|
|
17
|
+
latest_version:, latest_resolvable_version:)
|
|
18
|
+
@requirements = requirements
|
|
19
|
+
@updated_source = updated_source
|
|
20
|
+
@update_strategy = update_strategy
|
|
21
|
+
|
|
22
|
+
check_update_strategy
|
|
23
|
+
|
|
24
|
+
if latest_version && version_class.correct?(latest_version)
|
|
25
|
+
@latest_version = version_class.new(latest_version)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
return unless latest_resolvable_version
|
|
29
|
+
return unless version_class.correct?(latest_resolvable_version)
|
|
30
|
+
|
|
31
|
+
@latest_resolvable_version =
|
|
32
|
+
version_class.new(latest_resolvable_version)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def updated_requirements
|
|
36
|
+
requirements.map do |req|
|
|
37
|
+
req = req.merge(source: updated_source)
|
|
38
|
+
next req unless latest_resolvable_version
|
|
39
|
+
next initial_req_after_source_change(req) unless req[:requirement]
|
|
40
|
+
|
|
41
|
+
case update_strategy
|
|
42
|
+
when :widen_ranges then widen_requirement(req)
|
|
43
|
+
when :bump_versions then update_version(req)
|
|
44
|
+
else raise "Unexpected update strategy: #{update_strategy}"
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
attr_reader :requirements, :updated_source, :update_strategy,
|
|
52
|
+
:latest_version, :latest_resolvable_version
|
|
53
|
+
|
|
54
|
+
def check_update_strategy
|
|
55
|
+
return if ALLOWED_UPDATE_STRATEGIES.include?(update_strategy)
|
|
56
|
+
|
|
57
|
+
raise "Unknown update strategy: #{update_strategy}"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def updating_from_git_to_version?
|
|
61
|
+
return false unless updated_source&.fetch(:type) == "default"
|
|
62
|
+
|
|
63
|
+
original_source = requirements.map { |r| r[:source] }.compact.first
|
|
64
|
+
original_source&.fetch(:type) == "git"
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def initial_req_after_source_change(req)
|
|
68
|
+
return req unless updating_from_git_to_version?
|
|
69
|
+
return req unless req.fetch(:requirement).nil?
|
|
70
|
+
|
|
71
|
+
new_req =
|
|
72
|
+
if req.fetch(:file) == "go.mod"
|
|
73
|
+
"v#{latest_resolvable_version.to_s.gsub(/^v/, '')}"
|
|
74
|
+
else
|
|
75
|
+
"^#{latest_resolvable_version}"
|
|
76
|
+
end
|
|
77
|
+
req.merge(requirement: new_req)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def widen_requirement(req)
|
|
81
|
+
current_requirement = req[:requirement]
|
|
82
|
+
version = latest_resolvable_version
|
|
83
|
+
|
|
84
|
+
ruby_reqs = ruby_requirements(current_requirement)
|
|
85
|
+
return req if ruby_reqs.any? { |r| r.satisfied_by?(version) }
|
|
86
|
+
|
|
87
|
+
reqs = current_requirement.strip.split(",").map(&:strip)
|
|
88
|
+
|
|
89
|
+
updated_requirement =
|
|
90
|
+
if current_requirement.include?("||")
|
|
91
|
+
# Further widen the range by adding another OR condition
|
|
92
|
+
current_requirement + " || ^#{version}"
|
|
93
|
+
elsif reqs.any? { |r| r.match?(/(<|-\s)/) }
|
|
94
|
+
# Further widen the range by updating the upper bound
|
|
95
|
+
update_range_requirement(current_requirement)
|
|
96
|
+
else
|
|
97
|
+
# Convert existing requirement to a range
|
|
98
|
+
create_new_range_requirement(reqs)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
req.merge(requirement: updated_requirement)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def update_version(req)
|
|
105
|
+
current_requirement = req[:requirement]
|
|
106
|
+
version = latest_resolvable_version
|
|
107
|
+
|
|
108
|
+
ruby_reqs = ruby_requirements(current_requirement)
|
|
109
|
+
reqs = current_requirement.strip.split(",").map(&:strip)
|
|
110
|
+
|
|
111
|
+
if ruby_reqs.any? { |r| r.satisfied_by?(version) } &&
|
|
112
|
+
current_requirement.match?(/(<|-\s|\|\|)/)
|
|
113
|
+
return req
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
updated_requirement =
|
|
117
|
+
if current_requirement.include?("||")
|
|
118
|
+
# Further widen the range by adding another OR condition
|
|
119
|
+
current_requirement + " || ^#{version}"
|
|
120
|
+
elsif reqs.any? { |r| r.match?(/(<|-\s)/) }
|
|
121
|
+
# Further widen the range by updating the upper bound
|
|
122
|
+
update_range_requirement(current_requirement)
|
|
123
|
+
else
|
|
124
|
+
update_version_requirement(reqs)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
req.merge(requirement: updated_requirement)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def ruby_requirements(requirement_string)
|
|
131
|
+
requirement_class.requirements_array(requirement_string)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def update_range_requirement(req_string)
|
|
135
|
+
range_requirement = req_string.split(",").
|
|
136
|
+
find { |r| r.match?(/<|(\s+-\s+)/) }
|
|
137
|
+
|
|
138
|
+
versions = range_requirement.scan(VERSION_REGEX)
|
|
139
|
+
upper_bound = versions.map { |v| version_class.new(v) }.max
|
|
140
|
+
new_upper_bound = update_greatest_version(
|
|
141
|
+
upper_bound,
|
|
142
|
+
latest_resolvable_version
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
req_string.sub(
|
|
146
|
+
upper_bound.to_s,
|
|
147
|
+
new_upper_bound.to_s
|
|
148
|
+
)
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def create_new_range_requirement(string_reqs)
|
|
152
|
+
version = latest_resolvable_version
|
|
153
|
+
|
|
154
|
+
lower_bound =
|
|
155
|
+
string_reqs.
|
|
156
|
+
map { |req| requirement_class.new(req) }.
|
|
157
|
+
flat_map { |req| req.requirements.map(&:last) }.
|
|
158
|
+
min.to_s
|
|
159
|
+
|
|
160
|
+
upper_bound =
|
|
161
|
+
if string_reqs.first.start_with?("~") &&
|
|
162
|
+
version.to_s.split(".").count > 1
|
|
163
|
+
create_upper_bound_for_tilda_req(string_reqs.first)
|
|
164
|
+
else
|
|
165
|
+
upper_bound_parts = [version.to_s.split(".").first.to_i + 1]
|
|
166
|
+
upper_bound_parts.
|
|
167
|
+
fill("0", 1..(lower_bound.split(".").count - 1)).
|
|
168
|
+
join(".")
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
">= #{lower_bound}, < #{upper_bound}"
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def update_version_requirement(string_reqs)
|
|
175
|
+
version = latest_resolvable_version.to_s.gsub(/^v/, "")
|
|
176
|
+
current_req = string_reqs.first
|
|
177
|
+
|
|
178
|
+
current_req.gsub(VERSION_REGEX, version)
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def create_upper_bound_for_tilda_req(string_req)
|
|
182
|
+
tilda_version = requirement_class.new(string_req).
|
|
183
|
+
requirements.map(&:last).
|
|
184
|
+
min.to_s
|
|
185
|
+
|
|
186
|
+
upper_bound_parts = latest_resolvable_version.to_s.split(".")
|
|
187
|
+
upper_bound_parts.slice(0, tilda_version.to_s.split(".").count)
|
|
188
|
+
upper_bound_parts[-1] = "0"
|
|
189
|
+
upper_bound_parts[-2] = (upper_bound_parts[-2].to_i + 1).to_s
|
|
190
|
+
|
|
191
|
+
upper_bound_parts.join(".")
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def update_greatest_version(old_version, version_to_be_permitted)
|
|
195
|
+
version = version_class.new(old_version)
|
|
196
|
+
version = version.release if version.prerelease?
|
|
197
|
+
|
|
198
|
+
index_to_update =
|
|
199
|
+
version.segments.map.with_index { |seg, i| seg.zero? ? 0 : i }.max
|
|
200
|
+
|
|
201
|
+
version.segments.map.with_index do |_, index|
|
|
202
|
+
if index < index_to_update
|
|
203
|
+
version_to_be_permitted.segments[index]
|
|
204
|
+
elsif index == index_to_update
|
|
205
|
+
version_to_be_permitted.segments[index] + 1
|
|
206
|
+
else 0
|
|
207
|
+
end
|
|
208
|
+
end.join(".")
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def version_class
|
|
212
|
+
Dep::Version
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
def requirement_class
|
|
216
|
+
Dep::Requirement
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
end
|