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.
- checksums.yaml +4 -4
- data/lib/dependabot/clients/azure.rb +3 -3
- data/lib/dependabot/clients/codecommit.rb +1 -0
- data/lib/dependabot/config/file.rb +17 -6
- data/lib/dependabot/config/update_config.rb +23 -5
- data/lib/dependabot/dependency.rb +137 -27
- data/lib/dependabot/dependency_file.rb +84 -14
- data/lib/dependabot/dependency_group.rb +29 -5
- data/lib/dependabot/errors.rb +335 -13
- data/lib/dependabot/file_fetchers/base.rb +227 -93
- data/lib/dependabot/file_updaters/base.rb +1 -1
- data/lib/dependabot/git_commit_checker.rb +6 -0
- data/lib/dependabot/git_metadata_fetcher.rb +58 -20
- data/lib/dependabot/git_ref.rb +71 -0
- data/lib/dependabot/metadata_finders/base/changelog_finder.rb +13 -6
- data/lib/dependabot/pull_request_creator/github.rb +11 -8
- data/lib/dependabot/pull_request_creator/message.rb +21 -2
- data/lib/dependabot/pull_request_creator/message_builder/link_and_mention_sanitizer.rb +37 -16
- data/lib/dependabot/pull_request_creator/message_builder/metadata_presenter.rb +4 -2
- data/lib/dependabot/pull_request_creator/message_builder.rb +54 -4
- data/lib/dependabot/pull_request_creator/pr_name_prefixer.rb +10 -4
- data/lib/dependabot/shared_helpers.rb +117 -33
- data/lib/dependabot/simple_instrumentor.rb +22 -3
- data/lib/dependabot/source.rb +65 -17
- data/lib/dependabot/update_checkers/version_filters.rb +12 -1
- data/lib/dependabot/utils.rb +21 -2
- data/lib/dependabot/workspace/base.rb +42 -7
- data/lib/dependabot/workspace/change_attempt.rb +31 -3
- data/lib/dependabot/workspace/git.rb +34 -4
- data/lib/dependabot/workspace.rb +16 -2
- data/lib/dependabot.rb +1 -1
- metadata +38 -9
@@ -1,49 +1,73 @@
|
|
1
|
-
# typed:
|
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 ||=
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
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 ||=
|
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
|
-
|
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
|
-
|
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] =
|
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") ?
|
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
|
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:
|
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
|
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
|
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
|
-
|
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:
|
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:
|
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
|
-
|
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:
|
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 =
|
26
|
-
GITHUB_PRE_LANG FULL_INFO_STRING
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
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:
|
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"
|