dependabot-common 0.387.0 → 0.389.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.
@@ -2,6 +2,7 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "sorbet-runtime"
5
+ require "dependabot/error_details"
5
6
  require "dependabot/utils"
6
7
 
7
8
  # rubocop:disable Metrics/ModuleLength
@@ -23,7 +24,7 @@ module Dependabot
23
24
 
24
25
  # rubocop:disable Metrics/MethodLength
25
26
  # rubocop:disable Metrics/CyclomaticComplexity
26
- sig { params(error: StandardError).returns(T.nilable(T::Hash[Symbol, T.anything])) }
27
+ sig { params(error: StandardError).returns(T.nilable(Dependabot::ErrorDetails)) }
27
28
  def self.fetcher_error_details(error)
28
29
  case error
29
30
  when Dependabot::ToolVersionNotSupported
@@ -138,11 +139,12 @@ module Dependabot
138
139
  "rate-limit-reset": T.cast(error, Octokit::Error).response_headers["X-RateLimit-Reset"]
139
140
  }
140
141
  }
141
- end
142
+ end => details
143
+ Dependabot::ErrorDetails.from_hash(details) if details
142
144
  end
143
145
  # rubocop:enable Metrics/CyclomaticComplexity
144
146
 
145
- sig { params(error: StandardError).returns(T.nilable(T::Hash[Symbol, T.anything])) }
147
+ sig { params(error: StandardError).returns(T.nilable(Dependabot::ErrorDetails)) }
146
148
  def self.parser_error_details(error)
147
149
  case error
148
150
  when Dependabot::ToolFeatureNotSupported
@@ -220,13 +222,14 @@ module Dependabot
220
222
  # and responsibility for fixing it is on them, not us. As a result we
221
223
  # quietly log these as errors
222
224
  { "error-type": "server_error" }
223
- end
225
+ end => details
226
+ Dependabot::ErrorDetails.from_hash(details) if details
224
227
  end
225
228
 
226
229
  # rubocop:disable Lint/RedundantCopDisableDirective
227
230
  # rubocop:disable Metrics/CyclomaticComplexity
228
231
  # rubocop:disable Metrics/AbcSize
229
- sig { params(error: StandardError).returns(T.nilable(T::Hash[Symbol, T.anything])) }
232
+ sig { params(error: StandardError).returns(T.nilable(Dependabot::ErrorDetails)) }
230
233
  def self.updater_error_details(error)
231
234
  case error
232
235
  when Dependabot::ToolFeatureNotSupported
@@ -400,7 +403,8 @@ module Dependabot
400
403
  "rate-limit-reset": T.cast(error, Octokit::Error).response_headers["X-RateLimit-Reset"]
401
404
  }
402
405
  }
403
- end
406
+ end => details
407
+ Dependabot::ErrorDetails.from_hash(details) if details
404
408
  end
405
409
 
406
410
  # rubocop:enable Metrics/MethodLength
@@ -70,7 +70,7 @@ module Dependabot
70
70
  sig { returns(T.nilable(String)) }
71
71
  attr_reader :repo_contents_path
72
72
 
73
- sig { returns(T::Hash[String, String]) }
73
+ sig { returns(T::Hash[Symbol, Object]) }
74
74
  attr_reader :options
75
75
 
76
76
  CLIENT_NOT_FOUND_ERRORS = T.let(
@@ -135,7 +135,7 @@ module Dependabot
135
135
  source: Dependabot::Source,
136
136
  credentials: T::Array[Dependabot::Credential],
137
137
  repo_contents_path: T.nilable(String),
138
- options: T::Hash[String, String],
138
+ options: T::Hash[Symbol, Object],
139
139
  update_config: T.nilable(Dependabot::Config::UpdateConfig)
140
140
  )
141
141
  .void
@@ -1,4 +1,4 @@
1
- # typed: strict
1
+ # typed: strong
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "excon"
@@ -14,6 +14,7 @@ require "dependabot/utils"
14
14
  require "dependabot/source"
15
15
  require "dependabot/dependency"
