dependabot-common 0.236.0 → 0.238.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 +3 -3
  3. data/lib/dependabot/clients/codecommit.rb +1 -0
  4. data/lib/dependabot/config/file.rb +17 -6
  5. data/lib/dependabot/config/update_config.rb +23 -5
  6. data/lib/dependabot/dependency.rb +137 -27
  7. data/lib/dependabot/dependency_file.rb +84 -14
  8. data/lib/dependabot/dependency_group.rb +29 -5
  9. data/lib/dependabot/errors.rb +335 -13
  10. data/lib/dependabot/file_fetchers/base.rb +227 -93
  11. data/lib/dependabot/file_updaters/base.rb +1 -1
  12. data/lib/dependabot/git_commit_checker.rb +6 -0
  13. data/lib/dependabot/git_metadata_fetcher.rb +58 -20
  14. data/lib/dependabot/git_ref.rb +71 -0
  15. data/lib/dependabot/metadata_finders/base/changelog_finder.rb +13 -6
  16. data/lib/dependabot/pull_request_creator/github.rb +11 -8
  17. data/lib/dependabot/pull_request_creator/message.rb +21 -2
  18. data/lib/dependabot/pull_request_creator/message_builder/link_and_mention_sanitizer.rb +37 -16
  19. data/lib/dependabot/pull_request_creator/message_builder/metadata_presenter.rb +4 -2
  20. data/lib/dependabot/pull_request_creator/message_builder.rb +54 -4
  21. data/lib/dependabot/pull_request_creator/pr_name_prefixer.rb +10 -4
  22. data/lib/dependabot/shared_helpers.rb +117 -33
  23. data/lib/dependabot/simple_instrumentor.rb +22 -3
  24. data/lib/dependabot/source.rb +65 -17
  25. data/lib/dependabot/update_checkers/version_filters.rb +12 -1
  26. data/lib/dependabot/utils.rb +21 -2
  27. data/lib/dependabot/workspace/base.rb +42 -7
  28. data/lib/dependabot/workspace/change_attempt.rb +31 -3
  29. data/lib/dependabot/workspace/git.rb +34 -4
  30. data/lib/dependabot/workspace.rb +16 -2
  31. data/lib/dependabot.rb +1 -1
  32. metadata +38 -9
@@ -1,49 +1,73 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "excon"
5
5
  require "open3"
6
+ require "sorbet-runtime"
7
+
6
8
  require "dependabot/errors"
9
+ require "dependabot/git_ref"
7
10
 
8
11
  module Dependabot
9
12
  class GitMetadataFetcher
13
+ extend T::Sig
14
+
10
15
  KNOWN_HOSTS = /github\.com|bitbucket\.org|gitlab.com/i
11
16
 
17
+ sig do
18
+ params(
19
+ url: String,
20
+ credentials: T::Array[T::Hash[String, String]]
21
+ )
22
+ .void
23
+ end
12
24
  def initialize(url:, credentials:)
13
25
  @url = url
14
26
  @credentials = credentials
15
27
  end
16
28
 
29
+ sig { returns(T.nilable(String)) }
17
30
  def upload_pack
18
- @upload_pack ||= fetch_upload_pack_for(url)
31
+ @upload_pack ||= T.let(fetch_upload_pack_for(url), T.nilable(String))
19
32
  rescue Octokit::ClientError
20
33
  raise Dependabot::GitDependenciesNotReachable, [url]
21
34
  end
22
35
 
36
+ sig { returns(T::Array[GitRef]) }
23
37
  def tags
24
38
  return [] unless upload_pack
25
39
 
26
- @tags ||= tags_for_upload_pack.map do |ref|
27
- OpenStruct.new(
28
- name: ref.name,
29
- tag_sha: ref.ref_sha,
30
- commit_sha: ref.commit_sha
31
- )
32
- end
40
+ @tags ||= T.let(
41
+ tags_for_upload_pack.map do |ref|
42
+ GitRef.new(
43
+ name: ref.name,
44
+ tag_sha: ref.ref_sha,
45
+ commit_sha: ref.commit_sha
46
+ )
47
+ end,
48
+ T.nilable(T::Array[GitRef])
49
+ )
33
50
  end
34
51
 
52
+ sig { returns(T::Array[GitRef]) }
35
53
  def tags_for_upload_pack
