dependabot-common 0.242.1 → 0.243.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/lib/dependabot/clients/azure.rb +112 -24
  3. data/lib/dependabot/clients/bitbucket.rb +1 -1
  4. data/lib/dependabot/credential.rb +40 -0
  5. data/lib/dependabot/dependency_group.rb +8 -2
  6. data/lib/dependabot/file_fetchers/base.rb +35 -3
  7. data/lib/dependabot/file_parsers/base.rb +4 -3
  8. data/lib/dependabot/file_updaters/artifact_updater.rb +1 -1
  9. data/lib/dependabot/file_updaters/base.rb +4 -3
  10. data/lib/dependabot/file_updaters/vendor_updater.rb +1 -1
  11. data/lib/dependabot/git_commit_checker.rb +3 -2
  12. data/lib/dependabot/git_metadata_fetcher.rb +3 -2
  13. data/lib/dependabot/metadata_finders/base/changelog_finder.rb +151 -56
  14. data/lib/dependabot/metadata_finders/base/changelog_pruner.rb +45 -14
  15. data/lib/dependabot/metadata_finders/base/commits_finder.rb +123 -53
  16. data/lib/dependabot/metadata_finders/base/release_finder.rb +84 -31
  17. data/lib/dependabot/metadata_finders/base.rb +4 -3
  18. data/lib/dependabot/pull_request_creator/azure.rb +1 -1
  19. data/lib/dependabot/pull_request_creator/branch_namer/solo_strategy.rb +2 -2
  20. data/lib/dependabot/pull_request_creator/labeler.rb +3 -2
  21. data/lib/dependabot/pull_request_creator/message_builder/issue_linker.rb +14 -6
  22. data/lib/dependabot/pull_request_creator/message_builder/metadata_presenter.rb +50 -9
  23. data/lib/dependabot/pull_request_creator/message_builder.rb +2 -2
  24. data/lib/dependabot/pull_request_creator/pr_name_prefixer.rb +164 -58
  25. data/lib/dependabot/pull_request_creator.rb +6 -5
  26. data/lib/dependabot/pull_request_updater.rb +3 -2
  27. data/lib/dependabot/security_advisory.rb +2 -2
  28. data/lib/dependabot/shared_helpers.rb +3 -2
  29. data/lib/dependabot/utils.rb +1 -13
  30. data/lib/dependabot/workspace/git.rb +1 -1
  31. data/lib/dependabot.rb +1 -1
  32. metadata +4 -3
@@ -1,6 +1,9 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
+ require "sorbet-runtime"
5
+
6
+ require "dependabot/credential"
4
7
  require "dependabot/clients/github_with_retries"
5
8
  require "dependabot/clients/gitlab_with_retries"
6
9
  require "dependabot/metadata_finders/base"
@@ -10,41 +13,61 @@ module Dependabot
10
13
  module MetadataFinders
11
14
  class Base
12
15
  class ReleaseFinder
13
- attr_reader :dependency, :credentials, :source
16
+ extend T::Sig
17
+
18
+ sig { returns(Dependabot::Dependency) }
19
+ attr_reader :dependency
20
+
21
+ sig { returns(T::Array[Dependabot::Credential]) }
22
+ attr_reader :credentials
23
+
24
+ sig { returns(T.nilable(Dependabot::Source)) }
25
+ attr_reader :source
14
26
 
27
+ sig do
28
+ params(
29
+ source: T.nilable(Dependabot::Source),
30
+ dependency: Dependabot::Dependency,
31
+ credentials: T::Array[Dependabot::Credential]
32
+ )
33
+ .void
34
+ end
15
35
  def initialize(source:, dependency:, credentials:)
16
36
  @source = source
17
37
  @dependency = dependency
18
38
  @credentials = credentials
19
39
  end
20
40
 
41
+ sig { returns(T.nilable(String)) }
21
42
  def releases_url
22
43
  return unless source
23
44
 
24
45
  # Azure does not provide tags via API, so we can't check whether