16
16
  require "dependabot/credential"
17
+ require "dependabot/clients/github_release"
17
18
  require "dependabot/git_metadata_fetcher"
18
19
  require "dependabot/git_commit_checker/source_details"
19
20
  require "dependabot/git_tag_details"
@@ -485,7 +486,9 @@ module Dependabot
485
486
  def handle_tag_prefix(tags)
486
487
  if dependency_source_details&.ref&.start_with?("tags/")
487
488
  tags = tags.map do |tag|
488
- tag.dup.tap { |t| t.name = "tags/#{tag.name}" }
489
+ duplicated_tag = tag.dup
490
+ duplicated_tag.name = "tags/#{tag.name}"
491
+ duplicated_tag
489
492
  end
490
493
  end
491
494
 
@@ -524,8 +527,11 @@ module Dependabot
524
527
  client = Clients::GithubWithRetries
525
528
  .for_github_dot_com(credentials: credentials)
526
529
 
527
- # TODO: create this method instead of relying on method_missing
528
- T.unsafe(client.compare(T.must(listing_source_repo), ref1, ref2)).status
530
+ comparison = client.compare(T.must(listing_source_repo), ref1, ref2)
531
+ status = T.cast(comparison[:status], Object)
532
+ raise TypeError, "GitHub comparison status must be a string" unless status.is_a?(String)
533
+
534
+ status
529
535
  end
530
536
 
531
537
  sig { params(ref1: String, ref2: String).returns(String) }
@@ -534,9 +540,11 @@ module Dependabot
534
540
  .for_gitlab_dot_com(credentials: credentials)
535
541
 
536
542
  comparison = client.compare(T.must(listing_source_repo), ref1, ref2)
543
+ commits = T.cast(comparison["commits"], Object)
544
+ raise TypeError, "GitLab comparison commits must be an array" unless commits.is_a?(Array)
537
545
 
538
- if T.unsafe(comparison).commits.none? then "behind"
539
- elsif T.unsafe(comparison).compare_same_ref then "identical"
546
+ if commits.empty? then "behind"
547
+ elsif T.cast(comparison["compare_same_ref"], Object) == true then "identical"
540
548
  else
541
549
  "ahead"
542
550
  end
@@ -555,7 +563,13 @@ module Dependabot
555
563
 
556
564
  # Conservatively assume that ref2 is ahead in the equality case, of
557
565
  # if we get an unexpected format (e.g., due to a 404)
558
- if JSON.parse(response.body).fetch("values", ["x"]).none? then "behind"
566
+ comparison = T.cast(JSON.parse(response.body), Object)
567
+ raise TypeError, "Bitbucket comparison must be a hash" unless comparison.is_a?(Hash)
568
+
569
+ values = T.cast(comparison.fetch("values", ["x"]), Object)
570
+ raise TypeError, "Bitbucket comparison values must be an array" unless values.is_a?(Array)
571
+
572
+ if values.empty? then "behind"
559
573
  else
560
574
  "ahead"
561
575
  end
@@ -674,7 +688,9 @@ module Dependabot
674
688
 
675
689
  if dependency_source_details&.ref&.start_with?("tags/")
676
690
  tags = tags.map do |tag|
677
- tag.dup.tap { |t| t.name = "tags/#{tag.name}" }
691
+ duplicated_tag = tag.dup
692
+ duplicated_tag.name = "tags/#{tag.name}"
693
+ duplicated_tag
678
694
  end
679
695
  end
680
696
 
@@ -737,7 +753,7 @@ module Dependabot
737
753
  false
738
754
  end
739
755
 
740
- sig { returns(T::Array[T.untyped]) }
756
+ sig { returns(T::Array[Dependabot::Clients::GithubRelease]) }
741
757
  def github_releases
742
758
  @github_releases ||= T.let(
743
759
  begin
@@ -752,11 +768,17 @@ module Dependabot
752
768
  )
753
769
  # The Octokit RBI types this as non-nil, but it can be nil at runtime
754
770
  # (e.g. an empty/unexpected registry response), so guard against it.
