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
@@ -85,7 +85,7 @@ module Dependabot
|
|
85
85
|
msg = (msg[0..trunc_length] + tr_msg)
|
86
86
|
end
|
87
87
|
# if we used a custom encoding for calculating length, then we need to force back to UTF-8
|
88
|
-
msg.
|
88
|
+
msg = msg.encode("utf-8", "binary", invalid: :replace, undef: :replace) unless pr_message_encoding.nil?
|
89
89
|
msg
|
90
90
|
end
|
91
91
|
|
@@ -162,7 +162,13 @@ module Dependabot
|
|
162
162
|
|
163
163
|
def group_pr_name
|
164
164
|
updates = dependencies.map(&:name).uniq.count
|
165
|
-
|
165
|
+
|
166
|
+
if source&.directories
|
167
|
+
"bump the #{dependency_group.name} across #{source.directories.count} directories " \
|
168
|
+
"with #{updates} update#{'s' if updates > 1}"
|
169
|
+
else
|
170
|
+
"bump the #{dependency_group.name} group#{pr_name_directory} with #{updates} update#{'s' if updates > 1}"
|
171
|
+
end
|
166
172
|
end
|
167
173
|
|
168
174
|
def pr_name_prefix
|
@@ -260,6 +266,8 @@ module Dependabot
|
|
260
266
|
# rubocop:disable Metrics/PerceivedComplexity
|
261
267
|
# rubocop:disable Metrics/AbcSize
|
262
268
|
def version_commit_message_intro
|
269
|
+
return multi_directory_group_intro if dependency_group && source&.directories
|
270
|
+
|
263
271
|
return group_intro if dependency_group
|
264
272
|
|
265
273
|
return multidependency_property_intro if dependencies.count > 1 && updating_a_property?
|
@@ -346,6 +354,42 @@ module Dependabot
|
|
346
354
|
msg
|
347
355
|
end
|
348
356
|
|
357
|
+
def multi_directory_group_intro
|
358
|
+
msg = ""
|
359
|
+
|
360
|
+
source.directories.each do |directory|
|
361
|
+
dependencies_in_directory = dependencies.select { |dep| dep.metadata[:directory] == directory }
|
362
|
+
next unless dependencies_in_directory.any?
|
363
|
+
|
364
|
+
update_count = dependencies_in_directory.map(&:name).uniq.count
|
365
|
+
|
366
|
+
msg += "Bumps the #{dependency_group.name} " \
|
367
|
+
"with #{update_count} update#{update_count > 1 ? 's' : ''} in the #{directory} directory:"
|
368
|
+
|
369
|
+
msg += if update_count >= 5
|
370
|
+
header = %w(Package From To)
|
371
|
+
rows = dependencies_in_directory.map do |dep|
|
372
|
+
[
|
373
|
+
dependency_link(dep),
|
374
|
+
"`#{dep.humanized_previous_version}`",
|
375
|
+
"`#{dep.humanized_version}`"
|
376
|
+
]
|
377
|
+
end
|
378
|
+
"\n\n#{table([header] + rows)}"
|
379
|
+
elsif update_count > 1
|
380
|
+
dependency_links_in_directory = dependency_links_for_directory(directory)
|
381
|
+
" #{dependency_links_in_directory[0..-2].join(', ')} and #{dependency_links_in_directory[-1]}."
|
382
|
+
else
|
383
|
+
dependency_links_in_directory = dependency_links_for_directory(directory)
|
384
|
+
" #{dependency_links_in_directory.first}."
|
385
|
+
end
|
386
|
+
|
387
|
+
msg += "\n"
|
388
|
+
end
|
389
|
+
|
390
|
+
msg
|
391
|
+
end
|
392
|
+
|
349
393
|
def group_intro
|
350
394
|
update_count = dependencies.map(&:name).uniq.count
|
351
395
|
|
@@ -427,6 +471,12 @@ module Dependabot
|
|
427
471
|
@dependency_links = uniq_deps.map { |dep| dependency_link(dep) }
|
428
472
|
end
|
429
473
|
|
474
|
+
def dependency_links_for_directory(directory)
|
475
|
+
dependencies_in_directory = dependencies.select { |dep| dep.metadata[:directory] == directory }
|
476
|
+
uniq_deps = dependencies_in_directory.each_with_object({}) { |dep, memo| memo[dep.name] ||= dep }.values
|
477
|
+
@dependency_links = uniq_deps.map { |dep| dependency_link(dep) }
|
478
|
+
end
|
479
|
+
|
430
480
|
def dependency_link(dependency)
|
431
481
|
if source_url(dependency)
|
432
482
|
"[#{dependency.display_name}](#{source_url(dependency)})"
|
@@ -483,8 +533,8 @@ module Dependabot
|
|
483
533
|
"| #{row.join(' | ')} |"
|
484
534
|
end
|
485
535
|
|
486
|
-
def metadata_cascades
|
487
|
-
return metadata_cascades_for_dep(dependencies.first) if dependencies.one?
|
536
|
+
def metadata_cascades # rubocop:disable Metrics/PerceivedComplexity
|
537
|
+
return metadata_cascades_for_dep(dependencies.first) if dependencies.one? && !dependency_group
|
488
538
|
|
489
539
|
dependencies.map do |dep|
|
490
540
|
msg = if dep.removed?
|
@@ -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
|
@@ -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|
|