dependabot-composer 0.310.0 → 0.311.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 19b71569b2bd0eeb530428366bccef3bc61d6285f19c9895e8b9c76f6d39c14d
4
- data.tar.gz: 6e19dbc11146ebcc760aca127eee44433f17b66fc8649feefd2c7f01ea8d0104
3
+ metadata.gz: 1b7f9286dece3b3c0c35b5be85a580078a6467edb4dd43b9817601f541b5c2cf
4
+ data.tar.gz: 4d31a01b968c789451178cee2855a6e507fad696663bac7e01cc89103b6a5c0c
5
5
  SHA512:
6
- metadata.gz: 9e4d00454f1b5e3c0dd810c1bf9a3d8423cefe9974ad78c473870c7fe71f39cf7fdd3ae41449df12890ada3ceb5c9006a29e30943cdef2e027502134aec91257
7
- data.tar.gz: 417e824b9d0da5f8a2d7a0ef66ce8ab549bac4e4e6f0ea978fec219c525885d721ac6bf74ecf3a23217c3210caacd0a3c37aa62eaca0fcc103b1e111de9099de
6
+ metadata.gz: fb35c79db05afba4a18eceb83722484488031b68503975dd13df219b3eaba3d98d931bb0850dd74f2c5a3023689441e436083f01e81a188bd1db897840496a86
7
+ data.tar.gz: d963a5ccdc05bc63e3ee72b6d59bd8e9e8eedd3ac71d3a7da457d803ccd9ff618cf2d68c5da1e6e6707606cbbc1d094664dbc2d386fe3ca22ace4116eaa3a5cd
@@ -1,4 +1,4 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "json"
@@ -15,14 +15,17 @@ module Dependabot
15
15
  require_relative "file_fetcher/path_dependency_builder"
16
16
  require_relative "helpers"
17
17
 
18
+ sig { override.params(filenames: T::Array[String]).returns(T::Boolean) }
18
19
  def self.required_files_in?(filenames)
19
20
  filenames.include?(PackageManager::MANIFEST_FILENAME)
20
21
  end
21
22
 
23
+ sig { override.returns(String) }
22
24
  def self.required_files_message
23
25
  "Repo must contain a #{PackageManager::MANIFEST_FILENAME}."
24
26
  end
25
27
 
28
+ sig { override.returns(T.nilable(T::Hash[Symbol, T.untyped])) }
26
29
  def ecosystem_versions