36
- @tags_for_upload_pack ||= refs_for_upload_pack.select { |ref| ref.ref_type == :tag }
54
+ @tags_for_upload_pack ||= T.let(
55
+ refs_for_upload_pack.select { |ref| ref.ref_type == RefType::Tag },
56
+ T.nilable(T::Array[GitRef])
57
+ )
37
58
  end
38
59
 
60
+ sig { returns(T::Array[GitRef]) }
39
61
  def refs_for_upload_pack
40
- @refs_for_upload_pack ||= parse_refs_for_upload_pack
62
+ @refs_for_upload_pack ||= T.let(parse_refs_for_upload_pack, T.nilable(T::Array[GitRef]))
41
63
  end
42
64
 
65
+ sig { returns(T::Array[String]) }
43
66
  def ref_names
44
67
  refs_for_upload_pack.map(&:name)
45
68
  end
46
69
 
70
+ sig { params(ref: String).returns(T.nilable(String)) }
47
71
  def head_commit_for_ref(ref)
48
72
  if ref == "HEAD"
49
73
  # Remove the opening clause of the upload pack as this isn't always
@@ -51,8 +75,8 @@ module Dependabot
51
75
  # causes problems for our `sha_for_update_pack_line` logic. The format
52
76
  # of this opening clause is documented at
53
77
  # https://git-scm.com/docs/http-protocol#_smart_server_response