755
- T.unsafe(client.releases(T.must(source).repo, per_page: 100)) || []
771
+ releases = T.let(
772
+ client.releases(T.must(source).repo, per_page: 100),
773
+ T.nilable(T::Array[Sawyer::Resource])
774
+ )
775
+ (releases || []).filter_map do |release|
776
+ Dependabot::Clients::GithubRelease.from_resource(release)
777
+ end
756
778
  rescue Octokit::Error
757
779
  []
758
780
  end,
759
- T.nilable(T::Array[T.untyped])
781
+ T.nilable(T::Array[Dependabot::Clients::GithubRelease])
760
782
  )
761
783
  end
762
784
 
@@ -1,7 +1,8 @@
1
- # typed: strict
1
+ # typed: strong
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "sorbet-runtime"
5
+ require "dependabot/clients/github_release"
5
6
  require "dependabot/clients/github_with_retries"
6
7
  require "dependabot/shared_helpers"
7
8
  require "dependabot/source"
@@ -45,7 +46,8 @@ module Dependabot
45
46
  releases = cached_github_releases
46
47
  unless releases.empty?
47
48
  release = releases.find { |r| r.tag_name == tag_name }
48
- return release.published_at if release&.published_at
49
+ published_at = release&.published_at
50
+ return published_at if published_at
49
51
  end
50
52
 
51
53
  tag_creation_date(tag_name, commit_sha)
@@ -88,7 +90,7 @@ module Dependabot
88
90
 
89
91
  # Fetches and caches GitHub releases for the dependency source.
90
92
  # Returns an empty array for non-GitHub sources.
91
- sig { returns(T::Array[T.untyped]) } # rubocop:disable Sorbet/ForbidTUntyped
93
+ sig { returns(T::Array[Dependabot::Clients::GithubRelease]) }
92
94
  def cached_github_releases
93
95
  @cached_github_releases ||= T.let(
94
96
  begin
@@ -99,7 +101,13 @@ module Dependabot
99
101
  source: T.must(source),
100
102
  credentials: cooldown_credentials
101
103
  )
102
- client.releases(T.must(source).repo, per_page: 100)
104
+ releases = T.let(
105
+ client.releases(T.must(source).repo, per_page: 100),
106
+ T.nilable(T::Array[Sawyer::Resource])
107
+ )
108
+ (releases || []).filter_map do |release|
109
+ Dependabot::Clients::GithubRelease.from_resource(release)
110
+ end
103
111
  else
104
112
  []
105
113
  end
@@ -107,7 +115,7 @@ module Dependabot
107
115
  Dependabot.logger.debug("Error fetching GitHub releases: #{e.message}")
108
116
  []
109
117
  end,
110
- T.nilable(T::Array[T.untyped]) # rubocop:disable Sorbet/ForbidTUntyped
118
+ T.nilable(T::Array[Dependabot::Clients::GithubRelease])
111
119
  )
112
120
  end
113
121
  end
@@ -1,9 +1,11 @@
1
- # typed: strict
1
+ # typed: strong
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "sorbet-runtime"
5
+ require "time"
5
6
 
6
7
  require "dependabot/credential"
8
+ require "dependabot/clients/github_release"
7
9
  require "dependabot/clients/github_with_retries"
8
10
  require "dependabot/clients/gitlab_with_retries"
9
11
  require "dependabot/metadata_finders/base"
@@ -12,40 +14,62 @@ require "dependabot/utils"
12
14
  module Dependabot
13
15
  module MetadataFinders
14
16
  class Base
15
- class GitLabRelease
17
+ class GitLabRelease < T::ImmutableStruct
16
18
  extend T::Sig
17
19
 
18
- sig { returns(String) }
19
- attr_reader :name
20
-
21
- sig { returns(String) }
22
- attr_reader :tag_name
23
-
24
- sig { returns(String) }
25
- attr_reader :body
26
-
27
- sig { returns(String) }
28
- attr_reader :html_url
20
+ const :name, String
21
+ const :tag_name, String
22
+ const :body, T.nilable(String)
23
+ const :html_url, String
24
+ const :authored_at, Time
29
25
 