27
30
  {
28
31
  package_managers: {
@@ -44,28 +47,35 @@ module Dependabot
44
47
 
45
48
  private
46
49
 
50
+ sig { returns(Dependabot::DependencyFile) }
47
51
  def composer_json
48
- @composer_json ||= fetch_file_from_host(PackageManager::MANIFEST_FILENAME)
52
+ @composer_json ||= T.let(
53
+ fetch_file_from_host(PackageManager::MANIFEST_FILENAME),
54
+ T.nilable(Dependabot::DependencyFile)
55
+ )
49
56
  end
50
57
 
58
+ sig { returns(T.nilable(Dependabot::DependencyFile)) }
51
59
  def composer_lock
52
- return @composer_lock if defined?(@composer_lock)
53
-
54
- @composer_lock = fetch_file_if_present(PackageManager::LOCKFILE_FILENAME)
60
+ @composer_lock ||= T.let(
61
+ fetch_file_if_present(PackageManager::LOCKFILE_FILENAME),
62
+ T.nilable(Dependabot::DependencyFile)
63
+ )
55
64
  end
56
65
 
57
66
  # NOTE: This is fetched but currently unused
67
+ sig { returns(T.nilable(Dependabot::DependencyFile)) }
58
68
  def auth_json
59
- return @auth_json if defined?(@auth_json)
60
-
61
- @auth_json = fetch_support_file(PackageManager::AUTH_FILENAME)
69
+ @auth_json ||= T.let(
70
+ fetch_support_file(PackageManager::AUTH_FILENAME),
71
+ T.nilable(Dependabot::DependencyFile)
72
+ )
62
73
  end
63
74
 
75
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
64
76
  def artifact_dependencies
65
- return @artifact_dependencies if defined?(@artifact_dependencies)
66
-
67
77
  # Find zip files in the artifact sources and download them.
68
- @artifact_dependencies =
78
+ @artifact_dependencies ||= T.let(
69
79
  artifact_sources.map do |url|
70
80
  repo_contents(dir: url)
71
81
  .select { |file| file.type == "file" && file.name.end_with?(".zip") }
@@ -78,7 +88,9 @@ module Dependabot
78
88
  type: "file"
79
89
  )
80
90
  end
81
- end.flatten
91
+ end.flatten,
92
+ T.nilable(T::Array[Dependabot::DependencyFile])
93
+ )
82
94
 
83
95
  # Add .gitkeep to all directories in case they are empty. Composer isn't ok with empty directories.
84
96
  @artifact_dependencies += artifact_sources.map do |url|
@@ -96,8 +108,9 @@ module Dependabot
96
108
  @artifact_dependencies
97
109
  end
98
110
 
111
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
99
112
  def path_dependencies
100
- @path_dependencies ||=
113
+ @path_dependencies ||= T.let(
101
114
  begin
102
115
  composer_json_files = []
103
116
  unfetchable_deps = []
@@ -123,19 +136,24 @@ module Dependabot
123
136
  composer_json_files.tap do |files|
124
137
  files.each { |f| f.support_file = true }
125
138
  end
126
- end
139
+ end,
140
+ T.nilable(T::Array[Dependabot::DependencyFile])
141
+ )
127
142
  end
128
143
 
144
+ sig { returns(T::Array[String]) }
129
145
  def artifact_sources
130
146
  sources.select { |details| details["type"] == "artifact" }.map { |details| details["url"] }
131
147
  end
132
148
 
149
+ sig { returns(T::Array[String]) }
133
150
  def path_sources
134
151
  sources.select { |details| details["type"] == "path" }.map { |details| details["url"] }
135
152
  end
136
153
 
154
+ sig { returns(T::Array[T::Hash[String, T.untyped]]) }
137
155
  def sources
138
- @sources ||=
156
+ @sources ||= T.let(
139
157
  begin
140
158
  repos = parsed_composer_json.fetch("repositories", [])
141
159
  if repos.is_a?(Hash) || repos.is_a?(Array)
@@ -147,9 +165,12 @@ module Dependabot
147
165
  else
148
166
  []
149
167
  end
150
- end
168
+ end,
169
+ T.nilable(T::Array[T::Hash[String, T.untyped]])
170
+ )
151
171
  end
152
172
 
173
+ sig { params(unfetchable_deps: T::Array[String]).returns(T::Array[Dependabot::DependencyFile]) }
153
174
  def build_unfetchable_deps(unfetchable_deps)
154
175
  unfetchable_deps.filter_map do |path|
155
176
  PathDependencyBuilder.new(
@@ -160,6 +181,7 @@ module Dependabot
160
181
  end
161
182
  end
162
183
 
184
+ sig { params(path: String).returns(T::Array[String]) }
163
185
  def expand_path(path)
164
186
  wildcard_depth = 0
165
187
  path = path.gsub(/\*$/, "")
@@ -185,6 +207,7 @@ module Dependabot
185
207
  .select { |p| p.to_s.start_with?(path.gsub(/\*$/, "")) }
186
208
  end
187
209
 
210
+ sig { returns(T::Array[String]) }
188
211
  def lockfile_path_dependency_paths
189
212
  keys = FileParser::DEPENDENCY_GROUP_KEYS
190
213
  .map { |h| h.fetch(:lockfile) }
@@ -198,20 +221,29 @@ module Dependabot
198
221
  end
199
222
  end
200
223
 
224
+ sig { returns(T::Hash[String, T.untyped]) }
201
225
  def parsed_composer_json
202
- @parsed_composer_json ||= JSON.parse(composer_json.content)
226
+ @parsed_composer_json ||= T.let(
227
+ JSON.parse(T.must(composer_json.content)),
228
+ T.nilable(T::Hash[String, T.untyped])
229
+ )
203
230
  rescue JSON::ParserError
204
231
  raise Dependabot::DependencyFileNotParseable, composer_json.path
205
232
  end
206
233
 
234
+ sig { returns(T::Hash[String, T.untyped]) }
207
235
  def parsed_lockfile
208
236
  return {} unless composer_lock
209
237
 
210
- @parsed_lockfile ||= JSON.parse(composer_lock.content)
238
+ @parsed_lockfile ||= T.let(
239
+ JSON.parse(T.must(T.must(composer_lock).content)),
240
+ T.nilable(T::Hash[String, T.untyped])
241
+ )
211
242
  rescue JSON::ParserError
212
243
  {}
213
244
  end
214
245
 
246
+ sig { params(filename: String).returns(Dependabot::DependencyFile) }
215
247
  def fetch_file_with_root_fallback(filename)
216
248
  path = Pathname.new(File.join(directory, filename)).cleanpath.to_path
217
249
 
@@ -1,4 +1,4 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "dependabot/shared_helpers"
@@ -10,19 +10,26 @@ require "dependabot/composer/requirement"
10
10
  require "dependabot/composer/native_helpers"
11
11
  require "dependabot/composer/helpers"
12
12
  require "dependabot/composer/update_checker/version_resolver"
13
+ require "sorbet-runtime"
13
14
 
14
15
  # rubocop:disable Metrics/ClassLength
15
16
  module Dependabot
16
17
  module Composer
17
18
  class FileUpdater
18
19
  class LockfileUpdater
20
+ extend T::Sig
21
+
19
22
  require_relative "manifest_updater"
20
23
 
21
24
  class MissingExtensions < StandardError
25
+ extend T::Sig
26
+
27
+ sig { returns(T::Array[T::Hash[Symbol, String]]) }
22
28
  attr_reader :extensions
23
29
 
30
+ sig { params(extensions: T::Array[T::Hash[Symbol, String]]).void }
24
31
  def initialize(extensions)
25
- @extensions = extensions
32
+ @extensions = T.let(extensions, T::Array[T::Hash[Symbol, String]])
26
33
  super
27
34
  end
28
35
  end
@@ -39,15 +46,27 @@ module Dependabot
39
46
  }x
40
47
  MISSING_ENV_VAR_REGEX = /Environment variable '(?<env_var>.[^']+)' is not set/