25
46
  # there are any releases. So, optimistically return the tags location
26
- return "#{source.url}/tags" if source.provider == "azure"
47
+ return "#{T.must(source).url}/tags" if T.must(source).provider == "azure"
27
48
 
28
49
  # If there are no releases, we won't be linking to the releases page
29
50
  return unless all_releases.any?
30
51
 
31
- case source.provider
32
- when "github" then "#{source.url}/releases"
33
- when "gitlab" then "#{source.url}/tags"
52
+ case T.must(source).provider
53
+ when "github" then "#{T.must(source).url}/releases"
54
+ when "gitlab" then "#{T.must(source).url}/tags"
34
55
  when "bitbucket", "codecommit" then nil
35
- else raise "Unexpected repo provider '#{source.provider}'"
56
+ else raise "Unexpected repo provider '#{T.must(source).provider}'"
36
57
  end
37
58
  end
38
59
 
60
+ sig { returns(T.nilable(String)) }
39
61
  def releases_text
40
- return unless relevant_releases.any?
41
- return if relevant_releases.all? { |r| r.body.nil? || r.body == "" }
62
+ return unless relevant_releases&.any?
63
+ return if relevant_releases&.all? { |r| r.body.nil? || r.body == "" }
42
64
 
43
- relevant_releases.map { |r| serialize_release(r) }.join("\n\n")
65
+ relevant_releases&.map { |r| serialize_release(r) }&.join("\n\n")
44
66
  end
45
67
 
46
68
  private
47
69
 
70
+ sig { returns(T::Array[T.untyped]) }
48
71
  def all_dep_releases
49
72
  releases = all_releases
50
73
  dep_prefix = dependency.name.downcase
@@ -59,21 +82,23 @@ module Dependabot
59
82
  releases_with_dependency_name
60
83
  end
61
84
 
85
+ sig { returns(T::Array[T.untyped]) }
62
86
  def all_releases
63
- @all_releases ||= fetch_dependency_releases
87
+ @all_releases ||= T.let(fetch_dependency_releases, T.nilable(T::Array[T.untyped]))
64
88
  end
65
89
 
90
+ sig { returns(T.nilable(T::Array[T.untyped])) }
66
91
  def relevant_releases
67
92
  releases = releases_since_previous_version
68
93
 
69
94
  # Sometimes we can't filter the releases properly (if they're
70
95
  # prefixed by a number that gets confused with the version). In this
71
96
  # case, the best we can do is return nil.
72
- return [] unless releases.any?
97
+ return [] unless !releases.nil? && releases.any?
73
98
 
74
99
  if updated_release && version_class.correct?(new_version)
75
100
  releases = filter_releases_using_updated_release(releases)
76
- filter_releases_using_updated_version(releases, conservative: true)
101
+ filter_releases_using_updated_version(T.must(releases), conservative: true)
77
102
  elsif updated_release
78
103
  filter_releases_using_updated_release(releases)
79
104
  elsif version_class.correct?(new_version)
@@ -83,12 +108,13 @@ module Dependabot
83
108
  end
84
109
  end
85
110
 
111
+ sig { returns(T.nilable(T::Array[T.untyped])) }
86
112
  def releases_since_previous_version
87
113
  return [updated_release].compact unless previous_version
88
114
 
89
115
  if previous_release && version_class.correct?(previous_version)
90
116
  releases = filter_releases_using_previous_release(all_dep_releases)
91
- filter_releases_using_previous_version(releases, conservative: true)
117
+ filter_releases_using_previous_version(T.must(releases), conservative: true)
92
118
  elsif previous_release
93
119
  filter_releases_using_previous_release(all_dep_releases)
94
120
  elsif version_class.correct?(previous_version)
@@ -101,18 +127,21 @@ module Dependabot
101
127
  end
102
128
  end
103
129
 
130
+ sig { params(releases: T::Array[T.untyped]).returns(T.nilable(T::Array[T.untyped])) }
104
131
  def filter_releases_using_previous_release(releases)