30
26
  sig do
31
27
  params(
32
- name: String,
33
- tag_name: String,
34
- body: String,
35
- html_url: String
36
- ).void
37
- end
38
- def initialize(name:, tag_name:, body:, html_url:)
39
- @name = name
40
- @tag_name = tag_name
41
- @body = body
42
- @html_url = html_url
28
+ tag: Gitlab::ObjectifiedHash,
29
+ source_url: String
30
+ ).returns(T.nilable(GitLabRelease))
43
31
  end
32
+ def self.from_tag(tag, source_url:)
33
+ name = T.cast(tag["name"], Object)
34
+ release = T.cast(tag["release"], Object)
35
+ commit = T.cast(tag["commit"], Object)
36
+ return unless name.is_a?(String)
37
+ return unless release.is_a?(Gitlab::ObjectifiedHash)
38
+ return unless commit.is_a?(Gitlab::ObjectifiedHash)
39
+
40
+ tag_name = T.cast(release["tag_name"], Object)
41
+ return unless tag_name.is_a?(String)
42
+
43
+ authored_at = time_value(T.cast(commit["authored_date"], Object))
44
+ return unless authored_at
45
+
46
+ description = T.cast(release["description"], Object)
47
+ new(
48
+ name: name,
49
+ tag_name: tag_name,
50
+ body: description.is_a?(String) ? description : nil,
51
+ html_url: "#{source_url}/tags/#{name}",
52
+ authored_at: authored_at
53
+ )
54
+ end
55
+
56
+ sig { params(value: Object).returns(T.nilable(Time)) }
57
+ def self.time_value(value)
58
+ return value if value.is_a?(Time)
59
+ return unless value.is_a?(String)
60
+
61
+ Time.parse(value)
62
+ rescue ArgumentError
63
+ nil
64
+ end
65
+ private_class_method :time_value
44
66
  end
45
67
 
46
68
  class ReleaseFinder
47
69
  extend T::Sig
48
70
 
71
+ ReleaseType = T.type_alias { T.any(Dependabot::Clients::GithubRelease, GitLabRelease) }
72
+
49
73
  sig { returns(Dependabot::Dependency) }
50
74
  attr_reader :dependency
51
75
 
@@ -98,14 +122,13 @@ module Dependabot
98
122
 
99
123
  private
100
124
 
101
- sig { returns(T::Array[T.untyped]) }
125
+ sig { returns(T::Array[ReleaseType]) }
102
126
  def all_dep_releases
103
127
  releases = all_releases
104
128
  dep_prefix = dependency.name.downcase
105
129
 
106
130
  releases_with_dependency_name =
107
131
  releases
108
- .reject { |r| r.tag_name.nil? }
109
132
  .select { |r| r.tag_name.start_with?(dep_prefix) }
110
133
 
111
134
  return releases unless releases_with_dependency_name.any?
@@ -113,12 +136,12 @@ module Dependabot
113
136
  releases_with_dependency_name
114
137
  end
115
138
 
116
- sig { returns(T::Array[T.untyped]) }
139
+ sig { returns(T::Array[ReleaseType]) }
117
140
  def all_releases
118
- @all_releases ||= T.let(fetch_dependency_releases, T.nilable(T::Array[T.untyped]))
141
+ @all_releases ||= T.let(fetch_dependency_releases, T.nilable(T::Array[ReleaseType]))
119
142
  end
120
143
 
121
- sig { returns(T.nilable(T::Array[T.untyped])) }
144
+ sig { returns(T.nilable(T::Array[ReleaseType])) }
122
145
  def relevant_releases
123
146
  releases = releases_since_previous_version
124
147
 
@@ -139,7 +162,7 @@ module Dependabot
139
162
  end
140
163
  end
141
164
 
142
- sig { returns(T.nilable(T::Array[T.untyped])) }
165
+ sig { returns(T.nilable(T::Array[ReleaseType])) }
143
166
  def releases_since_previous_version