41
48
 
49
+ sig do
50
+ params(
51
+ dependencies: T::Array[Dependabot::Dependency],
52
+ dependency_files: T::Array[Dependabot::DependencyFile],
53
+ credentials: T::Array[Dependabot::Credential]
54
+ ).void
55
+ end
42
56
  def initialize(dependencies:, dependency_files:, credentials:)
43
57
  @dependencies = dependencies
44
58
  @dependency_files = dependency_files
45
59
  @credentials = credentials
46
- @composer_platform_extensions = initial_platform
60
+ @composer_platform_extensions = T.let(initial_platform, T::Hash[String, T::Array[String]])
61
+ @lock_git_deps = T.let(true, T::Boolean)
47
62
  end
48
63
 
64
+ sig { returns(String) }
49
65
  def updated_lockfile_content
50
- @updated_lockfile_content ||= generate_updated_lockfile_content
66
+ @updated_lockfile_content ||= T.let(
67
+ generate_updated_lockfile_content,
68
+ T.nilable(String)
69
+ )
51
70
  rescue MissingExtensions => e
52
71
  previous_extensions = composer_platform_extensions.dup
53
72
  update_required_extensions(e.extensions)
@@ -58,13 +77,21 @@ module Dependabot
58
77
 
59
78
  private
60
79
 
80
+ sig { returns(T::Array[Dependabot::Dependency]) }
61
81
  attr_reader :dependencies
82
+
83
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
62
84
  attr_reader :dependency_files
85
+
86
+ sig { returns(T::Array[Dependabot::Credential]) }
63
87
  attr_reader :credentials
88
+
89
+ sig { returns(T::Hash[String, T::Array[String]]) }
64
90
  attr_reader :composer_platform_extensions
65
91
 
92
+ sig { returns(String) }
66
93
  def generate_updated_lockfile_content
67
- base_directory = dependency_files.first.directory
94
+ base_directory = T.must(dependency_files.first).directory
68
95
  SharedHelpers.in_a_temporary_directory(base_directory) do
69
96
  write_temporary_dependency_files
70
97
 
@@ -87,13 +114,15 @@ module Dependabot
87
114
  handle_composer_errors(e)
88
115
  end
89
116
 
117
+ sig { returns(Dependabot::Dependency) }
90
118
  def dependency
