dependabot-composer 0.310.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.
@@ -1,22 +1,28 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
+ require "sorbet-runtime"
5
+
4
6
  require "dependabot/composer/file_updater"
5
7
 
6
8
  module Dependabot
7
9
  module Composer
8
10
  class FileUpdater
9
11
  class ManifestUpdater
12
+ extend T::Sig
13
+
14
+ sig { params(dependencies: T::Array[Dependabot::Dependency], manifest: Dependabot::DependencyFile).void }
10
15
  def initialize(dependencies:, manifest:)
11
16
  @dependencies = dependencies
12
17
  @manifest = manifest
13
18
  end
14
19
 
20
+ sig { returns(String) }
15
21
  def updated_manifest_content
16
- dependencies.reduce(manifest.content.dup) do |content, dep|
22
+ T.must(dependencies.reduce(manifest.content.dup) do |content, dep|
17
23
  updated_content = content
18
24
  updated_requirements(dep).each do |new_req|
19
- old_req = old_requirement(dep, new_req).fetch(:requirement)
25
+ old_req = old_requirement(dep, new_req)&.fetch(:requirement)
20
26
  updated_req = new_req.fetch(:requirement)
21
27
 
22
28
  regex =
@@ -25,7 +31,7 @@ module Dependabot
25
31
  "#{Regexp.escape(old_req)}"
26
32
  /x
27
33
 
28
- updated_content = content.gsub(regex) do |declaration|
34
+ updated_content = content&.gsub(regex) do |declaration|
29
35
  declaration.gsub(%("#{old_req}"), %("#{updated_req}"))
30
36
  end
31
37
 
@@ -33,32 +39,45 @@ module Dependabot
33
39
  end
34
40
 
35
41
  updated_content
36
- end
42
+ end)
37
43
  end
38
44
 
39
45
  private
40
46
 
47
+ sig { returns(T::Array[Dependabot::Dependency]) }
41
48
  attr_reader :dependencies
49
+
50
+ sig { returns(Dependabot::DependencyFile) }
42
51
  attr_reader :manifest
43
52
 
53
+ sig { params(dependency: Dependabot::Dependency).returns(T::Array[T::Hash[Symbol, T.untyped]]) }
44
54
  def new_requirements(dependency)
45
55
  dependency.requirements.select { |r| r[:file] == manifest.name }
46
56
  end
47
57
 
58
+ sig do
59
+ params(
60
+ dependency: Dependabot::Dependency,
61
+ new_requirement: T::Hash[Symbol, T.untyped]
62
+ )
63
+ .returns(T.nilable(T::Hash[Symbol, T.untyped]))
64
+ end
48
65
  def old_requirement(dependency, new_requirement)
49
- dependency.previous_requirements
50
- .select { |r| r[:file] == manifest.name }
51
- .find { |r| r[:groups] == new_requirement[:groups] }
66
+ T.must(dependency.previous_requirements)
67
+ .select { |r| r[:file] == manifest.name }
68
+ .find { |r| r[:groups] == new_requirement[:groups] }
52
69
  end
53
70
 
71
+ sig { params(dependency: Dependabot::Dependency).returns(T::Array[T::Hash[Symbol, T.untyped]]) }
54
72
  def updated_requirements(dependency)
55
73
  new_requirements(dependency)
56
- .reject { |r| dependency.previous_requirements.include?(r) }
74
+ .reject { |r| T.must(dependency.previous_requirements).include?(r) }
57
75
  end
58
76
 
77
+ sig { params(file: Dependabot::DependencyFile, dependency: Dependabot::Dependency).returns(T::Boolean) }
59
78
  def requirement_changed?(file, dependency)
60
79
  changed_requirements =
61
- dependency.requirements - dependency.previous_requirements
80
+ dependency.requirements - T.must(dependency.previous_requirements)
62
81
 
63
82
  changed_requirements.any? { |f| f[:file] == file.name }
64
83
  end
@@ -56,7 +56,7 @@ module Dependabot
56
56
  def updated_composer_json_content
57
57
  ManifestUpdater.new(
58
58
  dependencies: dependencies,
59
- manifest: composer_json
59
+ manifest: T.must(composer_json)
60
60
  ).updated_manifest_content
61
61
  end
62
62
 
@@ -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::DEFAULT_PARSER.regexp[:ABS_URI].match?(dependency_url)
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,7 +1,9 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "excon"
5
+ require "sorbet-runtime"
6
+
5
7
  require "dependabot/metadata_finders"
6
8
  require "dependabot/metadata_finders/base"
7
9
  require "dependabot/registry_client"
@@ -10,6 +12,21 @@ require "dependabot/composer/version"
10
12
  module Dependabot
11
13
  module Composer
12
14
  class MetadataFinder < Dependabot::MetadataFinders::Base
15
+ extend T::Sig
16
+
17
+ sig do
18
+ override
19
+ .params(
20
+ dependency: Dependabot::Dependency,
21
+ credentials: T::Array[Dependabot::Credential]
22
+ )
23
+ .void
24
+ end
25
+ def initialize(dependency:, credentials:)
26
+ @packagist_listing = T.let(nil, T.nilable(T::Hash[String, T.untyped]))
27
+ super
28
+ end
29
+
13
30
  private
14
31
 
15
32
  sig { override.returns(T.nilable(Source)) }
@@ -1,8 +1,9 @@
1
- # typed: true
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
- def initialize(dependency:, dependency_files:, credentials:,
17
- ignored_versions:, raise_on_ignored: false,
18
- security_advisories:)
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 ||= fetch_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 ||= fetch_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 { |c| url_host == c["registry"] || url_host == URI(c["registry"]).host }
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
- return [] unless auth_json
227
+ json = auth_json
228
+ return [] unless json
184
229
 
185
- parsed_auth_json = JSON.parse(auth_json.content)
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, auth_json.path
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: true
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
- real_version = req_string.split(/\sas\s/).first.strip
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
- .first(old_parts.count)
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
- upper_bound = versions.map { |v| version_class.new(v) }.max
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.to_s)
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