dependabot-common 0.235.0 → 0.237.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/dependabot/clients/azure.rb +3 -3
- data/lib/dependabot/config/file.rb +32 -9
- data/lib/dependabot/config/file_fetcher.rb +3 -3
- data/lib/dependabot/config/ignore_condition.rb +34 -8
- data/lib/dependabot/config/update_config.rb +42 -6
- data/lib/dependabot/config.rb +1 -1
- data/lib/dependabot/dependency_file.rb +89 -14
- data/lib/dependabot/dependency_group.rb +29 -5
- data/lib/dependabot/errors.rb +101 -13
- data/lib/dependabot/file_fetchers/base.rb +250 -93
- data/lib/dependabot/file_updaters/artifact_updater.rb +37 -10
- data/lib/dependabot/file_updaters/vendor_updater.rb +13 -3
- data/lib/dependabot/logger.rb +7 -2
- data/lib/dependabot/metadata_finders/base/changelog_finder.rb +13 -6
- data/lib/dependabot/pull_request_creator/commit_signer.rb +33 -7
- data/lib/dependabot/pull_request_creator/github.rb +13 -10
- 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 +5 -3
- data/lib/dependabot/pull_request_creator/message_builder.rb +5 -18
- data/lib/dependabot/pull_request_creator/pr_name_prefixer.rb +10 -4
- data/lib/dependabot/pull_request_updater/github.rb +2 -2
- 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 +37 -9
@@ -59,20 +59,11 @@ module Dependabot
|
|
59
59
|
end
|
60
60
|
|
61
61
|
def pr_message
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
"#{metadata_cascades}" \
|
68
|
-
"#{ignore_conditions_table}" \
|
69
|
-
"#{prefixed_pr_message_footer}"
|
70
|
-
else
|
71
|
-
"#{suffixed_pr_message_header}" \
|
72
|
-
"#{commit_message_intro}" \
|
73
|
-
"#{metadata_cascades}" \
|
74
|
-
"#{prefixed_pr_message_footer}"
|
75
|
-
end
|
62
|
+
msg = "#{suffixed_pr_message_header}" \
|
63
|
+
"#{commit_message_intro}" \
|
64
|
+
"#{metadata_cascades}" \
|
65
|
+
"#{ignore_conditions_table}" \
|
66
|
+
"#{prefixed_pr_message_footer}"
|
76
67
|
|
77
68
|
truncate_pr_message(msg)
|
78
69
|
rescue StandardError => e
|
@@ -80,10 +71,6 @@ module Dependabot
|
|
80
71
|
suffixed_pr_message_header + prefixed_pr_message_footer
|
81
72
|
end
|
82
73
|
|
83
|
-
def unignore_commands?
|
84
|
-
Experiments.enabled?(:unignore_commands)
|
85
|
-
end
|
86
|
-
|
87
74
|
# Truncate PR message as determined by the pr_message_max_length and pr_message_encoding instance variables
|
88
75
|
# The encoding is used when calculating length, all messages are returned as ruby UTF_8 encoded string
|
89
76
|
def truncate_pr_message(msg)
|
@@ -132,7 +132,7 @@ module Dependabot
|
|
132
132
|
case last_dependabot_commit_style
|
133
133
|
when :gitmoji then true
|
134
134
|
when :conventional_prefix, :conventional_prefix_with_scope
|
135
|
-
|
135
|
+
last_dependabot_commit_title.match?(/: (\[[Ss]ecurity\] )?(B|U)/)
|
136
136
|
else raise "Unknown commit style #{last_dependabot_commit_style}"
|
137
137
|
end
|
138
138
|
end
|
@@ -152,7 +152,7 @@ module Dependabot
|
|
152
152
|
end
|
153
153
|
|
154
154
|
def last_dependabot_commit_style
|
155
|
-
return unless (msg =
|
155
|
+
return unless (msg = last_dependabot_commit_title)
|
156
156
|
|
157
157
|
return :gitmoji if msg.start_with?("⬆️")
|
158
158
|
return :conventional_prefix if msg.match?(/\A(chore|build|upgrade):/i)
|
@@ -162,7 +162,7 @@ module Dependabot
|
|
162
162
|
end
|
163
163
|
|
164
164
|
def last_dependabot_commit_prefix
|
165
|
-
|
165
|
+
last_dependabot_commit_title&.split(/[:(]/)&.first
|
166
166
|
end
|
167
167
|
|
168
168
|
# rubocop:disable Metrics/PerceivedComplexity
|
@@ -245,7 +245,7 @@ module Dependabot
|
|
245
245
|
ANGULAR_PREFIXES.any? { |pre| message.match?(/#{pre}[:(]/i) }
|
246
246
|
end
|
247
247
|
|
248
|
-
return
|
248
|
+
return last_dependabot_commit_title&.start_with?(/[A-Z]/) if semantic_messages.none?
|
249
249
|
|
250
250
|
capitalized_msgs = semantic_messages
|
251
251
|
.select { |m| m.start_with?(/[A-Z]/) }
|
@@ -329,6 +329,12 @@ module Dependabot
|
|
329
329
|
.map(&:strip)
|
330
330
|
end
|
331
331
|
|
332
|
+
def last_dependabot_commit_title
|
333
|
+
return @last_dependabot_commit_title if defined?(@last_dependabot_commit_title)
|
334
|
+
|
335
|
+
@last_dependabot_commit_title = last_dependabot_commit_message&.split("\n")&.first
|
336
|
+
end
|
337
|
+
|
332
338
|
def last_dependabot_commit_message
|
333
339
|
@last_dependabot_commit_message ||=
|
334
340
|
case source.provider
|
@@ -128,7 +128,7 @@ module Dependabot
|
|
128
128
|
if file.type == "submodule"
|
129
129
|
{
|
130
130
|
path: file.path.sub(%r{^/}, ""),
|
131
|
-
mode:
|
131
|
+
mode: Dependabot::DependencyFile::Mode::SUBMODULE,
|
132
132
|
type: "commit",
|
133
133
|
sha: file.content
|
134
134
|
}
|
@@ -146,7 +146,7 @@ module Dependabot
|
|
146
146
|
|
147
147
|
{
|
148
148
|
path: file.realpath,
|
149
|
-
mode:
|
149
|
+
mode: Dependabot::DependencyFile::Mode::FILE,
|
150
150
|
type: "blob"
|
151
151
|
}.merge(content)
|
152
152
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "digest"
|
@@ -8,6 +8,7 @@ require "fileutils"
|
|
8
8
|
require "json"
|
9
9
|
require "open3"
|
10
10
|
require "shellwords"
|
11
|
+
require "sorbet-runtime"
|
11
12
|
require "tmpdir"
|
12
13
|
|
13
14
|
require "dependabot/simple_instrumentor"
|
@@ -18,20 +19,34 @@ require "dependabot"
|
|
18
19
|
|
19
20
|
module Dependabot
|
20
21
|
module SharedHelpers
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
22
|
+
extend T::Sig
|
23
|
+
|
24
|
+
GIT_CONFIG_GLOBAL_PATH = T.let(File.expand_path(".gitconfig", Utils::BUMP_TMP_DIR_PATH), String)
|
25
|
+
USER_AGENT = T.let(
|
26
|
+
"dependabot-core/#{Dependabot::VERSION} " \
|
27
|
+
"#{Excon::USER_AGENT} ruby/#{RUBY_VERSION} " \
|
28
|
+
"(#{RUBY_PLATFORM}) " \
|
29
|
+
"(+https://github.com/dependabot/dependabot-core)".freeze,
|
30
|
+
String
|
31
|
+
)
|
26
32
|
SIGKILL = 9
|
27
33
|
|
34
|
+
sig do
|
35
|
+
type_parameters(:T)
|
36
|
+
.params(
|
37
|
+
directory: String,
|
38
|
+
repo_contents_path: T.nilable(String),
|
39
|
+
block: T.proc.params(arg0: T.any(Pathname, String)).returns(T.type_parameter(:T))
|
40
|
+
)
|
41
|
+
.returns(T.type_parameter(:T))
|
42
|
+
end
|
28
43
|
def self.in_a_temporary_repo_directory(directory = "/", repo_contents_path = nil, &block)
|
29
44
|
if repo_contents_path
|
30
45
|
# If a workspace has been defined to allow orcestration of the git repo
|
31
46
|
# by the runtime we should defer to it, otherwise we prepare the folder
|
32
47
|
# for direct use and yield.
|
33
48
|
if Dependabot::Workspace.active_workspace
|
34
|
-
Dependabot::Workspace.active_workspace.change(&block)
|
49
|
+
T.must(Dependabot::Workspace.active_workspace).change(&block)
|
35
50
|
else
|
36
51
|
path = Pathname.new(File.join(repo_contents_path, directory)).expand_path
|
37
52
|
reset_git_repo(repo_contents_path)
|
@@ -46,9 +61,18 @@ module Dependabot
|
|
46
61
|
end
|
47
62
|
end
|
48
63
|
|
49
|
-
|
64
|
+
sig do
|
65
|
+
type_parameters(:T)
|
66
|
+
.params(
|
67
|
+
directory: String,
|
68
|
+
_block: T.proc.params(arg0: T.any(Pathname, String)).returns(T.type_parameter(:T))
|
69
|
+
)
|
70
|
+
.returns(T.type_parameter(:T))
|
71
|
+
end
|
72
|
+
def self.in_a_temporary_directory(directory = "/", &_block)
|
50
73
|
FileUtils.mkdir_p(Utils::BUMP_TMP_DIR_PATH)
|
51
74
|
tmp_dir = Dir.mktmpdir(Utils::BUMP_TMP_FILE_PREFIX, Utils::BUMP_TMP_DIR_PATH)
|
75
|
+
path = Pathname.new(File.join(tmp_dir, directory)).expand_path
|
52
76
|
|
53
77
|
begin
|
54
78
|
path = Pathname.new(File.join(tmp_dir, directory)).expand_path
|
@@ -60,28 +84,59 @@ module Dependabot
|
|
60
84
|
end
|
61
85
|
|
62
86
|
class HelperSubprocessFailed < Dependabot::DependabotError
|
63
|
-
|
87
|
+
extend T::Sig
|
88
|
+
|
89
|
+
sig { returns(String) }
|
90
|
+
attr_reader :error_class
|
91
|
+
|
92
|
+
sig { returns(T::Hash[Symbol, String]) }
|
93
|
+
attr_reader :error_context
|
94
|
+
|
95
|
+
sig { returns(T.nilable(T::Array[String])) }
|
96
|
+
attr_reader :trace
|
64
97
|
|
98
|
+
sig do
|
99
|
+
params(
|
100
|
+
message: String,
|
101
|
+
error_context: T::Hash[Symbol, String],
|
102
|
+
error_class: T.nilable(String),
|
103
|
+
trace: T.nilable(T::Array[String])
|
104
|
+
).void
|
105
|
+
end
|
65
106
|
def initialize(message:, error_context:, error_class: nil, trace: nil)
|
66
107
|
super(message)
|
67
|
-
@error_class = error_class || "HelperSubprocessFailed"
|
108
|
+
@error_class = T.let(error_class || "HelperSubprocessFailed", String)
|
68
109
|
@error_context = error_context
|
69
|
-
@fingerprint = error_context[:fingerprint] || error_context[:command]
|
110
|
+
@fingerprint = T.let(error_context[:fingerprint] || error_context[:command], T.nilable(String))
|
70
111
|
@trace = trace
|
71
112
|
end
|
72
113
|
|
114
|
+
sig { returns(T::Hash[Symbol, T.untyped]) }
|
73
115
|
def raven_context
|
74
116
|
{ fingerprint: [@fingerprint], extra: @error_context.except(:stderr_output, :fingerprint) }
|
75
117
|
end
|
76
118
|
end
|
77
119
|
|
78
120
|
# Escapes all special characters, e.g. = & | <>
|
121
|
+
sig { params(command: String).returns(String) }
|
79
122
|
def self.escape_command(command)
|
80
123
|
command_parts = command.split.map(&:strip).reject(&:empty?)
|
81
124
|
Shellwords.join(command_parts)
|
82
125
|
end
|
83
126
|
|
84
127
|
# rubocop:disable Metrics/MethodLength
|
128
|
+
# rubocop:disable Metrics/AbcSize
|
129
|
+
sig do
|
130
|
+
params(
|
131
|
+
command: String,
|
132
|
+
function: String,
|
133
|
+
args: T.any(T::Array[String], T::Hash[Symbol, String]),
|
134
|
+
env: T.nilable(T::Hash[String, String]),
|
135
|
+
stderr_to_stdout: T::Boolean,
|
136
|
+
allow_unsafe_shell_command: T::Boolean
|
137
|
+
)
|
138
|
+
.returns(T.nilable(T.any(String, T::Hash[String, T.untyped], T::Array[T::Hash[String, T.untyped]])))
|
139
|
+
end
|
85
140
|
def self.run_helper_subprocess(command:, function:, args:, env: nil,
|
86
141
|
stderr_to_stdout: false,
|
87
142
|
allow_unsafe_shell_command: false)
|
@@ -95,11 +150,11 @@ module Dependabot
|
|
95
150
|
if ENV["DEBUG_FUNCTION"] == function
|
96
151
|
puts helper_subprocess_bash_command(stdin_data: stdin_data, command: cmd, env: env)
|
97
152
|
# Pause execution so we can run helpers inside the temporary directory
|
98
|
-
debugger
|
153
|
+
T.unsafe(self).debugger
|
99
154
|
end
|
100
155
|
|
101
156
|
env_cmd = [env, cmd].compact
|
102
|
-
stdout, stderr, process = Open3.capture3(*env_cmd, stdin_data: stdin_data)
|
157
|
+
stdout, stderr, process = T.unsafe(Open3).capture3(*env_cmd, stdin_data: stdin_data)
|
103
158
|
time_taken = Time.now - start
|
104
159
|
|
105
160
|
if ENV["DEBUG_HELPERS"] == "true"
|
@@ -126,24 +181,27 @@ module Dependabot
|
|
126
181
|
|
127
182
|
check_out_of_memory_error(stderr, error_context)
|
128
183
|
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
184
|
+
begin
|
185
|
+
response = JSON.parse(stdout)
|
186
|
+
return response["result"] if process.success?
|
187
|
+
|
188
|
+
raise HelperSubprocessFailed.new(
|
189
|
+
message: response["error"],
|
190
|
+
error_class: response["error_class"],
|
191
|
+
error_context: error_context,
|
192
|
+
trace: response["trace"]
|
193
|
+
)
|
194
|
+
rescue JSON::ParserError
|
195
|
+
raise HelperSubprocessFailed.new(
|
196
|
+
message: stdout || "No output from command",
|
197
|
+
error_class: "JSON::ParserError",
|
198
|
+
error_context: error_context
|
199
|
+
)
|
200
|
+
end
|
144
201
|
end
|
145
202
|
|
146
203
|
# rubocop:enable Metrics/MethodLength
|
204
|
+
sig { params(stderr: T.nilable(String), error_context: T::Hash[Symbol, String]).void }
|
147
205
|
def self.check_out_of_memory_error(stderr, error_context)
|
148
206
|
return unless stderr&.include?("JavaScript heap out of memory")
|
149
207
|
|
@@ -154,12 +212,14 @@ module Dependabot
|
|
154
212
|
)
|
155
213
|
end
|
156
214
|
|
215
|
+
sig { returns(T::Array[T.class_of(Excon::Middleware::Base)]) }
|
157
216
|
def self.excon_middleware
|
158
|
-
Excon.defaults[:middlewares] +
|
217
|
+
T.must(T.cast(Excon.defaults, T::Hash[Symbol, T::Array[T.class_of(Excon::Middleware::Base)]])[:middlewares]) +
|
159
218
|
[Excon::Middleware::Decompress] +
|
160
219
|
[Excon::Middleware::RedirectFollower]
|
161
220
|
end
|
162
221
|
|
222
|
+
sig { params(headers: T.nilable(T::Hash[String, String])).returns(T::Hash[String, String]) }
|
163
223
|
def self.excon_headers(headers = nil)
|
164
224
|
headers ||= {}
|
165
225
|
{
|
@@ -167,9 +227,10 @@ module Dependabot
|
|
167
227
|
}.merge(headers)
|
168
228
|
end
|
169
229
|
|
230
|
+
sig { params(options: T.nilable(T::Hash[Symbol, T.untyped])).returns(T::Hash[Symbol, T.untyped]) }
|
170
231
|
def self.excon_defaults(options = nil)
|
171
232
|
options ||= {}
|
172
|
-
headers = options.delete(:headers)
|
233
|
+
headers = T.cast(options.delete(:headers), T.nilable(T::Hash[String, String]))
|
173
234
|
{
|
174
235
|
instrumentor: Dependabot::SimpleInstrumentor,
|
175
236
|
connect_timeout: 5,
|
@@ -182,7 +243,15 @@ module Dependabot
|
|
182
243
|
}.merge(options)
|
183
244
|
end
|
184
245
|
|
185
|
-
|
246
|
+
sig do
|
247
|
+
type_parameters(:T)
|
248
|
+
.params(
|
249
|
+
credentials: T::Array[T::Hash[String, String]],
|
250
|
+
_block: T.proc.returns(T.type_parameter(:T))
|
251
|
+
)
|
252
|
+
.returns(T.type_parameter(:T))
|
253
|
+
end
|
254
|
+
def self.with_git_configured(credentials:, &_block)
|
186
255
|
safe_directories = find_safe_directories
|
187
256
|
|
188
257
|
FileUtils.mkdir_p(Utils::BUMP_TMP_DIR_PATH)
|
@@ -203,18 +272,20 @@ module Dependabot
|
|
203
272
|
end
|
204
273
|
|
205
274
|
# Handle SCP-style git URIs
|
275
|
+
sig { params(uri: String).returns(String) }
|
206
276
|
def self.scp_to_standard(uri)
|
207
277
|
return uri unless uri.start_with?("git@")
|
208
278
|
|
209
|
-
"https://#{uri.split('git@').last.sub(%r{:/?}, '/')}"
|
279
|
+
"https://#{T.must(uri.split('git@').last).sub(%r{:/?}, '/')}"
|
210
280
|
end
|
211
281
|
|
282
|
+
sig { returns(String) }
|
212
283
|
def self.credential_helper_path
|
213
284
|
File.join(__dir__, "../../bin/git-credential-store-immutable")
|
214
285
|
end
|
215
286
|
|
216
|
-
# rubocop:disable Metrics/AbcSize
|
217
287
|
# rubocop:disable Metrics/PerceivedComplexity
|
288
|
+
sig { params(credentials: T::Array[T::Hash[String, String]], safe_directories: T::Array[String]).void }
|
218
289
|
def self.configure_git_to_use_https_with_credentials(credentials, safe_directories)
|
219
290
|
File.open(GIT_CONFIG_GLOBAL_PATH, "w") do |file|
|
220
291
|
file << "# Generated by dependabot/dependabot-core"
|
@@ -274,6 +345,7 @@ module Dependabot
|
|
274
345
|
# rubocop:enable Metrics/AbcSize
|
275
346
|
# rubocop:enable Metrics/PerceivedComplexity
|
276
347
|
|
348
|
+
sig { params(host: String).void }
|
277
349
|
def self.configure_git_to_use_https(host)
|
278
350
|
# NOTE: we use --global here (rather than --system) so that Dependabot
|
279
351
|
# can be run without privileged access
|
@@ -299,6 +371,7 @@ module Dependabot
|
|
299
371
|
)
|
300
372
|
end
|
301
373
|
|
374
|
+
sig { params(path: String).void }
|
302
375
|
def self.reset_git_repo(path)
|
303
376
|
Dir.chdir(path) do
|
304
377
|
run_shell_command("git reset HEAD --hard")
|
@@ -306,6 +379,7 @@ module Dependabot
|
|
306
379
|
end
|
307
380
|
end
|
308
381
|
|
382
|
+
sig { returns(T::Array[String]) }
|
309
383
|
def self.find_safe_directories
|
310
384
|
# to preserve safe directories from global .gitconfig
|
311
385
|
output, process = Open3.capture2("git config --global --get-all safe.directory")
|
@@ -314,6 +388,15 @@ module Dependabot
|
|
314
388
|
safe_directories
|
315
389
|
end
|
316
390
|
|
391
|
+
sig do
|
392
|
+
params(
|
393
|
+
command: String,
|
394
|
+
allow_unsafe_shell_command: T::Boolean,
|
395
|
+
env: T.nilable(T::Hash[String, String]),
|
396
|
+
fingerprint: T.nilable(String),
|
397
|
+
stderr_to_stdout: T::Boolean
|
398
|
+
).returns(String)
|
399
|
+
end
|
317
400
|
def self.run_shell_command(command,
|
318
401
|
allow_unsafe_shell_command: false,
|
319
402
|
env: {},
|
@@ -347,6 +430,7 @@ module Dependabot
|
|
347
430
|
)
|
348
431
|
end
|
349
432
|
|
433
|
+
sig { params(command: String, stdin_data: String, env: T.nilable(T::Hash[String, String])).returns(String) }
|
350
434
|
def self.helper_subprocess_bash_command(command:, stdin_data:, env:)
|
351
435
|
escaped_stdin_data = stdin_data.gsub("\"", "\\\"")
|
352
436
|
env_keys = env ? env.compact.map { |k, v| "#{k}=#{v}" }.join(" ") + " " : ""
|
@@ -1,16 +1,35 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strong
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
+
require "sorbet-runtime"
|
5
|
+
|
4
6
|
module Dependabot
|
5
7
|
module SimpleInstrumentor
|
6
8
|
class << self
|
7
|
-
|
9
|
+
extend T::Sig
|
10
|
+
extend T::Generic
|
11
|
+
|
12
|
+
sig { returns(T.nilable(T::Array[T.proc.params(name: String, params: T::Hash[Symbol, T.untyped]).void])) }
|
13
|
+
attr_accessor :subscribers
|
8
14
|
|
15
|
+
sig { params(block: T.proc.params(name: String, params: T::Hash[Symbol, T.untyped]).void).void }
|
9
16
|
def subscribe(&block)
|
10
|
-
@subscribers ||=
|
17
|
+
@subscribers ||= T.let(
|
18
|
+
[],
|
19
|
+
T.nilable(T::Array[T.proc.params(name: String, params: T::Hash[Symbol, T.untyped]).void])
|
20
|
+
)
|
11
21
|
@subscribers << block
|
12
22
|
end
|
13
23
|
|
24
|
+
sig do
|
25
|
+
type_parameters(:T)
|
26
|
+
.params(
|
27
|
+
name: String,
|
28
|
+
params: T::Hash[Symbol, T.untyped],
|
29
|
+
block: T.proc.returns(T.type_parameter(:T))
|
30
|
+
)
|
31
|
+
.returns(T.nilable(T.type_parameter(:T)))
|
32
|
+
end
|
14
33
|
def instrument(name, params = {}, &block)
|
15
34
|
@subscribers&.each { |s| s.call(name, params) }
|
16
35
|
yield if block
|
data/lib/dependabot/source.rb
CHANGED
@@ -1,8 +1,12 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
+
require "sorbet-runtime"
|
5
|
+
|
4
6
|
module Dependabot
|
5
7
|
class Source
|
8
|
+
extend T::Sig
|
9
|
+
|
6
10
|
GITHUB_SOURCE = %r{
|
7
11
|
(?<provider>github)
|
8
12
|
(?:\.com)[/:]
|
@@ -59,24 +63,48 @@ module Dependabot
|
|
59
63
|
(?:#{CODECOMMIT_SOURCE})
|
60
64
|
/x
|
61
65
|
|
62
|
-
IGNORED_PROVIDER_HOSTS = %w(gitbox.apache.org svn.apache.org fuchsia.googlesource.com).freeze
|
66
|
+
IGNORED_PROVIDER_HOSTS = T.let(%w(gitbox.apache.org svn.apache.org fuchsia.googlesource.com).freeze,
|
67
|
+
T::Array[String])
|
68
|
+
|
69
|
+
sig { returns(String) }
|
70
|
+
attr_accessor :provider
|
71
|
+
|
72
|
+
sig { returns(String) }
|
73
|
+
attr_accessor :repo
|
74
|
+
|
75
|
+
sig { returns(T.nilable(String)) }
|
76
|
+
attr_accessor :directory
|
63
77
|
|
64
|
-
|
65
|
-
|
78
|
+
sig { returns(T.nilable(T::Array[String])) }
|
79
|
+
attr_accessor :directories
|
66
80
|
|
81
|
+
sig { returns(T.nilable(String)) }
|
82
|
+
attr_accessor :branch
|
83
|
+
|
84
|
+
sig { returns(T.nilable(String)) }
|
85
|
+
attr_accessor :commit
|
86
|
+
|
87
|
+
sig { returns(String) }
|
88
|
+
attr_accessor :hostname
|
89
|
+
|
90
|
+
sig { returns(T.nilable(String)) }
|
91
|
+
attr_accessor :api_endpoint
|
92
|
+
|
93
|
+
sig { params(url_string: T.nilable(String)).returns(T.nilable(Source)) }
|
67
94
|
def self.from_url(url_string)
|
68
95
|
return github_enterprise_from_url(url_string) unless url_string&.match?(SOURCE_REGEX)
|
69
96
|
|
70
|
-
captures = url_string.match(SOURCE_REGEX).named_captures
|
97
|
+
captures = T.must(url_string.match(SOURCE_REGEX)).named_captures
|
71
98
|
|
72
99
|
new(
|
73
|
-
provider: captures.fetch("provider"),
|
74
|
-
repo: captures.fetch("repo").delete_suffix(".git").delete_suffix("."),
|
100
|
+
provider: T.must(captures.fetch("provider")),
|
101
|
+
repo: T.must(captures.fetch("repo")).delete_suffix(".git").delete_suffix("."),
|
75
102
|
directory: captures.fetch("directory"),
|
76
103
|
branch: captures.fetch("branch")
|
77
104
|
)
|
78
105
|
end
|
79
106
|
|
107
|
+
sig { params(url_string: T.nilable(String)).returns(T.nilable(Source)) }
|
80
108
|
def self.github_enterprise_from_url(url_string)
|
81
109
|
captures = url_string&.match(GITHUB_ENTERPRISE_SOURCE)&.named_captures
|
82
110
|
return unless captures
|
@@ -88,7 +116,7 @@ module Dependabot
|
|
88
116
|
|
89
117
|
new(
|
90
118
|
provider: "github",
|
91
|
-
repo: captures.fetch("repo").delete_suffix(".git").delete_suffix("."),
|
119
|
+
repo: T.must(captures.fetch("repo")).delete_suffix(".git").delete_suffix("."),
|
92
120
|
directory: captures.fetch("directory"),
|
93
121
|
branch: captures.fetch("branch"),
|
94
122
|
hostname: captures.fetch("host"),
|
@@ -96,18 +124,30 @@ module Dependabot
|
|
96
124
|
)
|
97
125
|
end
|
98
126
|
|
127
|
+
sig { params(base_url: String).returns(T::Boolean) }
|
99
128
|
def self.github_enterprise?(base_url)
|
100
129
|
resp = Excon.get(File.join(base_url, "status"))
|
101
130
|
resp.status == 200 &&
|
102
131
|
# Alternatively: resp.headers["Server"] == "GitHub.com", but this
|
103
132
|
# currently doesn't work with development environments
|
104
|
-
resp.headers["X-GitHub-Request-Id"] &&
|
105
|
-
!resp.headers["X-GitHub-Request-Id"].empty?
|
133
|
+
((resp.headers["X-GitHub-Request-Id"] && !resp.headers["X-GitHub-Request-Id"]&.empty?) || false)
|
106
134
|
rescue StandardError
|
107
135
|
false
|
108
136
|
end
|
109
137
|
|
110
|
-
|
138
|
+
sig do
|
139
|
+
params(
|
140
|
+
provider: String,
|
141
|
+
repo: String,
|
142
|
+
directory: T.nilable(String),
|
143
|
+
directories: T.nilable(T::Array[String]),
|
144
|
+
branch: T.nilable(String),
|
145
|
+
commit: T.nilable(String),
|
146
|
+
hostname: T.nilable(String),
|
147
|
+
api_endpoint: T.nilable(String)
|
148
|
+
).void
|
149
|
+
end
|
150
|
+
def initialize(provider:, repo:, directory: nil, directories: nil, branch: nil, commit: nil,
|
111
151
|
hostname: nil, api_endpoint: nil)
|
112
152
|
if (hostname.nil? ^ api_endpoint.nil?) && (provider != "codecommit")
|
113
153
|
msg = "Both hostname and api_endpoint must be specified if either " \
|
@@ -119,16 +159,19 @@ module Dependabot
|
|
119
159
|
@provider = provider
|
120
160
|
@repo = repo
|
121
161
|
@directory = directory
|
162
|
+
@directories = directories
|
122
163
|
@branch = branch
|
123
164
|
@commit = commit
|
124
|
-
@hostname = hostname || default_hostname(provider)
|
125
|
-
@api_endpoint = api_endpoint || default_api_endpoint(provider)
|
165
|
+
@hostname = T.let(hostname || default_hostname(provider), String)
|
166
|
+
@api_endpoint = T.let(api_endpoint || default_api_endpoint(provider), T.nilable(String))
|
126
167
|
end
|
127
168
|
|
169
|
+
sig { returns(String) }
|
128
170
|
def url
|
129
171
|
"https://" + hostname + "/" + repo
|
130
172
|
end
|
131
173
|
|
174
|
+
sig { returns(String) }
|
132
175
|
def url_with_directory
|
133
176
|
return url if [nil, ".", "/"].include?(directory)
|
134
177
|
|
@@ -149,25 +192,29 @@ module Dependabot
|
|
149
192
|
end
|
150
193
|
end
|
151
194
|
|
195
|
+
sig { returns(String) }
|
152
196
|
def organization
|
153
|
-
repo.split("/").first
|
197
|
+
T.must(repo.split("/").first)
|
154
198
|
end
|
155
199
|
|
200
|
+
sig { returns(String) }
|
156
201
|
def project
|
157
202
|
raise "Project is an Azure DevOps concept only" unless provider == "azure"
|
158
203
|
|
159
204
|
parts = repo.split("/_git/")
|
160
|
-
return parts.first.split("/").last if parts.first
|
205
|
+
return T.must(T.must(parts.first).split("/").last) if parts.first&.split("/")&.count == 2
|
161
206
|
|
162
|
-
parts.last
|
207
|
+
T.must(parts.last)
|
163
208
|
end
|
164
209
|
|
210
|
+
sig { returns(String) }
|
165
211
|
def unscoped_repo
|
166
|
-
repo.split("/").last
|
212
|
+
T.must(repo.split("/").last)
|
167
213
|
end
|
168
214
|
|
169
215
|
private
|
170
216
|
|
217
|
+
sig { params(provider: String).returns(String) }
|
171
218
|
def default_hostname(provider)
|
172
219
|
case provider
|
173
220
|
when "github" then "github.com"
|
@@ -179,6 +226,7 @@ module Dependabot
|
|
179
226
|
end
|
180
227
|
end
|
181
228
|
|
229
|
+
sig { params(provider: String).returns(T.nilable(String)) }
|
182
230
|
def default_api_endpoint(provider)
|
183
231
|
case provider
|
184
232
|
when "github" then "https://api.github.com/"
|
@@ -1,9 +1,20 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strong
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
+
require "sorbet-runtime"
|
5
|
+
|
4
6
|
module Dependabot
|
5
7
|
module UpdateCheckers
|
6
8
|
module VersionFilters
|
9
|
+
extend T::Sig
|
10
|
+
|
11
|
+
sig do
|
12
|
+
params(
|
13
|
+
versions_array: T::Array[T.any(Gem::Version, T::Hash[Symbol, String])],
|
14
|
+
security_advisories: T::Array[SecurityAdvisory]
|
15
|
+
)
|
16
|
+
.returns(T::Array[T.any(Gem::Version, T::Hash[Symbol, String])])
|
17
|
+
end
|
7
18
|
def self.filter_vulnerable_versions(versions_array, security_advisories)
|
8
19
|
versions_array.reject do |v|
|
9
20
|
security_advisories.any? do |a|
|