91
119
  # For now, we'll only ever be updating a single dependency for PHP
92
- dependencies.first
120
+ T.must(dependencies.first)
93
121
  end
94
122
 
123
+ sig { returns(T::Hash[String, String]) }
95
124
  def run_update_helper
96
- SharedHelpers.with_git_configured(credentials: credentials) do
125
+ SharedHelpers.with_git_configured(credentials: T.unsafe(credentials)) do
97
126
  SharedHelpers.run_helper_subprocess(
98
127
  command: "php -d memory_limit=-1 #{php_helper_path}",
99
128
  allow_unsafe_shell_command: true,
@@ -110,6 +139,7 @@ module Dependabot
110
139
  end
111
140
  end
112
141
 
142
+ sig { returns(String) }
113
143
  def updated_composer_json_content
114
144
  ManifestUpdater.new(
115
145
  dependencies: dependencies,
@@ -117,6 +147,7 @@ module Dependabot
117
147
  ).updated_manifest_content
118
148
  end
119
149
 
150
+ sig { params(error: SharedHelpers::HelperSubprocessFailed).returns(T::Boolean) }
120
151
  def transitory_failure?(error)
121
152
  return true if error.message.include?("404 Not Found")
122
153
  return true if error.message.include?("timed out")
@@ -125,6 +156,7 @@ module Dependabot
125
156
  error.message.include?("Content-Length mismatch")
126
157
  end
127
158
 
159
+ sig { params(error: SharedHelpers::HelperSubprocessFailed).returns(T::Boolean) }
128
160
  def locked_git_dep_error?(error)
129
161
  error.message.start_with?("Could not authenticate against")
130
162
  end
@@ -135,6 +167,7 @@ module Dependabot
135
167
  # rubocop:disable Metrics/CyclomaticComplexity
136
168
  # rubocop:disable Metrics/MethodLength
137
169
  # rubocop:disable Metrics/PerceivedComplexity
170
+ sig { params(error: SharedHelpers::HelperSubprocessFailed).returns(T.noreturn) }
138
171
  def handle_composer_errors(error)
139
172
  if error.message.match?(MISSING_EXPLICIT_PLATFORM_REQ_REGEX)
140
173
  # These errors occur when platform requirements declared explicitly
@@ -142,7 +175,7 @@ module Dependabot
142
175
  missing_extensions =
143
176
  error.message.scan(MISSING_EXPLICIT_PLATFORM_REQ_REGEX)
144
177
  .map do |extension_string|
145
- name, requirement = extension_string.strip.split(" ", 2)
178
+ name, requirement = T.cast(extension_string, String).strip.split(" ", 2)
146
179
  { name: name, requirement: requirement }
147
180
  end
148
181
  raise MissingExtensions, missing_extensions
@@ -153,7 +186,7 @@ module Dependabot
153
186
  missing_extensions =
154
187
  error.message.scan(MISSING_IMPLICIT_PLATFORM_REQ_REGEX)
155
188
  .map do |extension_string|
156
- name, requirement = extension_string.strip.split(" ", 2)
189
+ name, requirement = T.cast(extension_string, String).strip.split(" ", 2)
157
190
  { name: name, requirement: requirement }
158
191
  end
159
192
 
@@ -162,10 +195,10 @@ module Dependabot
162
195
  version_for_reqs(existing_reqs + [hash[:requirement]])
163
196
  end
164
197
 
165
- raise MissingExtensions, [missing_extension]
198
+ raise MissingExtensions, (T.must(missing_extension).then { |ext| [ext] })
166
199
  end
167
200
 
168
- raise git_dependency_reference_error(error) if error.message.start_with?("Failed to execute git checkout")
201
+ git_dependency_reference_error(error) if error.message.start_with?("Failed to execute git checkout")
169
202
 
170
203
  # Special case for Laravel Nova, which will fall back to attempting
171
204
  # to close a private repo if given invalid (or no) credentials
@@ -185,9 +218,10 @@ module Dependabot
185
218
 
186
219
  # NOTE: This matches error output from a composer plugin (private-composer-installer):
187
220
  # https://github.com/ffraenz/private-composer-installer/blob/8655e3da4e8f99203f13ccca33b9ab953ad30a31/src/Exception/MissingEnvException.php#L22
188
- if error.message.match?(MISSING_ENV_VAR_REGEX)
189
- env_var = error.message.match(MISSING_ENV_VAR_REGEX).named_captures.fetch("env_var")
190
- raise MissingEnvironmentVariable, env_var
221
+ match_data = error.message.match(MISSING_ENV_VAR_REGEX)
222
+ if match_data
223
+ env_var = match_data.named_captures.fetch("env_var")
224
+ raise MissingEnvironmentVariable, T.must(env_var)
191
225
  end
192
226
 
193
227
  if error.message.start_with?("Unknown downloader type: npm-sign") ||
@@ -198,9 +232,9 @@ module Dependabot
198
232
 
199
233
  raise Dependabot::OutOfMemory if error.message.start_with?("Allowed memory size")
200
234
 
201
- if error.message.include?("403 Forbidden")
202
- source = error.message.match(%r{https?://(?<source>[^/]+)/})
203
- .named_captures.fetch("source")
235
+ match_data = error.message.match(%r{https?://(?<source>[^/]+)/})
236
+ if error.message.include?("403 Forbidden") && match_data
237
+ source = match_data.named_captures.fetch("source")
204
238
  raise PrivateSourceAuthenticationFailure, source
205
239
  end
206
240
 
@@ -217,15 +251,17 @@ module Dependabot
217
251
  # rubocop:enable Metrics/MethodLength
218
252
  # rubocop:enable Metrics/PerceivedComplexity
219
253
 
254
+ sig { returns(T::Boolean) }
220
255
  def library?
221
256
  parsed_composer_json["type"] == "library"
222
257
  end
223
258
 
259
+ sig { params(message: String).returns(T::Boolean) }
224
260
  def implicit_platform_reqs_satisfiable?(message)
225
261
  missing_extensions =
226
262
  message.scan(MISSING_IMPLICIT_PLATFORM_REQ_REGEX)
227
263
  .map do |extension_string|
228
- name, requirement = extension_string.strip.split(" ", 2)
264
+ name, requirement = T.cast(extension_string, String).strip.split(" ", 2)
229
265
  { name: name, requirement: requirement }
230
266
  end
231
267
 
@@ -235,6 +271,7 @@ module Dependabot
235
271
  end
236
272
  end
237
273
 
274
+ sig { void }
238
275
  def write_temporary_dependency_files
239
276
  artifact_dependencies.each do |file|
240
277
  path = file.name
@@ -250,9 +287,10 @@ module Dependabot
250
287
 
251
288
  File.write(PackageManager::MANIFEST_FILENAME, locked_composer_json_content)
252
289
  File.write(PackageManager::LOCKFILE_FILENAME, lockfile.content)
253
- File.write(PackageManager::AUTH_FILENAME, auth_json.content) if auth_json
290
+ File.write(PackageManager::AUTH_FILENAME, T.must(auth_json).content) if auth_json
254
291
  end
255
292
 
293
+ sig { returns(String) }
256
294
  def locked_composer_json_content
257
295
  content = updated_composer_json_content
258
296
  content = lock_dependencies_being_updated(content)
@@ -261,6 +299,7 @@ module Dependabot
261
299
  content
262
300
  end
263
301
 
302
+ sig { params(content: String).returns(String) }
264
303
  def add_temporary_platform_extensions(content)
265
304
  json = JSON.parse(content)
266
305
 
@@ -274,6 +313,7 @@ module Dependabot
274
313
  JSON.dump(json)
275
314
  end
276
315
 
316
+ sig { params(original_content: String).returns(String) }
277
317
  def lock_dependencies_being_updated(original_content)
278
318
  dependencies.reduce(original_content) do |content, dep|
279
319
  updated_req = dep.version
@@ -298,6 +338,7 @@ module Dependabot
298
338
  end
299
339
  end
300
340
 
341
+ sig { params(content: String).returns(String) }
301
342
  def lock_git_dependencies(content)
302
343
  json = JSON.parse(content)
303
344
 
@@ -309,7 +350,7 @@ module Dependabot
309
350
  next if req.include?("#")
310
351
 
311
352
  commit_sha = parsed_lockfile
312
- .fetch(keys[:lockfile], [])
353
+ .fetch(T.must(keys[:lockfile]), [])
313
354
  .find { |d| d["name"] == name }
314
355
  &.dig("source", "reference")
315
356
  updated_req_parts = req.split
@@ -321,11 +362,13 @@ module Dependabot
321
362
  JSON.dump(json)
322
363
  end
323
364
 
365
+ sig { params(error: SharedHelpers::HelperSubprocessFailed).returns(T.noreturn) }
324
366
  def git_dependency_reference_error(error)
325
367
  ref = error.message.match(/checkout '(?<ref>.*?)'/)
326
- .named_captures.fetch("ref")
368
+ &.named_captures
369
+ &.fetch("ref")
327
370
  dependency_name =
328
- JSON.parse(lockfile.content)
371
+ JSON.parse(T.must(lockfile.content))
329
372
  .values_at("packages", "packages-dev").flatten(1)
330
373
  .find { |dep| dep.dig("source", "reference") == ref }
331
374
  &.fetch("name")
@@ -335,23 +378,20 @@ module Dependabot
335
378
  raise GitDependencyReferenceNotFound, dependency_name
336
379
  end
337
380
 
338
- def post_process_lockfile(content)
339
- content = replace_patches(content)
340
- content = replace_content_hash(content)
341
- replace_platform_overrides(content)
342
- end
343
-
381
+ sig { params(updated_content: String).returns(String) }
344
382
  def replace_patches(updated_content)
345
383
  content = updated_content
346
384
  %w(packages packages-dev).each do |package_type|
347
- JSON.parse(lockfile.content).fetch(package_type).each do |details|
385
+ JSON.parse(T.must(lockfile.content))
386
+ .fetch(package_type, [])
387
+ .each do |details|
348
388
  next unless details["extra"].is_a?(Hash)
349
389
  next unless (patches = details.dig("extra", "patches_applied"))
350
390
 
351
391
  updated_object = JSON.parse(content)
352
392
  updated_object_package =
353
393
  updated_object
354
- .fetch(package_type)
394
+ .fetch(package_type, [])
355
395
  .find { |d| d["name"] == details["name"] }
356
396
 
357
397
  next unless updated_object_package
@@ -360,14 +400,18 @@ module Dependabot
360
400
  updated_object_package["extra"]["patches_applied"] = patches
361
401
 
362
402
  content =
363
- JSON.pretty_generate(updated_object, indent: " ")
403
+ T.cast(
404
+ JSON.pretty_generate(updated_object, indent: " ")
364
405
  .gsub(/\[\n\n\s*\]/, "[]")
365
- .gsub(/\}\z/, "}\n")
406
+ .gsub(/\}\z/, "}\n"),
407
+ String
408
+ )
366
409
  end
367
410
  end
368
411
  content
369
412
  end
370
413
 
414
+ sig { params(content: String).returns(String) }
371
415
  def replace_content_hash(content)
372
416
  existing_hash = JSON.parse(content).fetch("content-hash")
373
417
  SharedHelpers.in_a_temporary_directory do
@@ -385,8 +429,9 @@ module Dependabot
385
429
  end
386
430
  end
387
431
 
432
+ sig { params(content: String).returns(String) }
388
433
  def replace_platform_overrides(content)
389
- original_object = JSON.parse(lockfile.content)
434
+ original_object = JSON.parse(T.must(lockfile.content))
390
435
  original_overrides = original_object.fetch("platform-overrides", nil)
391
436
 
392
437
  updated_object = JSON.parse(content)
@@ -402,6 +447,7 @@ module Dependabot
402
447
  .gsub(/\}\z/, "}\n")
403
448
  end
404
449
 
450
+ sig { params(requirements: T::Array[String]).returns(String) }
405
451
  def version_for_reqs(requirements)
406
452
  req_arrays =
407
453
  requirements
@@ -426,42 +472,60 @@ module Dependabot
426
472
  version.to_s
427
473
  end
428
474
 
475
+ sig { params(additional_extensions: T::Array[T::Hash[Symbol, String]]).void }
429
476
  def update_required_extensions(additional_extensions)
430
477
  additional_extensions.each do |ext|
431
478
  composer_platform_extensions[ext.fetch(:name)] ||= []
432
- composer_platform_extensions[ext.fetch(:name)] +=
433
- [ext.fetch(:requirement)]
479
+ existing_reqs = composer_platform_extensions[ext.fetch(:name)]
480
+ composer_platform_extensions[ext.fetch(:name)] =
481
+ T.must(existing_reqs) + [ext.fetch(:requirement)]
434
482
  composer_platform_extensions[ext.fetch(:name)] =
435
- composer_platform_extensions[ext.fetch(:name)].uniq
483
+ T.must(composer_platform_extensions[ext.fetch(:name)]).uniq
436
484
  end
437
485
  end
438
486
 
487
+ sig { returns(String) }
439
488
  def php_helper_path
440
489
  NativeHelpers.composer_helper_path(composer_version: composer_version)
441
490
  end
442
491
 
492
+ sig { params(content: String).returns(String) }
493
+ def post_process_lockfile(content)
494
+ content = replace_patches(content)
495
+ content = replace_content_hash(content)
496
+ replace_platform_overrides(content)
497
+ end
498
+
499
+ sig { returns(String) }
443
500
  def composer_version
444
- @composer_version ||= Helpers.composer_version(parsed_composer_json, parsed_lockfile)
501
+ @composer_version ||= T.let(
502
+ Helpers.composer_version(parsed_composer_json, parsed_lockfile),
503
+ T.nilable(String)
504
+ )
445
505
  end
446
506
 
507
+ sig { returns(T::Hash[String, String]) }
447
508
  def credentials_env
448
509
  credentials
449
510
  .select { |c| c.fetch("type") == "php_environment_variable" }
450
- .to_h { |cred| [cred["env-key"], cred.fetch("env-value", "-")] }
511
+ .to_h { |cred| [T.cast(cred["env-key"], String), cred.fetch("env-value", "-")] }
451
512
  end
452
513
 
514
+ sig { returns(T::Array[Dependabot::Credential]) }
453
515
  def git_credentials
454
516
  credentials
455
517
  .select { |cred| cred.fetch("type") == "git_source" }
456
518
  .select { |cred| cred["password"] }
457
519
  end
458
520
 
521
+ sig { returns(T::Array[Dependabot::Credential]) }
459
522
  def registry_credentials
460
523
  credentials
461
524
  .select { |cred| cred.fetch("type") == PackageManager::REPOSITORY_KEY }
462
525
  .select { |cred| cred["password"] }
463
526
  end
464
527
 
528
+ sig { returns(T::Hash[String, T::Array[String]]) }
465
529
  def initial_platform
466
530
  platform_php = Helpers.capture_platform_php(parsed_composer_json)
467
531
 
@@ -480,6 +544,7 @@ module Dependabot
480
544
  platform
481
545
  end
482
546
 
547
+ sig { params(req_string: String).returns(T::Boolean) }
483
548
  def requirement_valid?(req_string)
484
549
  Composer::Requirement.requirements_array(req_string)
485
550
  true
@@ -487,36 +552,60 @@ module Dependabot
487
552
  false
488
553
  end
489
554
 
555
+ sig { returns(T::Hash[String, T.untyped]) }
490
556
  def parsed_composer_json
491
- @parsed_composer_json ||= JSON.parse(composer_json.content)
557
+ @parsed_composer_json ||= T.let(
558
+ JSON.parse(T.must(composer_json.content)),
559
+ T.nilable(T::Hash[String, T.untyped])
560
+ )
492
561
  end
493
562
 
563
+ sig { returns(T::Hash[String, T.untyped]) }
494
564
  def parsed_lockfile
495
- @parsed_lockfile ||= JSON.parse(lockfile.content)
565
+ @parsed_lockfile ||= T.let(
566
+ JSON.parse(T.must(lockfile.content)),
567
+ T.nilable(T::Hash[String, T.untyped])
568
+ )
496
569
  end
497
570
 
571
+ sig { returns(Dependabot::DependencyFile) }
498
572
  def composer_json
499
- @composer_json ||=
500
- dependency_files.find { |f| f.name == PackageManager::MANIFEST_FILENAME }
573
+ @composer_json ||= T.let(
574
+ T.must(dependency_files.find { |f| f.name == PackageManager::MANIFEST_FILENAME }),
575
+ T.nilable(Dependabot::DependencyFile)
576
+ )
501
577
  end
502
578
 
579
+ sig { returns(Dependabot::DependencyFile) }
503
580
  def lockfile
504
- @lockfile ||=
505
- dependency_files.find { |f| f.name == PackageManager::LOCKFILE_FILENAME }
581
+ @lockfile ||= T.let(
582
+ T.must(dependency_files.find { |f| f.name == PackageManager::LOCKFILE_FILENAME }),
583
+ T.nilable(Dependabot::DependencyFile)
584
+ )
506
585
  end
507
586
 
587
+ sig { returns(T.nilable(Dependabot::DependencyFile)) }
508
588
  def auth_json
509
- @auth_json ||= dependency_files.find { |f| f.name == PackageManager::AUTH_FILENAME }
589
+ @auth_json ||= T.let(
590
+ dependency_files.find { |f| f.name == PackageManager::AUTH_FILENAME },
591
+ T.nilable(Dependabot::DependencyFile)
592
+ )
510
593
  end
511
594
 
595
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
512
596
  def artifact_dependencies
513
- @artifact_dependencies ||=
514
- dependency_files.select { |f| f.name.end_with?(".zip", ".gitkeep") }
597
+ @artifact_dependencies ||= T.let(
598
+ dependency_files.select { |f| f.name.end_with?(".zip", ".gitkeep") },
599
+ T.nilable(T::Array[Dependabot::DependencyFile])
600
+ )
515
601
  end
516
602
 
603
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
517
604
  def path_dependencies
518
- @path_dependencies ||=
519
- dependency_files.select { |f| f.name.end_with?("/#{PackageManager::MANIFEST_FILENAME}") }
605
+ @path_dependencies ||= T.let(
606
+ dependency_files.select { |f| f.name.end_with?("/#{PackageManager::MANIFEST_FILENAME}") },
607
+ T.nilable(T::Array[Dependabot::DependencyFile])
608
+ )
520
609
  end
521
610
  end
522
611
  end
@@ -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
 
@@ -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,6 +1,8 @@
1
- # typed: true
1
+ # typed: strong
2
2
  # frozen_string_literal: true
3
3
 
4
+ require "sorbet-runtime"
5
+
4
6
  require "dependabot/version"
5
7
  require "dependabot/utils"
6
8
 
@@ -11,11 +13,15 @@ require "dependabot/utils"
11
13
  module Dependabot
12
14
  module Composer
13
15
  class Version < Dependabot::Version
16
+ extend T::Sig
17
+
18
+ sig { override.params(version: VersionParameter).void }
14
19
  def initialize(version)
15
- @version_string = version.to_s
20
+ @version_string = T.let(version.to_s, String)
16
21
  super
17
22
  end
18
23
 
24
+ sig { returns(String) }
19
25
  def to_s
20
26
  @version_string
21
27
  end
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.310.0
4
+ version: 0.311.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dependabot
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-04-24 00:00:00.000000000 Z
10
+ date: 2025-05-01 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: dependabot-common
@@ -15,14 +15,14 @@ dependencies:
15
15
  requirements:
16
16
  - - '='
17
17
  - !ruby/object:Gem::Version
18
- version: 0.310.0
18
+ version: 0.311.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.310.0
25
+ version: 0.311.0
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: debug
28
28
  requirement: !ruby/object:Gem::Requirement
@@ -223,16 +223,16 @@ dependencies:
223
223
  name: webrick
224
224
  requirement: !ruby/object:Gem::Requirement
225
225
  requirements:
226
- - - ">="
226
+ - - "~>"
227
227
  - !ruby/object:Gem::Version
228
- version: '1.7'
228
+ version: '1.9'
229
229
  type: :development
230
230
  prerelease: false
231
231
  version_requirements: !ruby/object:Gem::Requirement
232
232
  requirements:
233
- - - ">="
233
+ - - "~>"
234
234
  - !ruby/object:Gem::Version
235
- version: '1.7'
235
+ version: '1.9'
236
236
  description: Dependabot-Composer provides support for bumping PHP (composer) libraries
237
237
  via Dependabot. If you want support for multiple package managers, you probably
238
238
  want the meta-gem dependabot-omnibus.
@@ -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.310.0
282
+ changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.311.0
283
283
  rdoc_options: []
284
284
  require_paths:
285
285
  - lib