dependabot-composer 0.311.0 → 0.312.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/lib/dependabot/composer/helpers.rb +1 -1
- data/lib/dependabot/composer/update_checker/latest_version_finder.rb +66 -14
- data/lib/dependabot/composer/update_checker/requirements_updater.rb +64 -30
- data/lib/dependabot/composer/update_checker/version_resolver.rb +146 -47
- data/lib/dependabot/composer/update_checker.rb +10 -10
- metadata +23 -23
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8c49eb03ec5cdf799b6e907a757ba5e332dfcea06312b82cb21d38e94da364f6
|
4
|
+
data.tar.gz: c49110cee8257749ed2d7f03c2f868d088d4219e9cd42cd39835aa8c80b3c48a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d792223dd012bd103ed61ddb50f4dcdd5d0ba86657939c8cdb70f52655e74e9b59535e983a8929bb533fe1ce9bffd00977fa66deb2ecc467fadb472d23cb5d53
|
7
|
+
data.tar.gz: 8618aa8c287c4eee25fedf9a551d5c682fc92464e0e5d3fd7a0018ed5752fdcbe96fe9512032fe86aa32c268c1abbe60dcaceaba2e6ccdc707b228d0e9c26e7f
|
@@ -174,7 +174,7 @@ module Dependabot
|
|
174
174
|
|
175
175
|
sig { params(dependency_url: String).returns(String) }
|
176
176
|
def self.clean_dependency_url(dependency_url)
|
177
|
-
return dependency_url unless URI::
|
177
|
+
return dependency_url unless URI::RFC2396_PARSER.regexp[:ABS_URI].match?(dependency_url)
|
178
178
|
|
179
179
|
url = URI.parse(dependency_url)
|
180
180
|
url.user = nil
|
@@ -1,8 +1,9 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "excon"
|
5
5
|
require "json"
|
6
|
+
require "sorbet-runtime"
|
6
7
|
|
7
8
|
require "dependabot/composer/update_checker"
|
8
9
|
require "dependabot/update_checkers/version_filters"
|
@@ -13,9 +14,20 @@ module Dependabot
|
|
13
14
|
module Composer
|
14
15
|
class UpdateChecker
|
15
16
|
class LatestVersionFinder
|
16
|
-
|
17
|
-
|
18
|
-
|
17
|
+
extend T::Sig
|
18
|
+
|
19
|
+
sig do
|
20
|
+
params(
|
21
|
+
dependency: Dependabot::Dependency,
|
22
|
+
dependency_files: T::Array[Dependabot::DependencyFile],
|
23
|
+
credentials: T::Array[Dependabot::Credential],
|
24
|
+
ignored_versions: T::Array[String],
|
25
|
+
security_advisories: T::Array[Dependabot::SecurityAdvisory],
|
26
|
+
raise_on_ignored: T::Boolean
|
27
|
+
).void
|
28
|
+
end
|
29
|
+
def initialize(dependency:, dependency_files:, credentials:, ignored_versions:, security_advisories:,
|
30
|
+
raise_on_ignored: false)
|
19
31
|
@dependency = dependency
|
20
32
|
@dependency_files = dependency_files
|
21
33
|
@credentials = credentials
|
@@ -24,22 +36,40 @@ module Dependabot
|
|
24
36
|
@security_advisories = security_advisories
|
25
37
|
end
|
26
38
|
|
39
|
+
sig { returns(T.nilable(Dependabot::Version)) }
|
27
40
|
def latest_version
|
28
|
-
@latest_version ||=
|
41
|
+
@latest_version ||= T.let(
|
42
|
+
fetch_latest_version,
|
43
|
+
T.nilable(Dependabot::Version)
|
44
|
+
)
|
29
45
|
end
|
30
46
|
|
47
|
+
sig { returns(T.nilable(Dependabot::Version)) }
|
31
48
|
def lowest_security_fix_version
|
32
|
-
@lowest_security_fix_version ||=
|
49
|
+
@lowest_security_fix_version ||= T.let(
|
50
|
+
fetch_lowest_security_fix_version,
|
51
|
+
T.nilable(Dependabot::Version)
|
52
|
+
)
|
33
53
|
end
|
34
54
|
|
35
55
|
private
|
36
56
|
|
57
|
+
sig { returns(Dependabot::Dependency) }
|
37
58
|
attr_reader :dependency
|
59
|
+
|
60
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
38
61
|
attr_reader :dependency_files
|
62
|
+
|
63
|
+
sig { returns(T::Array[Dependabot::Credential]) }
|
39
64
|
attr_reader :credentials
|
65
|
+
|
66
|
+
sig { returns(T::Array[String]) }
|
40
67
|
attr_reader :ignored_versions
|
68
|
+
|
69
|
+
sig { returns(T::Array[Dependabot::SecurityAdvisory]) }
|
41
70
|
attr_reader :security_advisories
|
42
71
|
|
72
|
+
sig { returns(T.nilable(Dependabot::Version)) }
|
43
73
|
def fetch_latest_version
|
44
74
|
versions = available_versions
|
45
75
|
versions = filter_prerelease_versions(versions)
|
@@ -47,6 +77,7 @@ module Dependabot
|
|
47
77
|
versions.max
|
48
78
|
end
|
49
79
|
|
80
|
+
sig { returns(T.nilable(Dependabot::Version)) }
|
50
81
|
def fetch_lowest_security_fix_version
|
51
82
|
versions = available_versions
|
52
83
|
versions = filter_prerelease_versions(versions)
|
@@ -58,12 +89,14 @@ module Dependabot
|
|
58
89
|
versions.min
|
59
90
|
end
|
60
91
|
|
92
|
+
sig { params(versions_array: T::Array[Dependabot::Version]).returns(T::Array[Dependabot::Version]) }
|
61
93
|
def filter_prerelease_versions(versions_array)
|
62
94
|
return versions_array if wants_prerelease?
|
63
95
|
|
64
96
|
versions_array.reject(&:prerelease?)
|
65
97
|
end
|
66
98
|
|
99
|
+
sig { params(versions_array: T::Array[Dependabot::Version]).returns(T::Array[Dependabot::Version]) }
|
67
100
|
def filter_ignored_versions(versions_array)
|
68
101
|
filtered =
|
69
102
|
versions_array
|
@@ -76,6 +109,7 @@ module Dependabot
|
|
76
109
|
filtered
|
77
110
|
end
|
78
111
|
|
112
|
+
sig { params(versions_array: T::Array[Dependabot::Version]).returns(T::Array[Dependabot::Version]) }
|
79
113
|
def filter_lower_versions(versions_array)
|
80
114
|
return versions_array unless dependency.numeric_version
|
81
115
|
|
@@ -83,6 +117,7 @@ module Dependabot
|
|
83
117
|
.select { |version| version > dependency.numeric_version }
|
84
118
|
end
|
85
119
|
|
120
|
+
sig { returns(T::Boolean) }
|
86
121
|
def wants_prerelease?
|
87
122
|
current_version = dependency.numeric_version
|
88
123
|
return true if current_version&.prerelease?
|
@@ -92,17 +127,19 @@ module Dependabot
|
|
92
127
|
end
|
93
128
|
end
|
94
129
|
|
130
|
+
sig { returns(T::Array[Dependabot::Version]) }
|
95
131
|
def available_versions
|
96
132
|
registry_version_details
|
97
133
|
.select { |version| version_class.correct?(version.gsub(/^v/, "")) }
|
98
134
|
.map { |version| version_class.new(version.gsub(/^v/, "")) }
|
99
135
|
end
|
100
136
|
|
137
|
+
sig { returns(T::Array[String]) }
|
101
138
|
def registry_version_details
|
102
139
|
return @registry_version_details unless @registry_version_details.nil?
|
103
140
|
|
104
141
|
repositories =
|
105
|
-
JSON.parse(composer_file.content)
|
142
|
+
JSON.parse(T.must(composer_file.content))
|
106
143
|
.fetch("repositories", [])
|
107
144
|
.select { |r| r.is_a?(Hash) }
|
108
145
|
|
@@ -115,16 +152,19 @@ module Dependabot
|
|
115
152
|
urls << "https://repo.packagist.org/p2/#{dependency.name.downcase}.json"
|
116
153
|
end
|
117
154
|
|
118
|
-
@registry_version_details
|
155
|
+
@registry_version_details ||= T.let([], T.nilable(T::Array[String]))
|
119
156
|
urls.each do |url|
|
120
157
|
@registry_version_details += fetch_registry_versions_from_url(url)
|
121
158
|
end
|
122
159
|
@registry_version_details.uniq
|
123
160
|
end
|
124
161
|
|
162
|
+
sig { params(url: String).returns(T::Array[String]) }
|
125
163
|
def fetch_registry_versions_from_url(url)
|
126
164
|
url_host = URI(url).host
|
127
|
-
cred = registry_credentials.find
|
165
|
+
cred = registry_credentials.find do |c|
|
166
|
+
url_host == c["registry"] || url_host == URI(T.must(c["registry"])).host
|
167
|
+
end
|
128
168
|
|
129
169
|
response = Dependabot::RegistryClient.get(
|
130
170
|
url: url,
|
@@ -139,6 +179,7 @@ module Dependabot
|
|
139
179
|
[]
|
140
180
|
end
|
141
181
|
|
182
|
+
sig { params(response: T.untyped, url: String).returns(T::Array[String]) }
|
142
183
|
def parse_registry_response(response, url)
|
143
184
|
return [] unless response.status == 200
|
144
185
|
|
@@ -154,6 +195,7 @@ module Dependabot
|
|
154
195
|
raise DependencyFileNotResolvable, msg
|
155
196
|
end
|
156
197
|
|
198
|
+
sig { params(listing: T::Hash[String, T.untyped]).returns(T::Array[String]) }
|
157
199
|
def extract_versions(listing)
|
158
200
|
# Packagist's Metadata API format:
|
159
201
|
# v1: "packages": {<package name>: {<version_number>: {hash of metadata for a particular release version}}}
|
@@ -174,26 +216,32 @@ module Dependabot
|
|
174
216
|
end
|
175
217
|
end
|
176
218
|
|
219
|
+
sig { returns(T::Array[Dependabot::Credential]) }
|
177
220
|
def registry_credentials
|
178
221
|
credentials.select { |cred| cred["type"] == PackageManager::REPOSITORY_KEY } +
|
179
222
|
auth_json_credentials
|
180
223
|
end
|
181
224
|
|
225
|
+
sig { returns(T::Array[Dependabot::Credential]) }
|
182
226
|
def auth_json_credentials
|
183
|
-
|
227
|
+
json = auth_json
|
228
|
+
return [] unless json
|
184
229
|
|
185
|
-
parsed_auth_json = JSON.parse(
|
230
|
+
parsed_auth_json = JSON.parse(T.must(json.content))
|
186
231
|
parsed_auth_json.fetch("http-basic", {}).map do |reg, details|
|
187
|
-
{
|
232
|
+
Dependabot::Credential.new({
|
188
233
|
"registry" => reg,
|
189
234
|
"username" => details["username"],
|
190
235
|
"password" => details["password"]
|
191
|
-
}
|
236
|
+
})
|
192
237
|
end
|
193
238
|
rescue JSON::ParserError
|
194
|
-
raise Dependabot::DependencyFileNotParseable,
|
239
|
+
raise Dependabot::DependencyFileNotParseable, json.path if json
|
240
|
+
|
241
|
+
raise Dependabot::DependencyFileNotParseable, "Unknown path"
|
195
242
|
end
|
196
243
|
|
244
|
+
sig { returns(Dependabot::DependencyFile) }
|
197
245
|
def composer_file
|
198
246
|
composer_file =
|
199
247
|
dependency_files.find do |f|
|
@@ -204,18 +252,22 @@ module Dependabot
|
|
204
252
|
composer_file
|
205
253
|
end
|
206
254
|
|
255
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
207
256
|
def auth_json
|
208
257
|
dependency_files.find { |f| f.name == PackageManager::AUTH_FILENAME }
|
209
258
|
end
|
210
259
|
|
260
|
+
sig { returns(T::Array[Dependabot::Requirement]) }
|
211
261
|
def ignore_requirements
|
212
262
|
ignored_versions.map { |req| requirement_class.new(req.split(",")) }
|
213
263
|
end
|
214
264
|
|
265
|
+
sig { returns(T.class_of(Dependabot::Version)) }
|
215
266
|
def version_class
|
216
267
|
dependency.version_class
|
217
268
|
end
|
218
269
|
|
270
|
+
sig { returns(T.class_of(Dependabot::Requirement)) }
|
219
271
|
def requirement_class
|
220
272
|
dependency.requirement_class
|
221
273
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
################################################################################
|
@@ -34,6 +34,13 @@ module Dependabot
|
|
34
34
|
T::Array[Dependabot::RequirementsUpdateStrategy]
|
35
35
|
)
|
36
36
|
|
37
|
+
sig do
|
38
|
+
params(
|
39
|
+
requirements: T::Array[T::Hash[Symbol, String]],
|
40
|
+
update_strategy: Dependabot::RequirementsUpdateStrategy,
|
41
|
+
latest_resolvable_version: T.nilable(T.any(String, Composer::Version))
|
42
|
+
).void
|
43
|
+
end
|
37
44
|
def initialize(requirements:, update_strategy:,
|
38
45
|
latest_resolvable_version:)
|
39
46
|
@requirements = requirements
|
@@ -43,10 +50,13 @@ module Dependabot
|
|
43
50
|
|
44
51
|
return unless latest_resolvable_version
|
45
52
|
|
46
|
-
@latest_resolvable_version =
|
47
|
-
version_class.new(latest_resolvable_version)
|
53
|
+
@latest_resolvable_version = T.let(
|
54
|
+
version_class.new(latest_resolvable_version),
|
55
|
+
Dependabot::Version
|
56
|
+
)
|
48
57
|
end
|
49
58
|
|
59
|
+
sig { returns(T::Array[T::Hash[Symbol, String]]) }
|
50
60
|
def updated_requirements
|
51
61
|
return requirements if update_strategy.lockfile_only?
|
52
62
|
return requirements unless latest_resolvable_version
|
@@ -56,10 +66,16 @@ module Dependabot
|
|
56
66
|
|
57
67
|
private
|
58
68
|
|
69
|
+
sig { returns(T::Array[T::Hash[Symbol, String]]) }
|
59
70
|
attr_reader :requirements
|
71
|
+
|
72
|
+
sig { returns(Dependabot::RequirementsUpdateStrategy) }
|
60
73
|
attr_reader :update_strategy
|
74
|
+
|
75
|
+
sig { returns(T.nilable(Dependabot::Version)) }
|
61
76
|
attr_reader :latest_resolvable_version
|
62
77
|
|
78
|
+
sig { void }
|
63
79
|
def check_update_strategy
|
64
80
|
return if ALLOWED_UPDATE_STRATEGIES.include?(update_strategy)
|
65
81
|
|
@@ -67,8 +83,9 @@ module Dependabot
|
|
67
83
|
end
|
68
84
|
|
69
85
|
# rubocop:disable Metrics/PerceivedComplexity
|
86
|
+
sig { params(req: T::Hash[Symbol, String]).returns(T::Hash[Symbol, String]) }
|
70
87
|
def updated_requirement(req)
|
71
|
-
req_string = req[:requirement].strip
|
88
|
+
req_string = T.must(req[:requirement]).strip
|
72
89
|
or_string_reqs = req_string.split(OR_SEPARATOR)
|
73
90
|
or_separator = req_string.match(OR_SEPARATOR)&.to_s || " || "
|
74
91
|
numeric_or_string_reqs = or_string_reqs
|
@@ -90,28 +107,33 @@ module Dependabot
|
|
90
107
|
update_requirement_version(req, or_separator)
|
91
108
|
end
|
92
109
|
|
110
|
+
# Add a T.must for new_req as it's defined in the case statement with multiple options
|
111
|
+
new_req = T.must(new_req)
|
93
112
|
new_req_string =
|
94
113
|
[new_req[:requirement], *branch_or_string_reqs].join(or_separator)
|
95
114
|
new_req.merge(requirement: new_req_string)
|
96
115
|
end
|
97
116
|
# rubocop:enable Metrics/PerceivedComplexity
|
98
117
|
|
118
|
+
sig { params(req: T::Hash[Symbol, String]).returns(T::Hash[Symbol, String]) }
|
99
119
|
def updated_alias(req)
|
100
|
-
req_string = req[:requirement]
|
101
|
-
|
120
|
+
req_string = T.must(req[:requirement])
|
121
|
+
parts = req_string.split(/\sas\s/)
|
122
|
+
real_version = T.must(parts.first).strip
|
102
123
|
|
103
124
|
# If the version we're aliasing isn't a version then we don't know
|
104
125
|
# how to update it, so we just return the existing requirement.
|
105
126
|
return req unless version_class.correct?(real_version)
|
106
127
|
|
107
|
-
new_version_string = latest_resolvable_version.to_s
|
128
|
+
new_version_string = T.must(latest_resolvable_version).to_s
|
108
129
|
new_req = req_string.sub(real_version, new_version_string)
|
109
130
|
req.merge(requirement: new_req)
|
110
131
|
end
|
111
132
|
|
112
133
|
# rubocop:disable Metrics/PerceivedComplexity
|
134
|
+
sig { params(req: T::Hash[Symbol, String], or_separator: String).returns(T::Hash[Symbol, String]) }
|
113
135
|
def widen_requirement(req, or_separator)
|
114
|
-
current_requirement = req[:requirement]
|
136
|
+
current_requirement = T.must(req[:requirement])
|
115
137
|
reqs = current_requirement.strip.split(SEPARATOR).map(&:strip)
|
116
138
|
|
117
139
|
updated_requirement =
|
@@ -131,13 +153,14 @@ module Dependabot
|
|
131
153
|
end
|
132
154
|
# rubocop:enable Metrics/PerceivedComplexity
|
133
155
|
|
156
|
+
sig { params(req: T::Hash[Symbol, String], or_separator: String).returns(T::Hash[Symbol, String]) }
|
134
157
|
def update_requirement_version(req, or_separator)
|
135
|
-
current_requirement = req[:requirement]
|
158
|
+
current_requirement = T.must(req[:requirement])
|
136
159
|
reqs = current_requirement.strip.split(SEPARATOR).map(&:strip)
|
137
160
|
|
138
161
|
updated_requirement =
|
139
162
|
if reqs.count > 1
|
140
|
-
"^#{latest_resolvable_version}"
|
163
|
+
"^#{T.must(latest_resolvable_version)}"
|
141
164
|
elsif reqs.any? { |r| r.match?(/<|(\s+-\s+)/) }
|
142
165
|
update_range_requirement(current_requirement, or_separator)
|
143
166
|
elsif reqs.any? { |r| r.match?(/>[^=]/) }
|
@@ -149,38 +172,42 @@ module Dependabot
|
|
149
172
|
req.merge(requirement: updated_requirement)
|
150
173
|
end
|
151
174
|
|
175
|
+
sig { params(requirement_string: String).returns(T::Boolean) }
|
152
176
|
def req_satisfied_by_latest_resolvable?(requirement_string)
|
153
177
|
ruby_requirements(requirement_string)
|
154
|
-
.any? { |r| r.satisfied_by?(latest_resolvable_version) }
|
178
|
+
.any? { |r| r.satisfied_by?(T.must(latest_resolvable_version)) }
|
155
179
|
end
|
156
180
|
|
181
|
+
sig { params(req_string: String).returns(String) }
|
157
182
|
def update_version_string(req_string)
|
158
183
|
req_string
|
159
184
|
.sub(VERSION_REGEX) do |old_version|
|
160
|
-
next latest_resolvable_version.to_s unless req_string.match?(/[~*\^]/)
|
185
|
+
next T.must(latest_resolvable_version).to_s unless req_string.match?(/[~*\^]/)
|
161
186
|
|
162
187
|
old_parts = old_version.split(".")
|
163
|
-
new_parts = latest_resolvable_version.to_s.split(".")
|
164
|
-
|
188
|
+
new_parts = T.must(latest_resolvable_version).to_s.split(".")
|
189
|
+
.first(old_parts.count)
|
165
190
|
new_parts.map.with_index do |part, i|
|
166
191
|
old_parts[i] == "*" ? "*" : part
|
167
192
|
end.join(".")
|
168
193
|
end
|
169
194
|
end
|
170
195
|
|
196
|
+
sig { params(requirement_string: String).returns(T::Array[Composer::Requirement]) }
|
171
197
|
def ruby_requirements(requirement_string)
|
172
198
|
Composer::Requirement.requirements_array(requirement_string)
|
173
199
|
end
|
174
200
|
|
201
|
+
sig { params(req_string: String, or_separator: String).returns(String) }
|
175
202
|
def update_caret_requirement(req_string, or_separator)
|
176
203
|
caret_requirements =
|
177
204
|
req_string.split(SEPARATOR).select { |r| r.strip.start_with?("^") }
|
178
|
-
version_parts = latest_resolvable_version.segments
|
205
|
+
version_parts = T.must(latest_resolvable_version).segments
|
179
206
|
|
180
207
|
min_existing_precision =
|
181
|
-
caret_requirements.map { |r| r.split(".").count }.min
|
208
|
+
caret_requirements.map { |r| r.split(".").count }.min || 0
|
182
209
|
first_non_zero_index =
|
183
|
-
version_parts.count.times.find { |i| version_parts[i] != 0 }
|
210
|
+
version_parts.count.times.find { |i| version_parts[i] != 0 } || 0
|
184
211
|
|
185
212
|
precision = [min_existing_precision, first_non_zero_index + 1].max
|
186
213
|
version = version_parts.first(precision).map.with_index do |part, i|
|
@@ -190,71 +217,78 @@ module Dependabot
|
|
190
217
|
req_string + "#{or_separator}^#{version}"
|
191
218
|
end
|
192
219
|
|
220
|
+
sig { params(req_string: String, or_separator: String).returns(String) }
|
193
221
|
def update_tilda_requirement(req_string, or_separator)
|
194
222
|
tilda_requirements =
|
195
223
|
req_string.split(SEPARATOR).select { |r| r.strip.start_with?("~") }
|
196
|
-
precision = tilda_requirements.map { |r| r.split(".").count }.min
|
224
|
+
precision = tilda_requirements.map { |r| r.split(".").count }.min || 0
|
197
225
|
|
198
|
-
version_parts = latest_resolvable_version.segments.first(precision)
|
199
|
-
version_parts[-1] = 0
|
226
|
+
version_parts = T.must(latest_resolvable_version).segments.first(precision)
|
227
|
+
version_parts[-1] = 0 if version_parts.any?
|
200
228
|
version = version_parts.join(".")
|
201
229
|
|
202
230
|
req_string + "#{or_separator}~#{version}"
|
203
231
|
end
|
204
232
|
|
233
|
+
sig { params(req_string: String, or_separator: String).returns(String) }
|
205
234
|
def update_wildcard_requirement(req_string, or_separator)
|
206
235
|
wildcard_requirements =
|
207
236
|
req_string.split(SEPARATOR).select { |r| r.include?("*") }
|
208
237
|
precision = wildcard_requirements.map do |r|
|
209
238
|
r.split(".").reject { |s| s == "*" }.count
|
210
|
-
end.min
|
239
|
+
end.min || 0
|
211
240
|
wildcard_count = wildcard_requirements.map do |r|
|
212
241
|
r.split(".").select { |s| s == "*" }.count
|
213
|
-
end.min
|
242
|
+
end.min || 0
|
214
243
|
|
215
|
-
version_parts = latest_resolvable_version.segments.first(precision)
|
244
|
+
version_parts = T.must(latest_resolvable_version).segments.first(precision)
|
216
245
|
version = version_parts.join(".")
|
217
246
|
|
218
247
|
req_string + "#{or_separator}#{version}#{'.*' * wildcard_count}"
|
219
248
|
end
|
220
249
|
|
250
|
+
sig { params(req_string: String, or_separator: String).returns(String) }
|
221
251
|
def update_range_requirement(req_string, or_separator)
|
222
252
|
range_requirements =
|
223
253
|
req_string.split(SEPARATOR).select { |r| r.match?(/<|(\s+-\s+)/) }
|
224
254
|
|
225
255
|
if range_requirements.count == 1
|
226
|
-
range_requirement = range_requirements.first
|
256
|
+
range_requirement = T.must(range_requirements.first)
|
227
257
|
versions = range_requirement.scan(VERSION_REGEX)
|
228
|
-
|
258
|
+
# Convert version strings to Version objects and find the maximum
|
259
|
+
upper_bounds = versions.map { |v| version_class.new(T.cast(v, String)) }
|
260
|
+
upper_bound = T.cast(upper_bounds.max, Dependabot::Version)
|
229
261
|
new_upper_bound = update_greatest_version(
|
230
262
|
upper_bound,
|
231
|
-
latest_resolvable_version
|
263
|
+
T.must(latest_resolvable_version)
|
232
264
|
)
|
233
265
|
|
234
|
-
req_string.sub(upper_bound.to_s, new_upper_bound
|
266
|
+
req_string.sub(upper_bound.to_s, new_upper_bound)
|
235
267
|
else
|
236
|
-
req_string + "#{or_separator}^#{latest_resolvable_version}"
|
268
|
+
req_string + "#{or_separator}^#{T.must(latest_resolvable_version)}"
|
237
269
|
end
|
238
270
|
end
|
239
271
|
|
272
|
+
sig { params(old_version: Dependabot::Version, version_to_be_permitted: Dependabot::Version).returns(String) }
|
240
273
|
def update_greatest_version(old_version, version_to_be_permitted)
|
241
274
|
version = version_class.new(old_version)
|
242
275
|
version = version.release if version.prerelease?
|
243
276
|
|
244
277
|
index_to_update =
|
245
|
-
version.segments.map.with_index { |seg, i| seg.zero? ? 0 : i }.max
|
278
|
+
version.segments.map.with_index { |seg, i| seg.to_i.zero? ? 0 : i }.max || 0
|
246
279
|
|
247
280
|
version.segments.map.with_index do |_, index|
|
248
281
|
if index < index_to_update
|
249
282
|
version_to_be_permitted.segments[index]
|
250
283
|
elsif index == index_to_update
|
251
|
-
version_to_be_permitted.segments[index] + 1
|
284
|
+
version_to_be_permitted.segments[index].to_i + 1
|
252
285
|
else
|
253
286
|
0
|
254
287
|
end
|
255
288
|
end.join(".")
|
256
289
|
end
|
257
290
|
|
291
|
+
sig { returns(T.class_of(Dependabot::Composer::Version)) }
|
258
292
|
def version_class
|
259
293
|
Composer::Version
|
260
294
|
end
|
@@ -1,8 +1,9 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "json"
|
5
5
|
require "uri"
|
6
|
+
require "sorbet-runtime"
|
6
7
|
|
7
8
|
require "dependabot/errors"
|
8
9
|
require "dependabot/shared_helpers"
|
@@ -17,30 +18,49 @@ module Dependabot
|
|
17
18
|
module Composer
|
18
19
|
class UpdateChecker
|
19
20
|
class VersionResolver # rubocop:disable Metrics/ClassLength
|
21
|
+
extend T::Sig
|
22
|
+
|
20
23
|
class MissingExtensions < StandardError
|
24
|
+
extend T::Sig
|
25
|
+
|
26
|
+
sig { returns(T::Array[T::Hash[Symbol, T.untyped]]) }
|
21
27
|
attr_reader :extensions
|
22
28
|
|
29
|
+
sig { params(extensions: T::Array[T::Hash[Symbol, T.untyped]]).void }
|
23
30
|
def initialize(extensions)
|
24
|
-
@extensions = extensions
|
31
|
+
@extensions = T.let(extensions, T::Array[T::Hash[Symbol, T.untyped]])
|
25
32
|
super
|
26
33
|
end
|
27
34
|
end
|
28
35
|
|
29
|
-
MISSING_EXPLICIT_PLATFORM_REQ_REGEX =
|
36
|
+
MISSING_EXPLICIT_PLATFORM_REQ_REGEX = T.let(
|
30
37
|
%r{
|
31
38
|
(?<=PHP\sextension\s)ext\-[^\s\/]+\s.*?\s(?=is|but)|
|
32
39
|
(?<=requires\s)php(?:\-[^\s\/]+)?\s.*?\s(?=but)
|
33
|
-
}x
|
34
|
-
|
40
|
+
}x,
|
41
|
+
Regexp
|
42
|
+
)
|
43
|
+
MISSING_IMPLICIT_PLATFORM_REQ_REGEX = T.let(
|
35
44
|
%r{
|
36
45
|
(?<!with|for|by)\sext\-[^\s\/]+\s.*?\s(?=->)|
|
37
46
|
(?<=require\s)php(?:\-[^\s\/]+)?\s.*?\s(?=->) # composer v2
|
38
|
-
}x
|
39
|
-
|
47
|
+
}x,
|
48
|
+
Regexp
|
49
|
+
)
|
50
|
+
VERSION_REGEX = T.let(/[0-9]+(?:\.[A-Za-z0-9\-_]+)*/, Regexp)
|
40
51
|
|
41
52
|
# Example Timeout error from Composer 2.7.7: "curl error 28 while downloading https://example.com:81/packages.json: Failed to connect to example.com port 81 after 9853 ms: Connection timed out" # rubocop:disable Layout/LineLength
|
42
|
-
SOURCE_TIMED_OUT_REGEX = %r{curl error 28 while downloading (?<url>https?://.+/packages\.json): }
|
53
|
+
SOURCE_TIMED_OUT_REGEX = T.let(%r{curl error 28 while downloading (?<url>https?://.+/packages\.json): }, Regexp)
|
43
54
|
|
55
|
+
sig do
|
56
|
+
params(
|
57
|
+
credentials: T::Array[Dependabot::Credential],
|
58
|
+
dependency: Dependabot::Dependency,
|
59
|
+
dependency_files: T::Array[Dependabot::DependencyFile],
|
60
|
+
requirements_to_unlock: Symbol,
|
61
|
+
latest_allowable_version: T.nilable(Gem::Version)
|
62
|
+
).void
|
63
|
+
end
|
44
64
|
def initialize(credentials:, dependency:, dependency_files:,
|
45
65
|
requirements_to_unlock:, latest_allowable_version:)
|
46
66
|
@credentials = credentials
|
@@ -48,24 +68,43 @@ module Dependabot
|
|
48
68
|
@dependency_files = dependency_files
|
49
69
|
@requirements_to_unlock = requirements_to_unlock
|
50
70
|
@latest_allowable_version = latest_allowable_version
|
51
|
-
@composer_platform_extensions = initial_platform
|
52
|
-
@error_handler = ComposerErrorHandler.new
|
71
|
+
@composer_platform_extensions = T.let(initial_platform, T::Hash[String, T::Array[String]])
|
72
|
+
@error_handler = T.let(ComposerErrorHandler.new, ComposerErrorHandler)
|
53
73
|
end
|
54
74
|
|
75
|
+
sig { returns(T.nilable(Dependabot::Version)) }
|
55
76
|
def latest_resolvable_version
|
56
|
-
@latest_resolvable_version ||=
|
77
|
+
@latest_resolvable_version ||= T.let(
|
78
|
+
fetch_latest_resolvable_version,
|
79
|
+
T.nilable(Dependabot::Version)
|
80
|
+
)
|
57
81
|
end
|
58
82
|
|
59
83
|
private
|
60
84
|
|
85
|
+
# Initialize instance variables with T.let for strict typing
|
86
|
+
sig { returns(T::Array[Dependabot::Credential]) }
|
61
87
|
attr_reader :credentials
|
88
|
+
|
89
|
+
sig { returns(Dependabot::Dependency) }
|
62
90
|
attr_reader :dependency
|
91
|
+
|
92
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
63
93
|
attr_reader :dependency_files
|
94
|
+
|
95
|
+
sig { returns(Symbol) }
|
64
96
|
attr_reader :requirements_to_unlock
|
97
|
+
|
98
|
+
sig { returns(T.nilable(Gem::Version)) }
|
65
99
|
attr_reader :latest_allowable_version
|
100
|
+
|
101
|
+
sig { returns(T::Hash[String, T::Array[String]]) }
|
66
102
|
attr_reader :composer_platform_extensions
|
103
|
+
|
104
|
+
sig { returns(ComposerErrorHandler) }
|
67
105
|
attr_reader :error_handler
|
68
106
|
|
107
|
+
sig { returns(T.nilable(Dependabot::Version)) }
|
69
108
|
def fetch_latest_resolvable_version
|
70
109
|
version = fetch_latest_resolvable_version_string
|
71
110
|
return if version.nil?
|
@@ -80,8 +119,9 @@ module Dependabot
|
|
80
119
|
retry
|
81
120
|
end
|
82
121
|
|
122
|
+
sig { returns(T.nilable(String)) }
|
83
123
|
def fetch_latest_resolvable_version_string
|
84
|
-
base_directory = dependency_files.first.directory
|
124
|
+
base_directory = T.must(dependency_files.first).directory
|
85
125
|
SharedHelpers.in_a_temporary_directory(base_directory) do
|
86
126
|
write_temporary_dependency_files
|
87
127
|
run_update_checker
|
@@ -93,6 +133,7 @@ module Dependabot
|
|
93
133
|
handle_composer_errors(e)
|
94
134
|
end
|
95
135
|
|
136
|
+
sig { params(unlock_requirement: T::Boolean).void }
|
96
137
|
def write_temporary_dependency_files(unlock_requirement: true)
|
97
138
|
write_dependency_file(unlock_requirement: unlock_requirement)
|
98
139
|
write_path_dependency_files
|
@@ -101,6 +142,7 @@ module Dependabot
|
|
101
142
|
write_auth_file
|
102
143
|
end
|
103
144
|
|
145
|
+
sig { void }
|
104
146
|
def write_zipped_path_dependency_files
|
105
147
|
zipped_path_dependency_files.each do |file|
|
106
148
|
FileUtils.mkdir_p(Pathname.new(file.name).dirname)
|
@@ -108,6 +150,7 @@ module Dependabot
|
|
108
150
|
end
|
109
151
|
end
|
110
152
|
|
153
|
+
sig { params(unlock_requirement: T::Boolean).void }
|
111
154
|
def write_dependency_file(unlock_requirement:)
|
112
155
|
File.write(
|
113
156
|
PackageManager::MANIFEST_FILENAME,
|
@@ -117,6 +160,7 @@ module Dependabot
|
|
117
160
|
)
|
118
161
|
end
|
119
162
|
|
163
|
+
sig { void }
|
120
164
|
def write_path_dependency_files
|
121
165
|
path_dependency_files.each do |file|
|
122
166
|
FileUtils.mkdir_p(Pathname.new(file.name).dirname)
|
@@ -124,14 +168,17 @@ module Dependabot
|
|
124
168
|
end
|
125
169
|
end
|
126
170
|
|
171
|
+
sig { void }
|
127
172
|
def write_lockfile
|
128
|
-
File.write(PackageManager::LOCKFILE_FILENAME, lockfile.content) if lockfile
|
173
|
+
File.write(PackageManager::LOCKFILE_FILENAME, T.must(lockfile).content) if lockfile
|
129
174
|
end
|
130
175
|
|
176
|
+
sig { void }
|
131
177
|
def write_auth_file
|
132
|
-
File.write(PackageManager::AUTH_FILENAME, auth_json.content) if auth_json
|
178
|
+
File.write(PackageManager::AUTH_FILENAME, T.must(auth_json).content) if auth_json
|
133
179
|
end
|
134
180
|
|
181
|
+
sig { params(error: SharedHelpers::HelperSubprocessFailed).returns(T::Boolean) }
|
135
182
|
def transitory_failure?(error)
|
136
183
|
return true if error.message.include?("404 Not Found")
|
137
184
|
return true if error.message.include?("timed out")
|
@@ -140,6 +187,7 @@ module Dependabot
|
|
140
187
|
error.message.include?("Content-Length mismatch")
|
141
188
|
end
|
142
189
|
|
190
|
+
sig { returns(String) }
|
143
191
|
def run_update_checker
|
144
192
|
SharedHelpers.with_git_configured(credentials: credentials) do
|
145
193
|
SharedHelpers.run_helper_subprocess(
|
@@ -156,14 +204,16 @@ module Dependabot
|
|
156
204
|
end
|
157
205
|
end
|
158
206
|
|
207
|
+
sig { params(unlock_requirement: T::Boolean).returns(String) }
|
159
208
|
def prepared_composer_json_content(unlock_requirement: true)
|
160
|
-
content = composer_file.content
|
209
|
+
content = T.must(T.must(composer_file).content)
|
161
210
|
content = unlock_dep_being_updated(content) if unlock_requirement
|
162
211
|
content = lock_git_dependencies(content) if lockfile
|
163
212
|
content = add_temporary_platform_extensions(content)
|
164
213
|
content
|
165
214
|
end
|
166
215
|
|
216
|
+
sig { params(content: String).returns(String) }
|
167
217
|
def unlock_dep_being_updated(content)
|
168
218
|
content.gsub(
|
169
219
|
/"#{Regexp.escape(dependency.name)}"\s*:\s*".*"/,
|
@@ -171,6 +221,7 @@ module Dependabot
|
|
171
221
|
)
|
172
222
|
end
|
173
223
|
|
224
|
+
sig { params(content: String).returns(String) }
|
174
225
|
def add_temporary_platform_extensions(content)
|
175
226
|
json = JSON.parse(content)
|
176
227
|
|
@@ -186,6 +237,7 @@ module Dependabot
|
|
186
237
|
JSON.dump(json)
|
187
238
|
end
|
188
239
|
|
240
|
+
sig { params(content: String).returns(String) }
|
189
241
|
def lock_git_dependencies(content)
|
190
242
|
json = JSON.parse(content)
|
191
243
|
|
@@ -197,7 +249,7 @@ module Dependabot
|
|
197
249
|
next if req.include?("#")
|
198
250
|
|
199
251
|
commit_sha = parsed_lockfile
|
200
|
-
.fetch(keys[:lockfile], [])
|
252
|
+
.fetch(T.must(keys[:lockfile]), [])
|
201
253
|
.find { |d| d["name"] == name }
|
202
254
|
&.dig("source", "reference")
|
203
255
|
updated_req_parts = req.split
|
@@ -211,6 +263,7 @@ module Dependabot
|
|
211
263
|
|
212
264
|
# rubocop:disable Metrics/PerceivedComplexity
|
213
265
|
# rubocop:disable Metrics/AbcSize
|
266
|
+
sig { returns(String) }
|
214
267
|
def updated_version_requirement_string
|
215
268
|
lower_bound =
|
216
269
|
if requirements_to_unlock == :none
|
@@ -253,6 +306,7 @@ module Dependabot
|
|
253
306
|
# rubocop:disable Metrics/AbcSize
|
254
307
|
# rubocop:disable Metrics/CyclomaticComplexity
|
255
308
|
# rubocop:disable Metrics/MethodLength
|
309
|
+
sig { params(error: SharedHelpers::HelperSubprocessFailed).returns(T.nilable(NilClass)) }
|
256
310
|
def handle_composer_errors(error)
|
257
311
|
# Special case for Laravel Nova, which will fall back to attempting
|
258
312
|
# to close a private repo if given invalid (or no) credentials
|
@@ -271,7 +325,11 @@ module Dependabot
|
|
271
325
|
missing_extensions =
|
272
326
|
error.message.scan(MISSING_EXPLICIT_PLATFORM_REQ_REGEX)
|
273
327
|
.map do |extension_string|
|
274
|
-
name, requirement = extension_string.
|
328
|
+
name, requirement = if extension_string.is_a?(Array)
|
329
|
+
[extension_string.first.to_s.strip, extension_string.last.to_s]
|
330
|
+
else
|
331
|
+
extension_string.to_s.strip.split(" ", 2)
|
332
|
+
end
|
275
333
|
{ name: name, requirement: requirement }
|
276
334
|
end
|
277
335
|
raise MissingExtensions, missing_extensions
|
@@ -282,7 +340,7 @@ module Dependabot
|
|
282
340
|
missing_extensions =
|
283
341
|
error.message.scan(MISSING_IMPLICIT_PLATFORM_REQ_REGEX)
|
284
342
|
.map do |extension_string|
|
285
|
-
name, requirement = extension_string.strip.split(" ", 2)
|
343
|
+
name, requirement = T.cast(extension_string, String).strip.split(" ", 2)
|
286
344
|
{ name: name, requirement: requirement }
|
287
345
|
end
|
288
346
|
|
@@ -291,7 +349,7 @@ module Dependabot
|
|
291
349
|
version_for_reqs(existing_reqs + [hash[:requirement]])
|
292
350
|
end
|
293
351
|
|
294
|
-
raise MissingExtensions, [missing_extension]
|
352
|
+
raise MissingExtensions, [missing_extension].compact
|
295
353
|
elsif error.message.include?("cannot require itself") ||
|
296
354
|
error.message.include?('packages.json" file could not be down')
|
297
355
|
raise Dependabot::DependencyFileNotResolvable, error.message
|
@@ -312,14 +370,14 @@ module Dependabot
|
|
312
370
|
# now, we therefore just ignore the dependency and log the error.
|
313
371
|
|
314
372
|
Dependabot.logger.error(error.message)
|
315
|
-
error.backtrace
|
373
|
+
error.backtrace&.each { |line| Dependabot.logger.error(line) }
|
316
374
|
nil
|
317
375
|
elsif error.message.include?("URL required authentication") ||
|
318
376
|
error.message.include?("403 Forbidden")
|
319
|
-
source = error.message.match(%r{https?://(?<source>[^/]+)/})
|
377
|
+
source = error.message.match(%r{https?://(?<source>[^/]+)/})&.named_captures&.fetch("source")
|
320
378
|
raise Dependabot::PrivateSourceAuthenticationFailure, source
|
321
379
|
elsif error.message.match?(SOURCE_TIMED_OUT_REGEX)
|
322
|
-
url = error.message.match(SOURCE_TIMED_OUT_REGEX)
|
380
|
+
url = T.must(error.message.match(SOURCE_TIMED_OUT_REGEX)&.named_captures&.fetch("url"))
|
323
381
|
raise if [
|
324
382
|
"packagist.org",
|
325
383
|
"www.packagist.org"
|
@@ -357,6 +415,7 @@ module Dependabot
|
|
357
415
|
# rubocop:enable Metrics/CyclomaticComplexity
|
358
416
|
# rubocop:enable Metrics/MethodLength
|
359
417
|
|
418
|
+
sig { params(error: SharedHelpers::HelperSubprocessFailed).returns(T::Boolean) }
|
360
419
|
def unresolvable_error?(error)
|
361
420
|
error.message.start_with?("Could not parse version") ||
|
362
421
|
error.message.include?("does not allow connections to http://") ||
|
@@ -364,15 +423,17 @@ module Dependabot
|
|
364
423
|
error.message.start_with?("Invalid version string")
|
365
424
|
end
|
366
425
|
|
426
|
+
sig { returns(T::Boolean) }
|
367
427
|
def library?
|
368
428
|
parsed_composer_file["type"] == "library"
|
369
429
|
end
|
370
430
|
|
431
|
+
sig { params(message: String).returns(T::Boolean) }
|
371
432
|
def implicit_platform_reqs_satisfiable?(message)
|
372
433
|
missing_extensions =
|
373
434
|
message.scan(MISSING_IMPLICIT_PLATFORM_REQ_REGEX)
|
374
435
|
.map do |extension_string|
|
375
|
-
name, requirement = extension_string.strip.split(" ", 2)
|
436
|
+
name, requirement = T.cast(extension_string, String).strip.split(" ", 2)
|
376
437
|
{ name: name, requirement: requirement }
|
377
438
|
end
|
378
439
|
|
@@ -382,8 +443,9 @@ module Dependabot
|
|
382
443
|
end
|
383
444
|
end
|
384
445
|
|
446
|
+
sig { returns(T::Boolean) }
|
385
447
|
def check_original_requirements_resolvable
|
386
|
-
base_directory = dependency_files.first.directory
|
448
|
+
base_directory = T.must(dependency_files.first).directory
|
387
449
|
SharedHelpers.in_a_temporary_directory(base_directory) do
|
388
450
|
write_temporary_dependency_files(unlock_requirement: false)
|
389
451
|
|
@@ -414,6 +476,7 @@ module Dependabot
|
|
414
476
|
raise Dependabot::DependencyFileNotResolvable, e.message
|
415
477
|
end
|
416
478
|
|
479
|
+
sig { params(requirements: T::Array[String]).returns(T.nilable(String)) }
|
417
480
|
def version_for_reqs(requirements)
|
418
481
|
req_arrays =
|
419
482
|
requirements
|
@@ -438,25 +501,32 @@ module Dependabot
|
|
438
501
|
version.to_s
|
439
502
|
end
|
440
503
|
|
504
|
+
sig { params(additional_extensions: T::Array[T::Hash[Symbol, String]]).void }
|
441
505
|
def update_required_extensions(additional_extensions)
|
442
506
|
additional_extensions.each do |ext|
|
443
507
|
composer_platform_extensions[ext.fetch(:name)] ||= []
|
444
|
-
composer_platform_extensions[ext.fetch(:name)] +=
|
445
|
-
[ext.fetch(:requirement)]
|
446
508
|
composer_platform_extensions[ext.fetch(:name)] =
|
447
|
-
composer_platform_extensions[ext.fetch(:name)].
|
509
|
+
T.must(composer_platform_extensions[ext.fetch(:name)]) + [ext.fetch(:requirement)]
|
510
|
+
composer_platform_extensions[ext.fetch(:name)] =
|
511
|
+
T.must(composer_platform_extensions[ext.fetch(:name)]).uniq
|
448
512
|
end
|
449
513
|
end
|
450
514
|
|
515
|
+
sig { returns(String) }
|
451
516
|
def php_helper_path
|
452
517
|
NativeHelpers.composer_helper_path(composer_version: composer_version)
|
453
518
|
end
|
454
519
|
|
520
|
+
sig { returns(String) }
|
455
521
|
def composer_version
|
456
522
|
parsed_lockfile_or_nil = lockfile ? parsed_lockfile : nil
|
457
|
-
@composer_version ||=
|
523
|
+
@composer_version ||= T.let(
|
524
|
+
Helpers.composer_version(parsed_composer_file, parsed_lockfile_or_nil),
|
525
|
+
T.nilable(String)
|
526
|
+
)
|
458
527
|
end
|
459
528
|
|
529
|
+
sig { returns(T::Hash[String, T::Array[String]]) }
|
460
530
|
def initial_platform
|
461
531
|
platform_php = Helpers.capture_platform_php(parsed_composer_file)
|
462
532
|
|
@@ -477,38 +547,63 @@ module Dependabot
|
|
477
547
|
platform
|
478
548
|
end
|
479
549
|
|
550
|
+
sig { returns(T::Hash[String, T.untyped]) }
|
480
551
|
def parsed_composer_file
|
481
|
-
@parsed_composer_file ||=
|
552
|
+
@parsed_composer_file ||= T.let(
|
553
|
+
JSON.parse(T.must(T.must(composer_file).content)),
|
554
|
+
T.nilable(T::Hash[String, T.untyped])
|
555
|
+
)
|
482
556
|
end
|
483
557
|
|
558
|
+
sig { returns(T::Hash[String, T.untyped]) }
|
484
559
|
def parsed_lockfile
|
485
|
-
@parsed_lockfile ||=
|
560
|
+
@parsed_lockfile ||= T.let(
|
561
|
+
JSON.parse(T.must(lockfile&.content)),
|
562
|
+
T.nilable(T::Hash[String, T.untyped])
|
563
|
+
)
|
486
564
|
end
|
487
565
|
|
566
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
488
567
|
def composer_file
|
489
|
-
@composer_file ||=
|
490
|
-
dependency_files.find { |f| f.name == PackageManager::MANIFEST_FILENAME }
|
568
|
+
@composer_file ||= T.let(
|
569
|
+
dependency_files.find { |f| f.name == PackageManager::MANIFEST_FILENAME },
|
570
|
+
T.nilable(Dependabot::DependencyFile)
|
571
|
+
)
|
491
572
|
end
|
492
573
|
|
574
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
493
575
|
def path_dependency_files
|
494
|
-
@path_dependency_files ||=
|
495
|
-
dependency_files.select { |f| f.name.end_with?("/#{PackageManager::MANIFEST_FILENAME}") }
|
576
|
+
@path_dependency_files ||= T.let(
|
577
|
+
dependency_files.select { |f| f.name.end_with?("/#{PackageManager::MANIFEST_FILENAME}") },
|
578
|
+
T.nilable(T::Array[Dependabot::DependencyFile])
|
579
|
+
)
|
496
580
|
end
|
497
581
|
|
582
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
498
583
|
def zipped_path_dependency_files
|
499
|
-
@zipped_path_dependency_files ||=
|
500
|
-
dependency_files.select { |f| f.name.end_with?(".zip", ".gitkeep") }
|
584
|
+
@zipped_path_dependency_files ||= T.let(
|
585
|
+
dependency_files.select { |f| f.name.end_with?(".zip", ".gitkeep") },
|
586
|
+
T.nilable(T::Array[Dependabot::DependencyFile])
|
587
|
+
)
|
501
588
|
end
|
502
589
|
|
590
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
503
591
|
def lockfile
|
504
|
-
@lockfile ||=
|
505
|
-
dependency_files.find { |f| f.name == PackageManager::LOCKFILE_FILENAME }
|
592
|
+
@lockfile ||= T.let(
|
593
|
+
dependency_files.find { |f| f.name == PackageManager::LOCKFILE_FILENAME },
|
594
|
+
T.nilable(Dependabot::DependencyFile)
|
595
|
+
)
|
506
596
|
end
|
507
597
|
|
598
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
508
599
|
def auth_json
|
509
|
-
@auth_json ||=
|
600
|
+
@auth_json ||= T.let(
|
601
|
+
dependency_files.find { |f| f.name == PackageManager::AUTH_FILENAME },
|
602
|
+
T.nilable(Dependabot::DependencyFile)
|
603
|
+
)
|
510
604
|
end
|
511
605
|
|
606
|
+
sig { params(req_string: String).returns(T::Boolean) }
|
512
607
|
def requirement_valid?(req_string)
|
513
608
|
Composer::Requirement.requirements_array(req_string)
|
514
609
|
true
|
@@ -516,12 +611,14 @@ module Dependabot
|
|
516
611
|
false
|
517
612
|
end
|
518
613
|
|
614
|
+
sig { returns(T::Array[Dependabot::Credential]) }
|
519
615
|
def git_credentials
|
520
616
|
credentials
|
521
617
|
.select { |cred| cred["type"] == "git_source" }
|
522
618
|
.select { |cred| cred["password"] }
|
523
619
|
end
|
524
620
|
|
621
|
+
sig { returns(T::Array[Dependabot::Credential]) }
|
525
622
|
def registry_credentials
|
526
623
|
credentials
|
527
624
|
.select { |cred| cred["type"] == PackageManager::REPOSITORY_KEY }
|
@@ -534,23 +631,24 @@ module Dependabot
|
|
534
631
|
extend T::Sig
|
535
632
|
|
536
633
|
# Private source errors
|
537
|
-
CURL_ERROR = /curl error 52 while downloading (?<url>.*): Empty reply from server
|
634
|
+
CURL_ERROR = T.let(/curl error 52 while downloading (?<url>.*): Empty reply from server/, Regexp)
|
538
635
|
|
539
|
-
PRIVATE_SOURCE_AUTH_FAIL = [
|
636
|
+
PRIVATE_SOURCE_AUTH_FAIL = T.let([
|
540
637
|
/Could not authenticate against (?<url>.*)/,
|
541
638
|
/The '(?<url>.*)' URL could not be accessed \(HTTP 403\)/,
|
542
639
|
/The "(?<url>.*)" file could not be downloaded/
|
543
|
-
].freeze
|
640
|
+
].freeze, T::Array[Regexp])
|
544
641
|
|
545
|
-
REQUIREMENT_ERROR = /^(?<req>.*) is invalid, it should not contain uppercase characters
|
642
|
+
REQUIREMENT_ERROR = T.let(/^(?<req>.*) is invalid, it should not contain uppercase characters/, Regexp)
|
546
643
|
|
547
|
-
NO_URL = "No URL specified"
|
644
|
+
NO_URL = T.let("No URL specified", String)
|
548
645
|
|
646
|
+
sig { params(url: String).returns(String) }
|
549
647
|
def sanitize_uri(url)
|
550
648
|
url = "http://#{url}" unless url.start_with?("http")
|
551
649
|
uri = URI.parse(url)
|
552
650
|
host = T.must(uri.host).downcase
|
553
|
-
host.start_with?("www.") ? host[4..-1] : host
|
651
|
+
host.start_with?("www.") ? T.must(host[4..-1]) : host
|
554
652
|
end
|
555
653
|
|
556
654
|
# Handles errors with specific to composer error codes
|
@@ -561,7 +659,8 @@ module Dependabot
|
|
561
659
|
next unless error.message.match?(regex)
|
562
660
|
|
563
661
|
url = T.must(error.message.match(regex)).named_captures["url"]
|
564
|
-
|
662
|
+
sanitized_url = sanitize_uri(T.must(url))
|
663
|
+
raise Dependabot::PrivateSourceAuthenticationFailure, sanitized_url.empty? ? NO_URL : sanitized_url
|
565
664
|
end
|
566
665
|
|
567
666
|
# invalid requirement mentioned in manifest file
|
@@ -573,7 +672,7 @@ module Dependabot
|
|
573
672
|
return unless error.message.match?(CURL_ERROR)
|
574
673
|
|
575
674
|
url = T.must(error.message.match(CURL_ERROR)).named_captures["url"]
|
576
|
-
raise PrivateSourceBadResponse, url
|
675
|
+
raise PrivateSourceBadResponse, T.must(url)
|
577
676
|
end
|
578
677
|
end
|
579
678
|
end
|
@@ -28,7 +28,7 @@ module Dependabot
|
|
28
28
|
latest_version_from_registry || latest_resolvable_version
|
29
29
|
end
|
30
30
|
|
31
|
-
sig { override.returns(T.nilable(Dependabot::
|
31
|
+
sig { override.returns(T.nilable(Dependabot::Version)) }
|
32
32
|
def latest_resolvable_version
|
33
33
|
return nil if path_dependency? || git_dependency?
|
34
34
|
|
@@ -40,26 +40,26 @@ module Dependabot
|
|
40
40
|
latest_allowable_version: latest_version_from_registry,
|
41
41
|
requirements_to_unlock: :own
|
42
42
|
).latest_resolvable_version,
|
43
|
-
T.nilable(Dependabot::
|
43
|
+
T.nilable(Dependabot::Version)
|
44
44
|
)
|
45
45
|
end
|
46
46
|
|
47
|
-
sig { override.returns(T.nilable(Dependabot::
|
47
|
+
sig { override.returns(T.nilable(Dependabot::Version)) }
|
48
48
|
def lowest_security_fix_version
|
49
49
|
latest_version_finder.lowest_security_fix_version
|
50
50
|
end
|
51
51
|
|
52
|
-
sig { override.returns(T.nilable(Dependabot::
|
52
|
+
sig { override.returns(T.nilable(Dependabot::Version)) }
|
53
53
|
def lowest_resolvable_security_fix_version
|
54
54
|
raise "Dependency not vulnerable!" unless vulnerable?
|
55
55
|
|
56
56
|
@lowest_resolvable_security_fix_version ||= T.let(
|
57
57
|
fetch_lowest_resolvable_security_fix_version,
|
58
|
-
T.nilable(Dependabot::
|
58
|
+
T.nilable(Dependabot::Version)
|
59
59
|
)
|
60
60
|
end
|
61
61
|
|
62
|
-
sig { override.returns(T.nilable(Dependabot::
|
62
|
+
sig { override.returns(T.nilable(Dependabot::Version)) }
|
63
63
|
def latest_resolvable_version_with_no_unlock
|
64
64
|
return nil if path_dependency? || git_dependency?
|
65
65
|
|
@@ -71,7 +71,7 @@ module Dependabot
|
|
71
71
|
latest_allowable_version: latest_version_from_registry,
|
72
72
|
requirements_to_unlock: :none
|
73
73
|
).latest_resolvable_version,
|
74
|
-
T.nilable(Dependabot::
|
74
|
+
T.nilable(Dependabot::Version)
|
75
75
|
)
|
76
76
|
end
|
77
77
|
|
@@ -80,7 +80,7 @@ module Dependabot
|
|
80
80
|
RequirementsUpdater.new(
|
81
81
|
requirements: dependency.requirements,
|
82
82
|
latest_resolvable_version: preferred_resolvable_version&.to_s,
|
83
|
-
update_strategy: requirements_update_strategy
|
83
|
+
update_strategy: T.must(requirements_update_strategy)
|
84
84
|
).updated_requirements
|
85
85
|
end
|
86
86
|
|
@@ -111,7 +111,7 @@ module Dependabot
|
|
111
111
|
raise NotImplementedError
|
112
112
|
end
|
113
113
|
|
114
|
-
sig { returns(T.nilable(Dependabot::
|
114
|
+
sig { returns(T.nilable(Dependabot::Version)) }
|
115
115
|
def latest_version_from_registry
|
116
116
|
latest_version_finder.latest_version
|
117
117
|
end
|
@@ -131,7 +131,7 @@ module Dependabot
|
|
131
131
|
)
|
132
132
|
end
|
133
133
|
|
134
|
-
sig { returns(T.nilable(Dependabot::
|
134
|
+
sig { returns(T.nilable(Dependabot::Version)) }
|
135
135
|
def fetch_lowest_resolvable_security_fix_version
|
136
136
|
return nil if path_dependency? || git_dependency?
|
137
137
|
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dependabot-composer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.312.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dependabot
|
8
8
|
bindir: bin
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-05-
|
10
|
+
date: 2025-05-09 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: dependabot-common
|
@@ -15,28 +15,28 @@ dependencies:
|
|
15
15
|
requirements:
|
16
16
|
- - '='
|
17
17
|
- !ruby/object:Gem::Version
|
18
|
-
version: 0.
|
18
|
+
version: 0.312.0
|
19
19
|
type: :runtime
|
20
20
|
prerelease: false
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
22
22
|
requirements:
|
23
23
|
- - '='
|
24
24
|
- !ruby/object:Gem::Version
|
25
|
-
version: 0.
|
25
|
+
version: 0.312.0
|
26
26
|
- !ruby/object:Gem::Dependency
|
27
27
|
name: debug
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
29
29
|
requirements:
|
30
30
|
- - "~>"
|
31
31
|
- !ruby/object:Gem::Version
|
32
|
-
version: 1.9
|
32
|
+
version: '1.9'
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
35
|
version_requirements: !ruby/object:Gem::Requirement
|
36
36
|
requirements:
|
37
37
|
- - "~>"
|
38
38
|
- !ruby/object:Gem::Version
|
39
|
-
version: 1.9
|
39
|
+
version: '1.9'
|
40
40
|
- !ruby/object:Gem::Dependency
|
41
41
|
name: gpgme
|
42
42
|
requirement: !ruby/object:Gem::Requirement
|
@@ -57,14 +57,14 @@ dependencies:
|
|
57
57
|
requirements:
|
58
58
|
- - "~>"
|
59
59
|
- !ruby/object:Gem::Version
|
60
|
-
version: '13'
|
60
|
+
version: '13.2'
|
61
61
|
type: :development
|
62
62
|
prerelease: false
|
63
63
|
version_requirements: !ruby/object:Gem::Requirement
|
64
64
|
requirements:
|
65
65
|
- - "~>"
|
66
66
|
- !ruby/object:Gem::Version
|
67
|
-
version: '13'
|
67
|
+
version: '13.2'
|
68
68
|
- !ruby/object:Gem::Dependency
|
69
69
|
name: rspec
|
70
70
|
requirement: !ruby/object:Gem::Requirement
|
@@ -99,98 +99,98 @@ dependencies:
|
|
99
99
|
requirements:
|
100
100
|
- - "~>"
|
101
101
|
- !ruby/object:Gem::Version
|
102
|
-
version: 1.9
|
102
|
+
version: '1.9'
|
103
103
|
type: :development
|
104
104
|
prerelease: false
|
105
105
|
version_requirements: !ruby/object:Gem::Requirement
|
106
106
|
requirements:
|
107
107
|
- - "~>"
|
108
108
|
- !ruby/object:Gem::Version
|
109
|
-
version: 1.9
|
109
|
+
version: '1.9'
|
110
110
|
- !ruby/object:Gem::Dependency
|
111
111
|
name: rubocop
|
112
112
|
requirement: !ruby/object:Gem::Requirement
|
113
113
|
requirements:
|
114
114
|
- - "~>"
|
115
115
|
- !ruby/object:Gem::Version
|
116
|
-
version: 1.67
|
116
|
+
version: '1.67'
|
117
117
|
type: :development
|
118
118
|
prerelease: false
|
119
119
|
version_requirements: !ruby/object:Gem::Requirement
|
120
120
|
requirements:
|
121
121
|
- - "~>"
|
122
122
|
- !ruby/object:Gem::Version
|
123
|
-
version: 1.67
|
123
|
+
version: '1.67'
|
124
124
|
- !ruby/object:Gem::Dependency
|
125
125
|
name: rubocop-performance
|
126
126
|
requirement: !ruby/object:Gem::Requirement
|
127
127
|
requirements:
|
128
128
|
- - "~>"
|
129
129
|
- !ruby/object:Gem::Version
|
130
|
-
version: 1.22
|
130
|
+
version: '1.22'
|
131
131
|
type: :development
|
132
132
|
prerelease: false
|
133
133
|
version_requirements: !ruby/object:Gem::Requirement
|
134
134
|
requirements:
|
135
135
|
- - "~>"
|
136
136
|
- !ruby/object:Gem::Version
|
137
|
-
version: 1.22
|
137
|
+
version: '1.22'
|
138
138
|
- !ruby/object:Gem::Dependency
|
139
139
|
name: rubocop-rspec
|
140
140
|
requirement: !ruby/object:Gem::Requirement
|
141
141
|
requirements:
|
142
142
|
- - "~>"
|
143
143
|
- !ruby/object:Gem::Version
|
144
|
-
version: 2.29
|
144
|
+
version: '2.29'
|
145
145
|
type: :development
|
146
146
|
prerelease: false
|
147
147
|
version_requirements: !ruby/object:Gem::Requirement
|
148
148
|
requirements:
|
149
149
|
- - "~>"
|
150
150
|
- !ruby/object:Gem::Version
|
151
|
-
version: 2.29
|
151
|
+
version: '2.29'
|
152
152
|
- !ruby/object:Gem::Dependency
|
153
153
|
name: rubocop-sorbet
|
154
154
|
requirement: !ruby/object:Gem::Requirement
|
155
155
|
requirements:
|
156
156
|
- - "~>"
|
157
157
|
- !ruby/object:Gem::Version
|
158
|
-
version: 0.8
|
158
|
+
version: '0.8'
|
159
159
|
type: :development
|
160
160
|
prerelease: false
|
161
161
|
version_requirements: !ruby/object:Gem::Requirement
|
162
162
|
requirements:
|
163
163
|
- - "~>"
|
164
164
|
- !ruby/object:Gem::Version
|
165
|
-
version: 0.8
|
165
|
+
version: '0.8'
|
166
166
|
- !ruby/object:Gem::Dependency
|
167
167
|
name: simplecov
|
168
168
|
requirement: !ruby/object:Gem::Requirement
|
169
169
|
requirements:
|
170
170
|
- - "~>"
|
171
171
|
- !ruby/object:Gem::Version
|
172
|
-
version: 0.22
|
172
|
+
version: '0.22'
|
173
173
|
type: :development
|
174
174
|
prerelease: false
|
175
175
|
version_requirements: !ruby/object:Gem::Requirement
|
176
176
|
requirements:
|
177
177
|
- - "~>"
|
178
178
|
- !ruby/object:Gem::Version
|
179
|
-
version: 0.22
|
179
|
+
version: '0.22'
|
180
180
|
- !ruby/object:Gem::Dependency
|
181
181
|
name: turbo_tests
|
182
182
|
requirement: !ruby/object:Gem::Requirement
|
183
183
|
requirements:
|
184
184
|
- - "~>"
|
185
185
|
- !ruby/object:Gem::Version
|
186
|
-
version: 2.2
|
186
|
+
version: '2.2'
|
187
187
|
type: :development
|
188
188
|
prerelease: false
|
189
189
|
version_requirements: !ruby/object:Gem::Requirement
|
190
190
|
requirements:
|
191
191
|
- - "~>"
|
192
192
|
- !ruby/object:Gem::Version
|
193
|
-
version: 2.2
|
193
|
+
version: '2.2'
|
194
194
|
- !ruby/object:Gem::Dependency
|
195
195
|
name: vcr
|
196
196
|
requirement: !ruby/object:Gem::Requirement
|
@@ -279,7 +279,7 @@ licenses:
|
|
279
279
|
- MIT
|
280
280
|
metadata:
|
281
281
|
bug_tracker_uri: https://github.com/dependabot/dependabot-core/issues
|
282
|
-
changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.
|
282
|
+
changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.312.0
|
283
283
|
rdoc_options: []
|
284
284
|
require_paths:
|
285
285
|
- lib
|