144
167
  return [updated_release].compact unless previous_version
145
168
 
@@ -158,21 +181,21 @@ module Dependabot
158
181
  end
159
182
  end
160
183
 
161
- sig { params(releases: T::Array[T.untyped]).returns(T.nilable(T::Array[T.untyped])) }
184
+ sig { params(releases: T::Array[ReleaseType]).returns(T.nilable(T::Array[ReleaseType])) }
162
185
  def filter_releases_using_previous_release(releases)
163
186
  return releases if releases.index(previous_release).nil?
164
187
 
165
188
  releases.first(T.must(releases.index(previous_release)))
166
189
  end
167
190
 
168
- sig { params(releases: T::Array[T.untyped]).returns(T.nilable(T::Array[T.untyped])) }
191
+ sig { params(releases: T::Array[ReleaseType]).returns(T.nilable(T::Array[ReleaseType])) }
169
192
  def filter_releases_using_updated_release(releases)
170
193
  return releases if releases.index(updated_release).nil?
171
194
 
172
195
  releases[releases.index(updated_release)..-1]
173
196
  end
174
197
 
175
- sig { params(releases: T::Array[T.untyped], conservative: T::Boolean).returns(T::Array[T.untyped]) }
198
+ sig { params(releases: T::Array[ReleaseType], conservative: T::Boolean).returns(T::Array[ReleaseType]) }
176
199
  def filter_releases_using_previous_version(releases, conservative:)
177
200
  releases.reject do |release|
178
201
  cleaned_tag = release.tag_name.gsub(/^[^0-9]*/, "")
@@ -193,7 +216,7 @@ module Dependabot
193
216
  end
194
217
  end
195
218
 
196
- sig { params(releases: T::Array[T.untyped], conservative: T::Boolean).returns(T::Array[T.untyped]) }
219
+ sig { params(releases: T::Array[ReleaseType], conservative: T::Boolean).returns(T::Array[ReleaseType]) }
197
220
  def filter_releases_using_updated_version(releases, conservative:)
198
221
  updated_version = version_class.new(new_version)
199
222
 
@@ -216,17 +239,17 @@ module Dependabot
216
239
  end
217
240
  end
218
241
 
219
- sig { returns(T.untyped) }
242
+ sig { returns(T.nilable(ReleaseType)) }
220
243
  def updated_release
221
244
  release_for_version(new_version)
222
245
  end
223
246
 
224
- sig { returns(T.untyped) }
247
+ sig { returns(T.nilable(ReleaseType)) }
225
248
  def previous_release
226
249
  release_for_version(previous_version)
227
250
  end
228
251
 
229
- sig { params(version: T.nilable(String)).returns(T.untyped) }
252
+ sig { params(version: T.nilable(String)).returns(T.nilable(ReleaseType)) }
230
253
  def release_for_version(version)
231
254
  return nil unless version
232
255
 
@@ -236,22 +259,23 @@ module Dependabot
236
259
  all_dep_releases.find { |r| release_regex.match?(r.name.to_s) }
237
260
  end
238
261
 
239
- sig { params(release: T.untyped).returns(String) }
262
+ sig { params(release: ReleaseType).returns(String) }
240
263
  def serialize_release(release)
241
- rel = release
242
- title = "## #{rel.name.to_s == '' ? rel.tag_name : rel.name}\n"
243
- body = if rel.body.to_s.gsub(/\n*\z/m, "") == ""
264
+ name = release.name
265
+ title = "## #{name.to_s == '' ? release.tag_name : name}\n"
266
+ body = if release.body.to_s.gsub(/\n*\z/m, "") == ""
244
267
  "No release notes provided."
245
268
  else
246
- rel.body.gsub(/\n*\z/m, "")
269
+ T.must(release.body).gsub(/\n*\z/m, "")
247
270
  end
248
271
 
249
- release_body_includes_title?(rel) ? body : title + body
272
+ release_body_includes_title?(release) ? body : title + body
250
273
  end
251
274
 