54
- line = upload_pack.gsub(/^[0-9a-f]{4}# service=git-upload-pack/, "")
55
- .lines.find { |l| l.include?(" HEAD") }
78
+ line = T.must(upload_pack).gsub(/^[0-9a-f]{4}# service=git-upload-pack/, "")
79
+ .lines.find { |l| l.include?(" HEAD") }
56
80
  return sha_for_update_pack_line(line) if line
57
81
  end
58
82
 
@@ -61,6 +85,7 @@ module Dependabot
61
85
  &.commit_sha
62
86
  end
63
87
 
88
+ sig { params(ref: String).returns(T.nilable(String)) }
64
89
  def head_commit_for_ref_sha(ref)
65
90
  refs_for_upload_pack
66
91
  .find { |r| r.ref_sha == ref }
@@ -69,8 +94,13 @@ module Dependabot
69
94
 
70
95
  private
71
96
 
72
- attr_reader :url, :credentials
97
+ sig { returns(String) }
98
+ attr_reader :url
99
+
100
+ sig { returns(T::Array[T::Hash[String, String]]) }
101
+ attr_reader :credentials
73
102
 
103
+ sig { params(uri: String).returns(String) }
74
104
  def fetch_upload_pack_for(uri)
75
105
  response = fetch_raw_upload_pack_for(uri)
76
106
  return response.body if response.status == 200
@@ -97,6 +127,7 @@ module Dependabot
97
127
  raise Dependabot::GitDependenciesNotReachable, [uri]
98
128
  end
99
129
 
130
+ sig { params(uri: String).returns(Excon::Response) }
100
131
  def fetch_raw_upload_pack_for(uri)
101
132
  url = service_pack_uri(uri)
102
133
  url = url.rpartition("@").tap { |a| a.first.gsub!("@", "%40") }.join
@@ -107,6 +138,7 @@ module Dependabot
107
138
  )
108
139
  end
109
140
 
141
+ sig { params(uri: String).returns(T.untyped) }
110
142
  def fetch_raw_upload_pack_with_git_for(uri)
111
143
  service_pack_uri = uri
112
144
  service_pack_uri += ".git" unless service_pack_uri.end_with?(".git") || skip_git_suffix(uri)
@@ -129,11 +161,12 @@ module Dependabot
129
161
  end
130
162
  end
131
163
 
164
+ sig { returns(T::Array[GitRef]) }
132
165
  def parse_refs_for_upload_pack
133
166
  peeled_lines = []
134
167
 
135
- result = upload_pack.lines.each_with_object({}) do |line, res|
136
- full_ref_name = line.split.last
168
+ result = T.must(upload_pack).lines.each_with_object({}) do |line, res|
169
+ full_ref_name = T.must(line.split.last)
137
170
  next unless full_ref_name.start_with?("refs/tags", "refs/heads")
138
171
 
139
172
  (peeled_lines << line) && next if line.strip.end_with?("^{}")
@@ -141,10 +174,10 @@ module Dependabot
141
174
  ref_name = full_ref_name.sub(%r{^refs/(tags|heads)/}, "").strip
142
175
  sha = sha_for_update_pack_line(line)
143
176
 
144
- res[ref_name] = OpenStruct.new(
177
+ res[ref_name] = GitRef.new(
145
178
  name: ref_name,
146
179
  ref_sha: sha,
147
- ref_type: full_ref_name.start_with?("refs/tags") ? :tag : :head,
180
+ ref_type: full_ref_name.start_with?("refs/tags") ? RefType::Tag : RefType::Head,
148
181
  commit_sha: sha
149
182
  )
150
183
  end
@@ -162,6 +195,7 @@ module Dependabot
162
195
  result.values
163
196
  end
164
197
 
198
+ sig { params(uri: String).returns(String) }
165
199
  def service_pack_uri(uri)
166
200
  service_pack_uri = uri_with_auth(uri)
167
201
  service_pack_uri = service_pack_uri.gsub(%r{/$}, "")
@@ -169,6 +203,7 @@ module Dependabot
169
203
  service_pack_uri + "/info/refs?service=git-upload-pack"
170
204
  end
171
205
 
206
+ sig { params(uri: String).returns(T::Boolean) }
172
207
  def skip_git_suffix(uri)
173
208
  # TODO: Unlike the other providers (GitHub, GitLab, BitBucket), as of 2023-01-18 Azure DevOps does not support the
174
209
  # ".git" suffix. It will return a 404.
@@ -188,6 +223,7 @@ module Dependabot
188
223
 
189
224
  # Add in username and password if present in credentials.
190
225
  # Credentials are never present for production Dependabot.
226
+ sig { params(uri: String).returns(String) }
191
227
  def uri_with_auth(uri)
192
228
  uri = SharedHelpers.scp_to_standard(uri)
193
229
  uri = URI(uri)
@@ -196,7 +232,7 @@ module Dependabot
196
232
 
197
233
  uri.scheme = "https" if uri.scheme != "http"
198
234
 
199
- if !uri.password && cred&.fetch("username", nil) && cred&.fetch("password", nil)
235
+ if !uri.password && cred && cred.fetch("username", nil) && cred.fetch("password", nil)
200
236
  # URI doesn't have authentication details, but we have credentials
201
237
  uri.user = URI.encode_www_form_component(cred["username"])
202
238
  uri.password = URI.encode_www_form_component(cred["password"])
@@ -205,10 +241,12 @@ module Dependabot
205
241
  uri.to_s
206
242
  end
207
243
 
244
+ sig { params(line: String).returns(String) }
208
245
  def sha_for_update_pack_line(line)
209
- line.split.first.chars.last(40).join
246
+ T.must(line.split.first).chars.last(40).join
210
247
  end
211
248
 
249
+ sig { returns(T::Hash[Symbol, T.untyped]) }
212
250
  def excon_defaults
213
251
  # Some git hosts are slow when returning a large number of tags
214
252
  SharedHelpers.excon_defaults(read_timeout: 20)
@@ -0,0 +1,71 @@
1
+ # typed: strong
2
+ # frozen_string_literal: true
3
+
4
+ require "sorbet-runtime"
5
+
6
+ module Dependabot
7
+ class GitRef
8
+ extend T::Sig
9
+
10
+ sig { returns(String) }
11
+ attr_accessor :name
12
+
13
+ sig { returns(String) }
14
+ attr_accessor :commit_sha
15
+
16
+ sig { returns(T.nilable(String)) }
17
+ attr_reader :tag_sha
18
+
19
+ sig { returns(T.nilable(String)) }
20
+ attr_reader :ref_sha
21
+
22
+ sig { returns(T.nilable(RefType)) }
23
+ attr_reader :ref_type
24
+
25
+ sig do
26
+ params(
27
+ name: String,
28
+ commit_sha: String,
29
+ tag_sha: T.nilable(String),
30
+ ref_sha: T.nilable(String),
31
+ ref_type: T.nilable(RefType)
32
+ )
33
+ .void
34
+ end
35
+ def initialize(name:, commit_sha:, tag_sha: nil, ref_sha: nil, ref_type: nil)
36
+ @name = name
37
+ @commit_sha = commit_sha
38
+ @ref_sha = ref_sha
39
+ @tag_sha = tag_sha
40
+ @ref_type = ref_type
41
+ end
42
+
43
+ sig { params(other: BasicObject).returns(T::Boolean) }
44
+ def ==(other)
45
+ case other
46
+ when GitRef
47
+ to_h == other.to_h
48
+ else
49
+ false
50
+ end
51
+ end
52
+
53
+ sig { returns(T::Hash[Symbol, T.nilable(String)]) }
54
+ def to_h
55
+ {
56
+ name: name,
57
+ commit_sha: commit_sha,
58
+ tag_sha: tag_sha,
59
+ ref_sha: ref_sha,
60
+ ref_type: ref_type
61
+ }.compact
62
+ end
63
+ end
64
+
65
+ class RefType < T::Enum
66
+ enums do
67
+ Tag = new
68
+ Head = new
69
+ end
70
+ end
71
+ end
@@ -1,7 +1,8 @@
1
- # typed: false
1
+ # typed: true
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "excon"
5
+ require "sorbet-runtime"
5
6
 
6
7
  require "dependabot/clients/github_with_retries"
7
8
  require "dependabot/clients/gitlab_with_retries"
@@ -12,6 +13,8 @@ module Dependabot
12
13
  module MetadataFinders
13
14
  class Base
14
15
  class ChangelogFinder
16
+ extend T::Sig
17
+
15
18
  require_relative "changelog_pruner"
16
19
  require_relative "commits_finder"
17
20
 
@@ -86,9 +89,9 @@ module Dependabot
86
89
  suggested_source = Source.from_url(suggested_changelog_url)
87
90
  return unless suggested_source&.provider == "github"
88
91
 
89
- opts = { path: suggested_source.directory, ref: suggested_source.branch }.compact
92
+ opts = { path: suggested_source&.directory, ref: suggested_source&.branch }.compact
90
93
  suggested_source_client = github_client_for_source(suggested_source)
91
- tmp_files = suggested_source_client.contents(suggested_source.repo, opts)
94
+ tmp_files = suggested_source_client.contents(suggested_source&.repo, opts)
92
95
 
93
96
  filename = suggested_changelog_url.split("/").last.split("#").first
94
97
  @changelog_from_suggested_url =
@@ -128,7 +131,10 @@ module Dependabot
128
131
  file = candidates.first if candidates.one?
129
132
  file ||=
130
133
  candidates.find do |f|
131
- candidates -= [f] && next if fetch_file_text(f).nil?
134
+ if fetch_file_text(f).nil?
135
+ candidates -= [f]
136
+ next
137
+ end
132
138
  pruner = ChangelogPruner.new(
133
139
  dependency: dependency,
134
140
  changelog_text: fetch_file_text(f)
@@ -159,11 +165,12 @@ module Dependabot
159
165
  fetch_file_text(changelog)
160
166
  end
161
167
 
168
+ sig { params(file: T.untyped).returns(T.nilable(String)) }
162
169
  def fetch_file_text(file)
163
170
  @file_text ||= {}
164
171
 
165
172
  unless @file_text.key?(file.download_url)
166
- file_source = Source.from_url(file.html_url)
173
+ file_source = T.must(Source.from_url(file.html_url))
167
174
  @file_text[file.download_url] =
168
175
  case file_source.provider
169
176
  when "github" then fetch_github_file(file_source, file)
@@ -171,7 +178,7 @@ module Dependabot
171
178
  when "bitbucket" then fetch_bitbucket_file(file)
172
179
  when "azure" then fetch_azure_file(file)
173
180
  when "codecommit" then nil # TODO: git file from codecommit
174
- else raise "Unsupported provider '#{provider}'"
181
+ else raise "Unsupported provider '#{file_source.provider}'"
175
182
  end
176
183
  end
177
184
 
@@ -1,8 +1,9 @@
1
- # typed: false
1
+ # typed: true
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "octokit"
5
5
  require "securerandom"
6
+ require "sorbet-runtime"
6
7
  require "dependabot/clients/github_with_retries"
7
8
  require "dependabot/pull_request_creator"
8
9
  require "dependabot/pull_request_creator/commit_signer"
@@ -10,6 +11,8 @@ module Dependabot
10
11
  class PullRequestCreator
11
12
  # rubocop:disable Metrics/ClassLength
12
13
  class Github
14
+ extend T::Sig
15
+
13
16
  # GitHub limits PR descriptions to a max of 65,536 characters:
14
17
  # https://github.com/orgs/community/discussions/27190#discussioncomment-3726017
15
18
  PR_DESCRIPTION_MAX_LENGTH = 65_535 # 0 based count
@@ -65,11 +68,11 @@ module Dependabot
65
68
  def branch_exists?(name)
66
69
  git_metadata_fetcher.ref_names.include?(name)
67
70
  rescue Dependabot::GitDependenciesNotReachable => e
68
- raise e.cause if e.cause&.message&.include?("is disabled")
69
- raise e.cause if e.cause.is_a?(Octokit::Unauthorized)
71
+ raise T.must(e.cause) if e.cause&.message&.include?("is disabled")
72
+ raise T.must(e.cause) if e.cause.is_a?(Octokit::Unauthorized)
70
73
  raise(RepoNotFound, source.url) unless repo_exists?
71
74
 
72
- retrying ||= false
75
+ retrying ||= T.let(false, T::Boolean)
73
76
 
74
77
  msg = "Unexpected git error!\n\n#{e.cause&.class}: #{e.cause&.message}"
75
78
  raise msg if retrying
@@ -249,14 +252,14 @@ module Dependabot
249
252
  rescue Octokit::UnprocessableEntity => e
250
253
  raise if e.message.match?(/Reference already exists/i)
251
254
 
252
- retrying_branch_creation ||= false
255
+ retrying_branch_creation ||= T.let(false, T::Boolean)
253
256
  raise if retrying_branch_creation
254
257
 
255
258
  retrying_branch_creation = true
256
259
 
257
260
  # Branch creation will fail if a branch called `dependabot` already
258
261
  # exists, since git won't be able to create a dir with the same name
259
- ref = "refs/heads/#{SecureRandom.hex[0..3] + branch_name}"
262
+ ref = "refs/heads/#{T.must(SecureRandom.hex[0..3]) + branch_name}"
260
263
  retry
261
264
  end
262
265
  end
@@ -320,7 +323,7 @@ module Dependabot
320
323
  "`@#{reviewers.first}`"
321
324
  else
322
325
  names = reviewers.map { |rv| "`@#{rv}`" }
323
- "#{names[0..-2].join(', ')} and #{names[-1]}"
326
+ "#{T.must(names[0..-2]).join(', ')} and #{names[-1]}"
324
327
  end
325
328
 
326
329
  msg = "Dependabot tried to add #{reviewers_string} as "
@@ -371,7 +374,7 @@ module Dependabot
371
374
  # Sometimes PR creation fails with no details (presumably because the
372
375
  # details are internal). It doesn't hurt to retry in these cases, in
373
376
  # case the cause is a race.
374
- retrying_pr_creation ||= false
377
+ retrying_pr_creation ||= T.let(false, T::Boolean)
375
378
  raise if retrying_pr_creation
376
379
 
377
380
  retrying_pr_creation = true
@@ -1,12 +1,31 @@
1
- # typed: true
1
+ # typed: strong
2
2
  # frozen_string_literal: true
3
3
 
4
+ require "sorbet-runtime"
5
+
4
6
  module Dependabot
5
7
  class PullRequestCreator
6
8
  # Message is a static alternative to MessageBuilder
7
9
  class Message
8
- attr_reader :commit_message, :pr_name, :pr_message
10
+ extend T::Sig
11
+
12
+ sig { returns(T.nilable(String)) }
13
+ attr_reader :commit_message
14
+
15
+ sig { returns(T.nilable(String)) }
16
+ attr_reader :pr_name
9
17
 
18
+ sig { returns(T.nilable(String)) }
19
+ attr_reader :pr_message
20
+
21
+ sig do
22
+ params(
23
+ commit_message: T.nilable(String),
24
+ pr_name: T.nilable(String),
25
+ pr_message: T.nilable(String)
26
+ )
27
+ .void
28
+ end
10
29
  def initialize(commit_message: nil, pr_name: nil, pr_message: nil)
11
30
  @commit_message = commit_message
12
31
  @pr_name = pr_name
@@ -1,7 +1,8 @@
1
- # typed: false
1
+ # typed: strong
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "commonmarker"
5
+ require "sorbet-runtime"
5
6
  require "strscan"
6
7
  require "dependabot/pull_request_creator/message_builder"
7
8
 
@@ -9,6 +10,8 @@ module Dependabot
9
10
  class PullRequestCreator
10
11
  class MessageBuilder
11
12
  class LinkAndMentionSanitizer
13
+ extend T::Sig
14
+
12
15
  GITHUB_USERNAME = /[a-z0-9]+(-[a-z0-9]+)*/i
13
16
  GITHUB_REF_REGEX = %r{
14
17
  (?:https?://)?
@@ -22,19 +25,24 @@ module Dependabot
22
25
  TEAM_MENTION_REGEX = %r{(?<![A-Za-z0-9`~])@(?<org>#{GITHUB_USERNAME})/(?<team>#{GITHUB_USERNAME})/?}
23
26
  # End of string
24
27
  EOS_REGEX = /\z/
25
- COMMONMARKER_OPTIONS = %i(
26
- GITHUB_PRE_LANG FULL_INFO_STRING
27
- ).freeze
28
- COMMONMARKER_EXTENSIONS = %i(
29
- table tasklist strikethrough autolink tagfilter
30
- ).freeze
31
-
28
+ COMMONMARKER_OPTIONS = T.let(
29
+ %i(GITHUB_PRE_LANG FULL_INFO_STRING).freeze,
30
+ T::Array[Symbol]
31
+ )
32
+ COMMONMARKER_EXTENSIONS = T.let(
33
+ %i(table tasklist strikethrough autolink tagfilter).freeze,
34
+ T::Array[Symbol]
35
+ )
36
+
37
+ sig { returns(T.nilable(String)) }
32
38
  attr_reader :github_redirection_service
33
39
 
40
+ sig { params(github_redirection_service: T.nilable(String)).void }
34
41
  def initialize(github_redirection_service:)
35
42
  @github_redirection_service = github_redirection_service
36
43
  end
37
44
 
45
+ sig { params(text: String, unsafe: T::Boolean, format_html: T::Boolean).returns(String) }
38
46
  def sanitize_links_and_mentions(text:, unsafe: false, format_html: true)
39
47
  doc = CommonMarker.render_doc(
40
48
  text, :LIBERAL_HTML_TAG, COMMONMARKER_EXTENSIONS
@@ -53,6 +61,7 @@ module Dependabot
53
61
 
54
62
  private
55
63
 
64
+ sig { params(doc: CommonMarker::Node).void }
56
65
  def sanitize_mentions(doc)
57
66
  doc.walk do |node|
58
67
  if node.type == :text &&
@@ -77,6 +86,7 @@ module Dependabot
77
86
  # This is because there are ecosystems that have packages that follow the same pattern
78
87
  # (e.g. @angular/angular-cli), and we don't want to create an invalid link, since
79
88
  # team mentions link to `https://github.com/org/:organization_name/teams/:team_name`.
89
+ sig { params(doc: CommonMarker::Node).void }
80
90
  def sanitize_team_mentions(doc)
81
91
  doc.walk do |node|
82
92
  if node.type == :text &&
@@ -92,6 +102,7 @@ module Dependabot
92
102
  end
93
103
  end
94
104
 
105
+ sig { params(doc: CommonMarker::Node).void }
95
106
  def sanitize_links(doc)
96
107
  doc.walk do |node|
97
108
  if node.type == :link && node.url.match?(GITHUB_REF_REGEX)
@@ -101,7 +112,7 @@ module Dependabot
101
112
  next
102
113
  end
103
114
 
104
- last_match = subnode.string_content.match(GITHUB_REF_REGEX)
115
+ last_match = T.must(subnode.string_content.match(GITHUB_REF_REGEX))
105
116
  number = last_match.named_captures.fetch("number")
106
117
  repo = last_match.named_captures.fetch("repo")
107
118
  subnode.string_content = "#{repo}##{number}"
@@ -115,6 +126,7 @@ module Dependabot
115
126
  end
116
127
  end
117
128
 
129
+ sig { params(doc: CommonMarker::Node).void }
118
130
  def sanitize_nwo_text(doc)
119
131
  doc.walk do |node|
120
132
  if node.type == :text &&
@@ -125,8 +137,9 @@ module Dependabot
125
137
  end
126
138
  end
127
139
 
140
+ sig { params(node: CommonMarker::Node).void }
128
141
  def replace_nwo_node(node)
129
- match = node.string_content.match(GITHUB_NWO_REGEX)
142
+ match = T.must(node.string_content.match(GITHUB_NWO_REGEX))
130
143
  repo = match.named_captures.fetch("repo")
131
144
  number = match.named_captures.fetch("number")
132
145
  new_node = build_nwo_text_node("#{repo}##{number}")
@@ -134,22 +147,24 @@ module Dependabot
134
147
  node.delete
135
148
  end
136
149
 
150
+ sig { params(text: String).returns(String) }
137
151
  def replace_github_host(text)
138
- return text if !github_redirection_service.nil? && text.include?(github_redirection_service)
152
+ return text if !github_redirection_service.nil? && text.include?(T.must(github_redirection_service))
139
153
 
140
154
  text.gsub(
141
155
  /(www\.)?github.com/, github_redirection_service || "github.com"
142
156
  )
143
157
  end
144
158
 
159
+ sig { params(text: String).returns(T::Array[CommonMarker::Node]) }
145
160
  def build_mention_nodes(text)
146
- nodes = []
161
+ nodes = T.let([], T::Array[CommonMarker::Node])
147
162
  scan = StringScanner.new(text)
148
163
 
149
164
  until scan.eos?
150
165
  line = scan.scan_until(MENTION_REGEX) ||
151
166
  scan.scan_until(EOS_REGEX)
152
- line_match = line.match(MENTION_REGEX)
167
+ line_match = T.must(line).match(MENTION_REGEX)
153
168
  mention = line_match&.to_s
154
169
  text_node = CommonMarker::Node.new(:text)
155
170
 
@@ -168,14 +183,15 @@ module Dependabot
168
183
  nodes
169
184
  end
170
185
 
186
+ sig { params(text: String).returns(T::Array[CommonMarker::Node]) }
171
187
  def build_team_mention_nodes(text)
172
- nodes = []
188
+ nodes = T.let([], T::Array[CommonMarker::Node])
173
189
 
174
190
  scan = StringScanner.new(text)
175
191
  until scan.eos?
176
192
  line = scan.scan_until(TEAM_MENTION_REGEX) ||
177
193
  scan.scan_until(EOS_REGEX)
178
- line_match = line.match(TEAM_MENTION_REGEX)
194
+ line_match = T.must(line).match(TEAM_MENTION_REGEX)
179
195
  mention = line_match&.to_s
180
196
  text_node = CommonMarker::Node.new(:text)
181
197
 
@@ -192,18 +208,21 @@ module Dependabot
192
208
  nodes
193
209
  end
194
210
 
211
+ sig { params(text: String).returns(T::Array[CommonMarker::Node]) }
195
212
  def build_mention_link_text_nodes(text)
196
213
  code_node = CommonMarker::Node.new(:code)
197
214
  code_node.string_content = insert_zero_width_space_in_mention(text)
198
215
  [code_node]
199
216
  end
200
217
 
218
+ sig { params(text: String).returns(CommonMarker::Node) }
201
219
  def build_nwo_text_node(text)
202
220
  code_node = CommonMarker::Node.new(:code)
203
221
  code_node.string_content = text
204
222
  code_node
205
223
  end
206
224
 
225
+ sig { params(url: String, text: String).returns(CommonMarker::Node) }
207
226
  def create_link_node(url, text)
208
227
  link_node = CommonMarker::Node.new(:link)
209
228
  code_node = CommonMarker::Node.new(:code)
@@ -217,12 +236,14 @@ module Dependabot
217
236
  # email replies on dependabot pull requests triggering notifications to
218
237
  # users who've been mentioned in changelogs etc. PR email replies parse
219
238
  # the content of the pull request body in plain text.
239
+ sig { params(mention: String).returns(String) }
220
240
  def insert_zero_width_space_in_mention(mention)
221
241
  mention.sub("@", "@\u200B").encode("utf-8")
222
242
  end
223
243
 
244
+ sig { params(node: CommonMarker::Node).returns(T::Boolean) }
224
245
  def parent_node_link?(node)
225
- node.type == :link || (node.parent && parent_node_link?(node.parent))
246
+ node.type == :link || (!node.parent.nil? && parent_node_link?(T.must(node.parent)))
226
247
  end
227
248
  end
228
249
  end
@@ -1,12 +1,14 @@
1
- # typed: false
1
+ # typed: true
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 MetadataPresenter
11
+ extend T::Sig
10
12
  extend Forwardable
11
13
 
12
14
  attr_reader :dependency, :source, :metadata_finder,
@@ -192,7 +194,7 @@ module Dependabot
192
194
  unaffected_versions
193
195
  affected_versions
194
196
  ).each do |tp|
195
- type = tp.split("_").first.capitalize
197
+ type = T.must(tp.split("_").first).capitalize
196
198
  next unless details[tp]
197
199
 
198
200
  versions_string = details[tp].any? ? details[tp].join("; ") : "none"