105
132
  return releases if releases.index(previous_release).nil?
106
133
 
107
- releases.first(releases.index(previous_release))
134
+ releases.first(T.must(releases.index(previous_release)))
108
135
  end
109
136
 
137
+ sig { params(releases: T::Array[T.untyped]).returns(T.nilable(T::Array[T.untyped])) }
110
138
  def filter_releases_using_updated_release(releases)
111
139
  return releases if releases.index(updated_release).nil?
112
140
 
113
141
  releases[releases.index(updated_release)..-1]
114
142
  end
115
143
 
144
+ sig { params(releases: T::Array[T.untyped], conservative: T::Boolean).returns(T::Array[T.untyped]) }
116
145
  def filter_releases_using_previous_version(releases, conservative:)
117
146
  releases.reject do |release|
118
147
  cleaned_tag = release.tag_name.gsub(/^[^0-9]*/, "")
@@ -133,6 +162,7 @@ module Dependabot
133
162
  end
134
163
  end
135
164
 
165
+ sig { params(releases: T::Array[T.untyped], conservative: T::Boolean).returns(T::Array[T.untyped]) }
136
166
  def filter_releases_using_updated_version(releases, conservative:)
137
167
  updated_version = version_class.new(new_version)
138
168
 
@@ -155,14 +185,17 @@ module Dependabot
155
185
  end
156
186
  end
157
187
 
188
+ sig { returns(T.untyped) }
158
189
  def updated_release
159
190
  release_for_version(new_version)
160
191
  end
161
192
 
193
+ sig { returns(T.untyped) }
162
194
  def previous_release
163
195
  release_for_version(previous_version)
164
196
  end
165
197
 
198
+ sig { params(version: T.nilable(String)).returns(T.untyped) }
166
199
  def release_for_version(version)
167
200
  return nil unless version
168
201
 
@@ -172,6 +205,7 @@ module Dependabot
172
205
  all_dep_releases.find { |r| release_regex.match?(r.name.to_s) }
173
206
  end
174
207
 
208
+ sig { params(release: T.untyped).returns(String) }
175
209
  def serialize_release(release)
176
210
  rel = release
177
211
  title = "## #{rel.name.to_s == '' ? rel.tag_name : rel.name}\n"
@@ -184,34 +218,39 @@ module Dependabot
184
218
  release_body_includes_title?(rel) ? body : title + body
185
219
  end
186
220
 
221
+ sig { params(release: T.untyped).returns(T::Boolean) }
187
222
  def release_body_includes_title?(release)
188
223
  title = release.name.to_s == "" ? release.tag_name : release.name