252
- sig { params(release: T.untyped).returns(T::Boolean) }
275
+ sig { params(release: ReleaseType).returns(T::Boolean) }
253
276
  def release_body_includes_title?(release)
254
- title = release.name.to_s == "" ? release.tag_name : release.name
277
+ name = release.name
278
+ title = name.nil? || name.empty? ? release.tag_name : name
255
279
  release.body.to_s.match?(/\A\s*\#*\s*#{Regexp.quote(title)}/m)
256
280
  end
257
281
 
@@ -265,7 +289,7 @@ module Dependabot
265
289
  dependency.version_class
266
290
  end
267
291
 
268
- sig { returns(T::Array[T.untyped]) }
292
+ sig { returns(T::Array[ReleaseType]) }
269
293
  def fetch_dependency_releases
270
294
  return [] unless source
271
295
 
@@ -279,47 +303,45 @@ module Dependabot
279
303
  end
280
304
  end
281
305
 
282
- sig { returns(T::Array[T.untyped]) }
306
+ sig { returns(T::Array[Dependabot::Clients::GithubRelease]) }
283
307
  def fetch_github_releases
284
- releases = github_client.releases(T.must(source).repo, per_page: 100)
285
-
286
- # Remove any releases without a tag name. These are draft releases and
287
- # aren't yet associated with a tag, so shouldn't be used.
288
- releases = releases.reject { |r| T.unsafe(r).tag_name.nil? }
308
+ releases = parsed_github_releases
289
309
 
290
310
  clean_release_names =
291
- releases.map { |r| T.unsafe(r).tag_name.gsub(/^[^0-9\.]*/, "") }
311
+ releases.map { |release| release.tag_name.gsub(/^[^0-9\.]*/, "") }
292
312
 
293
313
  if clean_release_names.all? { |nm| version_class.correct?(nm) }
294
- releases.sort_by do |r|
295
- version_class.new(T.unsafe(r).tag_name.gsub(/^[^0-9\.]*/, ""))
314
+ releases.sort_by do |release|
315
+ version_class.new(release.tag_name.gsub(/^[^0-9\.]*/, ""))
296
316
  end.reverse
297
317
  else
298
- releases.sort_by { |r| T.unsafe(r).id }.reverse
318
+ releases.sort_by { |release| release.id || 0 }.reverse
299
319
  end
300
320
  rescue Octokit::NotFound, Octokit::UnavailableForLegalReasons
301
321
  []
302
322
  end
303
323
 
304
- sig { returns(T::Array[T.untyped]) }
324
+ sig { returns(T::Array[Dependabot::Clients::GithubRelease]) }
325
+ def parsed_github_releases
326
+ resources = T.let(
327
+ github_client.releases(T.must(source).repo, per_page: 100),
328
+ T.nilable(T::Array[Sawyer::Resource])
329
+ )
330
+ releases = (resources || []).filter_map do |release|
331
+ Dependabot::Clients::GithubRelease.from_resource(release)
332
+ end
333
+ releases
334
+ end
335
+
336
+ sig { returns(T::Array[GitLabRelease]) }
305
337
  def fetch_gitlab_releases
306
- releases =
307
- T.unsafe(
308
- gitlab_client
309
- .tags(T.must(source).repo)
310
- )
311
- .select(&:release)
312
- .sort_by { |r| T.unsafe(r).commit.authored_date }
313
- .reverse
314
-
315
- releases.map do |tag|
316
- GitLabRelease.new(
317
- name: T.unsafe(tag).name,
318
- tag_name: T.unsafe(tag).release.tag_name,
319
- body: T.unsafe(tag).release.description,
320
- html_url: "#{T.must(source).url}/tags/#{T.unsafe(tag).name}"
321
- )
338
+ tags = gitlab_client.tags(T.must(source).repo)
339
+ releases = tags.filter_map do |tag|
340
+ next unless tag.is_a?(Gitlab::ObjectifiedHash)
341
+
342
+ GitLabRelease.from_tag(tag, source_url: T.must(source).url)
322
343
  end
344
+ releases.sort_by(&:authored_at).reverse
323
345
  rescue Gitlab::Error::NotFound
324
346
  []
325
347
  end
@@ -359,20 +381,32 @@ module Dependabot
359
381
 
360
382
  sig { returns(T.nilable(String)) }
361
383
  def previous_ref
362
- previous_refs = T.must(dependency.previous_requirements).filter_map do |r|
363
- r.dig(:source, "ref") || r.dig(:source, :ref)
384
+ previous_refs = T.must(dependency.previous_requirements).filter_map do |requirement|
385
+ requirement_ref(requirement)
364
386
  end.uniq
365
387
  previous_refs.first if previous_refs.one?
366
388
  end
367
389
 
368
390
  sig { returns(T.nilable(String)) }
369
391
  def new_ref
370
- new_refs = dependency.requirements.filter_map do |r|
371
- r.dig(:source, "ref") || r.dig(:source, :ref)
392
+ new_refs = dependency.requirements.filter_map do |requirement|
393
+ requirement_ref(requirement)
372
394
  end.uniq
373
395
  new_refs.first if new_refs.one?
374
396
  end
375
397
 
398
+ sig { params(requirement: Dependabot::DependencyRequirement).returns(T.nilable(String)) }
399
+ def requirement_ref(requirement)
400
+ source = requirement.source
401
+ return unless source.is_a?(Hash)
402
+
403
+ symbol_ref = source[:ref]
404
+ return symbol_ref if symbol_ref.is_a?(String)
405
+
406
+ string_ref = source["ref"]
407
+ string_ref if string_ref.is_a?(String)
408
+ end
409
+
376
410
  sig { returns(T::Boolean) }
377
411
  def ref_changed?
378
412
  # We could go from multiple previous refs (nil) to a single new ref
@@ -1,4 +1,4 @@
1
- # typed: strict
1
+ # typed: strong
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "sorbet-runtime"
@@ -279,7 +279,7 @@ module Dependabot
279
279
  sig { returns(String) }
280
280
  def dependency_name_group_pr_name
281
281
  dep = T.must(dependencies.first)
282
- directories = dep.metadata[:updated_directories] || [dep.metadata[:directory]].compact
282
+ directories = dep.metadata_string_array(:updated_directories) || [dep.metadata_string(:directory)].compact
283
283
 
284
284
  if directories.count > 1
285
285
  "bump #{dep.name} across #{directories.count} directories"
@@ -571,7 +571,7 @@ module Dependabot
571
571
  sig { returns(String) }
572
572
  def dependency_name_group_intro
573
573
  dep = T.must(dependencies.first)
574
- directories = dep.metadata[:updated_directories] || [dep.metadata[:directory]].compact
574
+ directories = dep.metadata_string_array(:updated_directories) || [dep.metadata_string(:directory)].compact
575
575
 
576
576
  msg = "Bumps #{dependency_links.first}"
577
577
 
@@ -801,6 +801,9 @@ module Dependabot
801
801
  vulnerabilities_fixed: vulnerabilities_fixed[dependency.name],
802
802
  github_redirection_service: github_redirection_service
803
803
  ).to_s
804
+ rescue StandardError => e
805
+ suppress_error("metadata cascades for #{dependency.name}", e)
806
+ ""
804
807
  end
805
808
 
806
809
  sig { returns(String) }
@@ -24,6 +24,15 @@ module Dependabot
24
24
  T.cast(super, Dependabot::Version)
25
25
  end
26
26
 
27
+ sig do
28
+ overridable
29
+ .params(_from_version: String, _to_version: String)
30
+ .returns(T.nilable(String))
31
+ end
32
+ def self.update_type(_from_version, _to_version)
33
+ nil
34
+ end
35
+
27
36
  # Opt-in to Rubygems 4 behavior
28
37
  sig { override.overridable.params(version: VersionParameter).returns(T::Boolean) }
29
38
  def self.correct?(version)
data/lib/dependabot.rb CHANGED
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Dependabot
5
- VERSION = "0.387.0"
5
+ VERSION = "0.389.0"
6
6
  end