189
224
  release.body.to_s.match?(/\A\s*\#*\s*#{Regexp.quote(title)}/m)
190
225
  end
191
226
 
227
+ sig { params(version: T.nilable(String)).returns(Regexp) }
192
228
  def version_regex(version)
193
229
  /(?:[^0-9\.]|\A)#{Regexp.escape(version || 'unknown')}\z/
194
230
  end
195
231
 
232
+ sig { returns(T.class_of(Dependabot::Version)) }
196
233
  def version_class
197
234
  dependency.version_class
198
235
  end
199
236
 
237
+ sig { returns(T::Array[T.untyped]) }
200
238
  def fetch_dependency_releases
201
239
  return [] unless source
202
240
 
203
- case source.provider
241
+ case T.must(source).provider
204
242
  when "github" then fetch_github_releases
205
243
  # Bitbucket and CodeCommit don't support releases and
206
244
  # Azure can't list API for annotated tags
207
245
  when "bitbucket", "azure", "codecommit" then []
208
246
  when "gitlab" then fetch_gitlab_releases
209
- else raise "Unexpected repo provider '#{source.provider}'"
247
+ else raise "Unexpected repo provider '#{T.must(source).provider}'"
210
248
  end
211
249
  end
212
250
 
251
+ sig { returns(T::Array[T.untyped]) }
213
252
  def fetch_github_releases
214
- releases = github_client.releases(source.repo, per_page: 100)
253
+ releases = T.unsafe(github_client).releases(T.must(source).repo, per_page: 100)
215
254
 
216
255
  # Remove any releases without a tag name. These are draft releases and
217
256
  # aren't yet associated with a tag, so shouldn't be used.
@@ -231,26 +270,28 @@ module Dependabot
231
270
  []
232
271
  end
233
272
 
273
+ sig { returns(T::Array[T.untyped]) }
234
274
  def fetch_gitlab_releases
235
275
  releases =
236
- gitlab_client
237
- .tags(source.repo)
238
- .select(&:release)
239
- .sort_by { |r| r.commit.authored_date }
240
- .reverse
276
+ T.unsafe(gitlab_client)
277
+ .tags(T.must(source).repo)
278
+ .select(&:release)
279
+ .sort_by { |r| r.commit.authored_date }
280
+ .reverse
241
281
 
242
282
  releases.map do |tag|
243
283
  OpenStruct.new(
244
284
  name: tag.name,
245
285
  tag_name: tag.release.tag_name,
246
286
  body: tag.release.description,
247
- html_url: "#{source.url}/tags/#{tag.name}"
287
+ html_url: "#{T.must(source).url}/tags/#{tag.name}"
248
288
  )
249
289
  end
250
290
  rescue Gitlab::Error::NotFound
251
291
  []
252
292
  end
253
293
 
294
+ sig { returns(T.nilable(String)) }
254
295
  def previous_version
255
296
  # If we don't have a previous version, we *may* still be able to
256
297
  # figure one out if a ref was provided and has been changed (in which
@@ -262,7 +303,7 @@ module Dependabot
262
303
  # Previous version looks like a git SHA and there's a previous ref, we
263
304
  # could be changing to a nil previous ref in which case we want to
264
305
  # fall back to tge sha version
265
- if dependency.previous_version.match?(/^[0-9a-f]{40}$/) &&
306
+ if T.must(dependency.previous_version).match?(/^[0-9a-f]{40}$/) &&
266
307
  ref_changed? && previous_ref
267
308
  previous_ref
268
309
  else
@@ -270,11 +311,12 @@ module Dependabot
270
311
  end
271
312
  end
272
313
 
314
+ sig { returns(T.nilable(String)) }
273
315
  def new_version
274
316
  # New version looks like a git SHA and there's a new ref, guarding
275
317
  # against changes to a nil new_ref (not certain this can actually
276
318
  # happen atm)
277
- if dependency.version.match?(/^[0-9a-f]{40}$/) && ref_changed? &&
319
+ if T.must(dependency.version).match?(/^[0-9a-f]{40}$/) && ref_changed? &&
278
320
  new_ref
279
321
  return new_ref
280
322
  end
@@ -282,13 +324,15 @@ module Dependabot
282
324
  dependency.version
283
325
  end
284
326
 
327
+ sig { returns(T.nilable(String)) }
285
328
  def previous_ref
286
- previous_refs = dependency.previous_requirements.filter_map do |r|
329
+ previous_refs = T.must(dependency.previous_requirements).filter_map do |r|
287
330
  r.dig(:source, "ref") || r.dig(:source, :ref)
288
331
  end.uniq
289
332
  previous_refs.first if previous_refs.count == 1
290
333
  end
291
334
 
335
+ sig { returns(T.nilable(String)) }
292
336
  def new_ref
293
337
  new_refs = dependency.requirements.filter_map do |r|
294
338
  r.dig(:source, "ref") || r.dig(:source, :ref)
@@ -296,19 +340,28 @@ module Dependabot
296
340
  new_refs.first if new_refs.count == 1
297
341
  end
298
342
 
343
+ sig { returns(T::Boolean) }
299
344
  def ref_changed?
300
345
  # We could go from multiple previous refs (nil) to a single new ref
301
346
  previous_ref != new_ref
302
347
  end
303
348
 
349
+ sig { returns(Dependabot::Clients::GitlabWithRetries) }
304
350
  def gitlab_client
305
- @gitlab_client ||= Dependabot::Clients::GitlabWithRetries
306
- .for_gitlab_dot_com(credentials: credentials)
351
+ @gitlab_client ||=
352
+ T.let(
353
+ Dependabot::Clients::GitlabWithRetries.for_gitlab_dot_com(credentials: credentials),
354
+ T.nilable(Dependabot::Clients::GitlabWithRetries)
355
+ )
307
356
  end
308
357
 
358
+ sig { returns(Dependabot::Clients::GithubWithRetries) }
309
359
  def github_client
310
- @github_client ||= Dependabot::Clients::GithubWithRetries
311
- .for_source(source: source, credentials: credentials)
360
+ @github_client ||=
361
+ T.let(
362
+ Dependabot::Clients::GithubWithRetries.for_source(source: source, credentials: credentials),
363
+ T.nilable(Dependabot::Clients::GithubWithRetries)
364
+ )
312
365
  end
313
366
  end
314
367
  end
@@ -3,6 +3,7 @@
3
3
 
4
4
  require "sorbet-runtime"
5
5
  require "dependabot/source"
6
+ require "dependabot/credential"
6
7
 
7
8
  module Dependabot
8
9
  module MetadataFinders
@@ -19,13 +20,13 @@ module Dependabot
19
20
  sig { returns(Dependabot::Dependency) }
20
21
  attr_reader :dependency
21
22
 
22
- sig { returns(T::Array[T::Hash[String, String]]) }
23
+ sig { returns(T::Array[Dependabot::Credential]) }
23
24
  attr_reader :credentials
24
25
 
25
26
  sig do
26
27
  params(
27
28
  dependency: Dependabot::Dependency,
28
- credentials: T::Array[T::Hash[String, String]]
29
+ credentials: T::Array[Dependabot::Credential]
29
30
  )
30
31
  .void
31
32
  end
@@ -175,7 +176,7 @@ module Dependabot
175
176
  @source = T.let(look_up_source, T.nilable(Dependabot::Source))
176
177
  end
177
178
 
178
- sig { overridable.returns(Dependabot::Source) }
179
+ sig { overridable.returns(T.nilable(Dependabot::Source)) }
179
180
  def look_up_source
180
181
  raise NotImplementedError
181
182
  end
@@ -56,7 +56,7 @@ module Dependabot
56
56
 
57
57
  def branch_exists?
58
58
  azure_client_for_source.branch(branch_name)
59
- rescue ::Azure::Error::NotFound
59
+ rescue ::Dependabot::Clients::Azure::NotFound
60
60
  false
61
61
  end
62
62
 
@@ -185,10 +185,10 @@ module Dependabot
185
185
  end
186
186
 
187
187
  # TODO: Bring this in line with existing library checks that we do in the
188
- # update checkers, which are also overriden by passing an explicit
188
+ # update checkers, which are also overridden by passing an explicit
189
189
  # `requirements_update_strategy`.
190
190
  #
191
- # TODO re-use in MessageBuilder
191
+ # TODO reuse in MessageBuilder
192
192
  sig { returns(T::Boolean) }
193
193
  def library?
194
194
  dependencies.any? { |d| !d.appears_in_lockfile? }
@@ -4,6 +4,7 @@
4
4
  require "octokit"
5
5
  require "sorbet-runtime"
6
6
  require "dependabot/pull_request_creator"
7
+ require "dependabot/credential"
7
8
 
8
9
  module Dependabot
9
10
  class PullRequestCreator
@@ -41,7 +42,7 @@ module Dependabot
41
42
  params(
42
43
  source: Dependabot::Source,
43
44
  custom_labels: T.nilable(T::Array[String]),
44
- credentials: T::Array[T::Hash[String, String]],
45
+ credentials: T::Array[Dependabot::Credential],
45
46
  dependencies: T::Array[Dependency],
46
47
  includes_security_fixes: T::Boolean,
47
48
  label_language: T::Boolean,
@@ -107,7 +108,7 @@ module Dependabot
107
108
  sig { returns(T.nilable(T::Array[String])) }
108
109
  attr_reader :custom_labels
109
110
 
110
- sig { returns(T::Array[T::Hash[String, String]]) }
111
+ sig { returns(T::Array[Dependabot::Credential]) }
111
112
  attr_reader :credentials
112
113
 
113
114
  sig { returns(T::Array[Dependency]) }
@@ -1,37 +1,45 @@
1
- # typed: true
1
+ # typed: strong
2
2
  # frozen_string_literal: true
3
3
 
4
+ require "sorbet-runtime"
4
5
  require "dependabot/pull_request_creator/message_builder"
5
6
 
6
7
  module Dependabot
7
8
  class PullRequestCreator
8
9
  class MessageBuilder
9
10
  class IssueLinker
11
+ extend T::Sig
12
+
10
13
  REPO_REGEX = %r{(?<repo>[\w.-]+/(?:(?!\.git|\.\s)[\w.-])+)}
11
14
  TAG_REGEX = /(?<tag>(?:\#|GH-)\d+)/i
12
- ISSUE_LINK_REGEXS = [
15
+ ISSUE_LINK_REGEXS = T.let([
13
16
  /
14
17
  (?:(?<=[^A-Za-z0-9\[\\]|^)\\*#{TAG_REGEX}(?=[^A-Za-z0-9\-]|$))|
15
18
  (?:(?<=\s|^)#{REPO_REGEX}#{TAG_REGEX}(?=[^A-Za-z0-9\-]|$))
16
19
  /x,
17
20
  /\[#{TAG_REGEX}\](?=[^A-Za-z0-9\-\(])/,
18
21
  /\[(?<tag>(?:\#|GH-)?\d+)\]\(\)/i
19
- ].freeze
22
+ ].freeze, T::Array[Regexp])
20
23
 
24
+ sig { returns(String) }
21
25
  attr_reader :source_url
22
26
 
27
+ sig { params(source_url: String).void }
23
28
  def initialize(source_url:)
24
29
  @source_url = source_url
25
30
  end
26
31
 
32
+ sig { params(text: String).returns(String) }
27
33
  def link_issues(text:)
28
34
  # Loop through each of the issue link regexes, replacing any instances
29
35
  # of them with an absolute link that uses the source URL
30
36
  ISSUE_LINK_REGEXS.reduce(text) do |updated_text, regex|
31
37
  updated_text.gsub(regex) do |issue_link|
32
- tag = issue_link
33
- .match(/(?<tag>(?:\#|GH-)?\d+)/i)
34
- .named_captures.fetch("tag")
38
+ tag = T.must(
39
+ T.must(issue_link
40
+ .match(/(?<tag>(?:\#|GH-)?\d+)/i))
41
+ .named_captures.fetch("tag")
42
+ )
35
43
  number = tag.match(/\d+/).to_s
36
44
 
37
45
  repo = issue_link
@@ -1,4 +1,4 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "sorbet-runtime"
@@ -11,8 +11,20 @@ module Dependabot
11
11
  extend T::Sig
12
12
  extend Forwardable
13
13
 
14
- attr_reader :dependency, :source, :metadata_finder,
15
- :vulnerabilities_fixed, :github_redirection_service
14
+ sig { returns(Dependabot::Dependency) }
15
+ attr_reader :dependency
16
+
17
+ sig { returns(Dependabot::Source) }
18
+ attr_reader :source
19
+
20
+ sig { returns(Dependabot::MetadataFinders::Base) }
21
+ attr_reader :metadata_finder
22
+
23
+ sig { returns(T.nilable(T::Array[T::Hash[String, String]])) }
24
+ attr_reader :vulnerabilities_fixed
25
+
26
+ sig { returns(T.nilable(String)) }
27
+ attr_reader :github_redirection_service
16
28
 
17
29
  def_delegators :metadata_finder,
18
30
  :changelog_url,
@@ -26,6 +38,16 @@ module Dependabot
26
38
  :upgrade_guide_url,
27
39
  :upgrade_guide_text
28
40
 
41
+ sig do
42
+ params(
43
+ dependency: Dependabot::Dependency,
44
+ source: Dependabot::Source,
45
+ metadata_finder: Dependabot::MetadataFinders::Base,
46
+ vulnerabilities_fixed: T.nilable(T::Array[T::Hash[String, String]]),
47
+ github_redirection_service: T.nilable(String)
48
+ )
49
+ .void
50
+ end
29
51
  def initialize(dependency:, source:, metadata_finder:,
30
52
  vulnerabilities_fixed:, github_redirection_service:)
31
53
  @dependency = dependency
@@ -35,6 +57,7 @@ module Dependabot
35
57
  @github_redirection_service = github_redirection_service
36
58
  end
37
59
 
60
+ sig { returns(String) }
38
61
  def to_s
39
62
  msg = ""
40
63
  msg += vulnerabilities_cascade
@@ -49,11 +72,12 @@ module Dependabot
49
72
 
50
73
  private
51
74
 
75
+ sig { returns(String) }
52
76
  def vulnerabilities_cascade
53
77
  return "" unless vulnerabilities_fixed&.any?
54
78
 
55
79
  msg = ""
56
- vulnerabilities_fixed.each do |v|
80
+ T.must(vulnerabilities_fixed).each do |v|
57
81
  msg += serialized_vulnerability_details(v)
58
82
  end
59
83
 
@@ -63,6 +87,7 @@ module Dependabot
63
87
  build_details_tag(summary: "Vulnerabilities fixed", body: msg)
64
88
  end
65
89
 
90
+ sig { returns(String) }
66
91
  def release_cascade
67
92
  return "" unless releases_text && releases_url
68
93
 
@@ -80,6 +105,7 @@ module Dependabot
80
105
  build_details_tag(summary: "Release notes", body: msg)
81
106
  end
82
107
 
108
+ sig { returns(String) }
83
109
  def changelog_cascade
84
110
  return "" unless changelog_url && changelog_text
85
111
 
@@ -95,6 +121,7 @@ module Dependabot
95
121
  build_details_tag(summary: "Changelog", body: msg)
96
122
  end
97
123
 
124
+ sig { returns(String) }
98
125
  def upgrade_guide_cascade
99
126
  return "" unless upgrade_guide_url && upgrade_guide_text
100
127
 
@@ -110,6 +137,7 @@ module Dependabot
110
137
  build_details_tag(summary: "Upgrade guide", body: msg)
111
138
  end
112
139
 
140
+ sig { returns(String) }
113
141
  def commits_cascade
114
142
  return "" unless commits_url && commits
115
143
 
@@ -138,6 +166,7 @@ module Dependabot
138
166
  build_details_tag(summary: "Commits", body: msg)
139
167
  end
140
168
 
169
+ sig { returns(String) }
141
170
  def maintainer_changes_cascade
142
171
  return "" unless maintainer_changes
143
172
 
@@ -147,6 +176,7 @@ module Dependabot
147
176
  )
148
177
  end
149
178
 
179
+ sig { params(summary: String, body: String).returns(String) }
150
180
  def build_details_tag(summary:, body:)
151
181
  # Bitbucket does not support <details> tag (https://jira.atlassian.com/browse/BCLOUD-20231)
152
182
  # CodeCommit does not support the <details> tag (no url available)
@@ -159,10 +189,11 @@ module Dependabot
159
189
  end
160
190
  end
161
191
 
192
+ sig { params(details: T::Hash[String, String]).returns(String) }
162
193
  def serialized_vulnerability_details(details)
163
194
  msg = vulnerability_source_line(details)
164
195
 
165
- msg += "> **#{details['title'].lines.map(&:strip).join(' ')}**\n" if details["title"]
196
+ msg += "> **#{T.must(details['title']).lines.map(&:strip).join(' ')}**\n" if details["title"]
166
197
 
167
198
  if (description = details["description"])
168
199
  description.strip.lines.first(20).each { |line| msg += "> #{line}" }
@@ -175,6 +206,7 @@ module Dependabot
175
206
  msg + "\n"
176
207
  end
177
208
 
209
+ sig { params(details: T::Hash[String, String]).returns(String) }
178
210
  def vulnerability_source_line(details)
179
211
  if details["source_url"] && details["source_name"]
180
212
  "*Sourced from [#{details['source_name']}]" \
@@ -186,6 +218,7 @@ module Dependabot
186
218
  end
187
219
  end
188
220
 
221
+ sig { params(details: T::Hash[String, T.untyped]).returns(String) }
189
222
  def vulnerability_version_range_lines(details)
190
223
  msg = ""
191
224
  %w(
@@ -203,18 +236,20 @@ module Dependabot
203
236
  msg
204
237
  end
205
238
 
239
+ sig { params(text: String).returns(String) }
206
240
  def link_issues(text:)
207
241
  IssueLinker
208
242
  .new(source_url: source_url)
209
243
  .link_issues(text: text)
210
244
  end
211
245
 
246
+ sig { params(text: String, base_url: String).returns(String) }
212
247
  def fix_relative_links(text:, base_url:)
213
248
  text.gsub(/\[.*?\]\([^)]+\)/) do |link|
214
249
  next link if link.include?("://")
215
250
 
216
- relative_path = link.match(/\((.*?)\)/).captures.last
217
- base = base_url.split("://").last.gsub(%r{[^/]*$}, "")
251
+ relative_path = T.must(T.must(link.match(/\((.*?)\)/)).captures.last)
252
+ base = T.must(base_url.split("://").last).gsub(%r{[^/]*$}, "")
218
253
  path = File.join(base, relative_path)
219
254
  absolute_path =
220
255
  base_url.sub(
@@ -225,6 +260,7 @@ module Dependabot
225
260
  end
226
261
  end
227
262
 
263
+ sig { params(text: String, limit: Integer).returns(String) }
228
264
  def quote_and_truncate(text, limit: 50)
229
265
  lines = text.split("\n")
230
266
  lines.first(limit).tap do |limited_lines|
@@ -233,32 +269,37 @@ module Dependabot
233
269
  end.join
234
270
  end
235
271
 
272
+ sig { returns(String) }
236
273
  def truncated_line
237
274
  # Tables can spill out of truncated details, so we close them
238
275
  "></tr></table> \n ... (truncated)\n"
239
276
  end
240
277
 
278
+ sig { returns(String) }
241
279
  def break_tag
242
280
  source_provider_supports_html? ? "\n<br />" : "\n\n"
243
281
  end
244
282
 
283
+ sig { returns(T::Boolean) }
245
284
  def source_provider_supports_html?
246
285
  !%w(bitbucket codecommit).include?(source.provider)
247
286
  end
248
287
 
288
+ sig { params(text: String, unsafe: T::Boolean).returns(String) }
249
289
  def sanitize_links_and_mentions(text, unsafe: false)
250
290
  LinkAndMentionSanitizer
251
291
  .new(github_redirection_service: github_redirection_service)
252
292
  .sanitize_links_and_mentions(text: text, unsafe: unsafe, format_html: source_provider_supports_html?)
253
293
  end
254
294
 
295
+ sig { params(text: String).returns(String) }
255
296
  def sanitize_template_tags(text)
256
297
  text.gsub(/\<.*?\>/) do |tag|
257
- tag_contents = tag.match(/\<(.*?)\>/).captures.first.strip
298
+ tag_contents = tag.match(/\<(.*?)\>/)&.captures&.first&.strip
258
299
 
259
300
  # Unclosed calls to template overflow out of the blockquote block,
260
301
  # wrecking the rest of our PRs. Other tags don't share this problem.
261
- next "\\#{tag}" if tag_contents.start_with?("template")
302
+ next "\\#{tag}" if tag_contents&.start_with?("template")
262
303
 
263
304
  tag
264
305
  end