dependabot-common 0.242.1 → 0.243.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/lib/dependabot/clients/azure.rb +112 -24
  3. data/lib/dependabot/clients/bitbucket.rb +1 -1
  4. data/lib/dependabot/credential.rb +40 -0
  5. data/lib/dependabot/dependency_group.rb +8 -2
  6. data/lib/dependabot/file_fetchers/base.rb +35 -3
  7. data/lib/dependabot/file_parsers/base.rb +4 -3
  8. data/lib/dependabot/file_updaters/artifact_updater.rb +1 -1
  9. data/lib/dependabot/file_updaters/base.rb +4 -3
  10. data/lib/dependabot/file_updaters/vendor_updater.rb +1 -1
  11. data/lib/dependabot/git_commit_checker.rb +3 -2
  12. data/lib/dependabot/git_metadata_fetcher.rb +3 -2
  13. data/lib/dependabot/metadata_finders/base/changelog_finder.rb +151 -56
  14. data/lib/dependabot/metadata_finders/base/changelog_pruner.rb +45 -14
  15. data/lib/dependabot/metadata_finders/base/commits_finder.rb +123 -53
  16. data/lib/dependabot/metadata_finders/base/release_finder.rb +84 -31
  17. data/lib/dependabot/metadata_finders/base.rb +4 -3
  18. data/lib/dependabot/pull_request_creator/azure.rb +1 -1
  19. data/lib/dependabot/pull_request_creator/branch_namer/solo_strategy.rb +2 -2
  20. data/lib/dependabot/pull_request_creator/labeler.rb +3 -2
  21. data/lib/dependabot/pull_request_creator/message_builder/issue_linker.rb +14 -6
  22. data/lib/dependabot/pull_request_creator/message_builder/metadata_presenter.rb +50 -9
  23. data/lib/dependabot/pull_request_creator/message_builder.rb +2 -2
  24. data/lib/dependabot/pull_request_creator/pr_name_prefixer.rb +164 -58
  25. data/lib/dependabot/pull_request_creator.rb +6 -5
  26. data/lib/dependabot/pull_request_updater.rb +3 -2
  27. data/lib/dependabot/security_advisory.rb +2 -2
  28. data/lib/dependabot/shared_helpers.rb +3 -2
  29. data/lib/dependabot/utils.rb +1 -13
  30. data/lib/dependabot/workspace/git.rb +1 -1
  31. data/lib/dependabot.rb +1 -1
  32. metadata +4 -3
@@ -1,6 +1,7 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
+ require "sorbet-runtime"
4
5
  require "dependabot/clients/github_with_retries"
5
6
  require "dependabot/clients/gitlab_with_retries"
6
7
  require "dependabot/clients/bitbucket_with_retries"
@@ -8,53 +9,74 @@ require "dependabot/shared_helpers"
8
9
  require "dependabot/git_metadata_fetcher"
9
10
  require "dependabot/git_commit_checker"
10
11
  require "dependabot/metadata_finders/base"
12
+ require "dependabot/credential"
11
13
 
12
14
  module Dependabot
13
15
  module MetadataFinders
14
16
  class Base
15
17
  class CommitsFinder
16
- attr_reader :source, :dependency, :credentials
18
+ extend T::Sig
17
19
 
20
+ sig { returns(T.nilable(Dependabot::Source)) }
21
+ attr_reader :source
22
+
23
+ sig { returns(Dependabot::Dependency) }
24
+ attr_reader :dependency
25
+
26
+ sig { returns(T::Array[Dependabot::Credential]) }
27
+ attr_reader :credentials
28
+
29
+ sig do
30
+ params(
31
+ source: T.nilable(Dependabot::Source),
32
+ dependency: Dependabot::Dependency,
33
+ credentials: T::Array[Dependabot::Credential]
34
+ )
35
+ .void
36
+ end
18
37
  def initialize(source:, dependency:, credentials:)
19
38
  @source = source
20
39
  @dependency = dependency
21
40
  @credentials = credentials
22
41
  end
23
42
 
43
+ sig { returns(T.nilable(String)) }
24
44
  def commits_url
25
45
  return unless source
26
- return if source.provider == "codecommit" # TODO: Fetch Codecommit commits
46
+ return if T.must(source).provider == "codecommit" # TODO: Fetch Codecommit commits
27
47
 
28
48
  path =
29
- case source.provider
49
+ case T.must(source).provider
30
50
  when "github" then github_compare_path(new_tag, previous_tag)
31
51
  when "bitbucket" then bitbucket_compare_path(new_tag, previous_tag)
32
52
  when "gitlab" then gitlab_compare_path(new_tag, previous_tag)
33
53
  when "azure" then azure_compare_path(new_tag, previous_tag)
34
- else raise "Unexpected source provider '#{source.provider}'"
54
+ else raise "Unexpected source provider '#{T.must(source).provider}'"
35
55
  end
36
56
 
37
- "#{source.url}/#{path}"
57
+ "#{T.must(source).url}/#{path}"
38
58
  end
39
59
 
60
+ sig { returns(T::Array[T::Hash[Symbol, String]]) }
40
61
  def commits
41
62
  return [] unless source
42
63
  return [] unless new_tag && previous_tag
43
64
 
44
- case source.provider
65
+ case T.must(source).provider
45
66
  when "github" then fetch_github_commits
46
67
  when "bitbucket" then fetch_bitbucket_commits
47
68
  when "gitlab" then fetch_gitlab_commits
48
69
  when "azure" then fetch_azure_commits
49
70
  when "codecommit" then [] # TODO: Fetch Codecommit commits
50
- else raise "Unexpected source provider '#{source.provider}'"
71
+ else raise "Unexpected source provider '#{T.must(source).provider}'"
51
72
  end
52
73
  end
53
74
 
75
+ sig { returns(T.nilable(String)) }
54
76
  def new_tag
55
77
  new_version = dependency.version
56
78
 
57
- return new_version if git_source?(dependency.requirements) && git_sha?(new_version)
79
+ return T.must(new_version) if git_source?(dependency.requirements) && git_sha?(new_version)
58
80
 
59
81
  return new_ref if new_ref && ref_changed?
60
82
 
@@ -68,6 +90,7 @@ module Dependabot
68
90
  private
69
91
 
70
92
  # rubocop:disable Metrics/PerceivedComplexity
93
+ sig { returns(T.nilable(String)) }
71
94
  def previous_tag
72
95
  previous_version = dependency.previous_version
73
96
 
@@ -89,15 +112,17 @@ module Dependabot
89
112
 
90
113
  # rubocop:enable Metrics/PerceivedComplexity
91
114
 
115
+ sig { returns(String) }
92
116
  def lowest_tag_satisfying_previous_requirements
93
117
  tags = dependency_tags
94
118
  .select { |t| version_from_tag(t) }
95
119
  .select { |t| satisfies_previous_reqs?(version_from_tag(t)) }
96
120
  .sort_by { |t| [version_from_tag(t), t.length] }
97
121
 
98
- tags.find { |t| t.include?(dependency.name) } || tags.first
122
+ tags.find { |t| t.include?(dependency.name) } || T.must(tags.first)
99
123
  end
100
124
 
125
+ sig { params(tag: String).returns(T.nilable(Dependabot::Version)) }
101
126
  def version_from_tag(tag)
102
127
  version_class.new(tag.gsub(/^v/, "")) if version_class.correct?(tag.gsub(/^v/, ""))
103
128
 
@@ -107,8 +132,9 @@ module Dependabot
107
132
  version_class.new(tag.gsub(/^[^\d]*/, ""))
108
133
  end
109
134
 
135
+ sig { params(version: T.nilable(Dependabot::Version)).returns(T::Boolean) }
110
136
  def satisfies_previous_reqs?(version)
111
- dependency.previous_requirements.all? do |req|
137
+ T.must(dependency.previous_requirements).all? do |req|
112
138
  next true unless req.fetch(:requirement)
113
139
 
114
140
  requirement_class
@@ -118,31 +144,35 @@ module Dependabot
118
144
  end
119
145
 
120
146
  # TODO: Refactor me so that Composer doesn't need to be special cased
147
+ sig { params(requirements: T.nilable(T::Array[T::Hash[Symbol, T.untyped]])).returns(T::Boolean) }
121
148
  def git_source?(requirements)
122
149
  # Special case Composer, which uses git as a source but handles tags
123
150
  # internally
124
151
  return false if dependency.package_manager == "composer"
125
152
 
126
- sources = requirements.map { |r| r.fetch(:source) }.uniq.compact
127
- return false if sources.empty?
153
+ sources = requirements&.map { |r| r.fetch(:source) }&.uniq&.compact
154
+ return false if sources.nil? || sources.empty?
128
155
 
129
156
  sources.all? { |s| s[:type] == "git" || s["type"] == "git" }
130
157
  end
131
158
 
159
+ sig { returns(T::Boolean) }
132
160
  def ref_changed?
133
161
  # We could go from multiple previous refs (nil) to a single new ref
134
162
  previous_ref != new_ref
135
163
  end
136
164
 
165
+ sig { returns(T.nilable(String)) }
137
166
  def previous_ref
138
167
  return unless git_source?(dependency.previous_requirements)
139
168
 
140
- previous_refs = dependency.previous_requirements.filter_map do |r|
169
+ previous_refs = T.must(dependency.previous_requirements).filter_map do |r|
141
170
  r.dig(:source, "ref") || r.dig(:source, :ref)
142
171
  end.uniq
143
172
  previous_refs.first if previous_refs.count == 1
144
173
  end
145
174
 
175
+ sig { returns(T.nilable(String)) }
146
176
  def new_ref
147
177
  return unless git_source?(dependency.previous_requirements)
148
178
 
@@ -152,6 +182,7 @@ module Dependabot
152
182
  new_refs.first if new_refs.count == 1
153
183
  end
154
184
 
185
+ sig { params(tag: String, version: T.nilable(String)).returns(T::Boolean) }
155
186
  def tag_matches_version?(tag, version)
156
187
  return false unless version
157
188
 
@@ -160,21 +191,27 @@ module Dependabot
160
191
  version_regex = GitCommitChecker::VERSION_REGEX
161
192
  return false unless tag.match?(version_regex)
162
193
 
163
- tag_version = tag.match(version_regex).named_captures.fetch("version")
194
+ tag_version = tag.match(version_regex)&.named_captures&.fetch("version")
164
195
  return false unless version_class.correct?(tag_version)
165
196
 
166
197
  version_class.new(tag_version) == version_class.new(version)
167
198
  end
168
199
 
200
+ sig { returns(T::Array[String]) }
169
201
  def dependency_tags
170
- @dependency_tags ||= fetch_dependency_tags
202
+ @dependency_tags ||=
203
+ T.let(
204
+ fetch_dependency_tags,
205
+ T.nilable(T::Array[String])
206
+ )
171
207
  end
172
208
 
209
+ sig { returns(T::Array[String]) }
173
210
  def fetch_dependency_tags
174
211
  return [] unless source
175
212
 
176
213
  GitMetadataFetcher
177
- .new(url: source.url, credentials: credentials)
214
+ .new(url: T.must(source).url, credentials: credentials)
178
215
  .tags
179
216
  .map(&:name)
180
217
  rescue Dependabot::GitDependenciesNotReachable,
@@ -183,12 +220,13 @@ module Dependabot
183
220
  []
184
221
  end
185
222
 
223
+ sig { params(new_tag: T.nilable(String), previous_tag: T.nilable(String)).returns(String) }
186
224
  def github_compare_path(new_tag, previous_tag)
187
225
  if part_of_monorepo?
188
226
  # If part of a monorepo then we're better off linking to the commits
189
227
  # for that directory than trying to put together a compare URL
190
228
  Pathname
191
- .new(File.join("commits/#{new_tag || 'HEAD'}", source.directory))
229
+ .new(File.join("commits/#{new_tag || 'HEAD'}", T.must(source).directory))
192
230
  .cleanpath.to_path
193
231
  elsif new_tag && previous_tag
194
232
  "compare/#{previous_tag}...#{new_tag}"
@@ -197,6 +235,7 @@ module Dependabot
197
235
  end
198
236
  end
199
237
 
238
+ sig { params(new_tag: T.nilable(String), previous_tag: T.nilable(String)).returns(String) }
200
239
  def bitbucket_compare_path(new_tag, previous_tag)
201
240
  if new_tag && previous_tag
202
241
  "branches/compare/#{new_tag}..#{previous_tag}"
@@ -207,6 +246,7 @@ module Dependabot
207
246
  end
208
247
  end
209
248
 
249
+ sig { params(new_tag: T.nilable(String), previous_tag: T.nilable(String)).returns(String) }
210
250
  def gitlab_compare_path(new_tag, previous_tag)
211
251
  if new_tag && previous_tag
212
252
  "compare/#{previous_tag}...#{new_tag}"
@@ -217,6 +257,7 @@ module Dependabot
217
257
  end
218
258
  end
219
259
 
260
+ sig { params(new_tag: T.nilable(String), previous_tag: T.nilable(String)).returns(String) }
220
261
  def azure_compare_path(new_tag, previous_tag)
221
262
  # GC for commits, GT for tags, and GB for branches
222
263
  type = git_sha?(new_tag) ? "GC" : "GT"
@@ -229,24 +270,25 @@ module Dependabot
229
270
  end
230
271
  end
231
272
 
273
+ sig { returns(T::Array[T::Hash[Symbol, String]]) }
232
274
  def fetch_github_commits
233
275
  commits =
234
276
  begin
235
277
  # If part of a monorepo we make two requests in order to get only
236
278
  # the commits relevant to the given path
237
- path = source.directory&.gsub(%r{^[./]+}, "")
238
- repo = source.repo
279
+ path = T.must(source).directory&.gsub(%r{^[./]+}, "")
280
+ repo = T.must(source).repo
239
281
 
240
282
  args = { sha: previous_tag, path: path }.compact
241
283
  previous_commit_shas =
242
- github_client.commits(repo, **args).map(&:sha)
284
+ T.unsafe(github_client).commits(repo, **args).map(&:sha)
243
285
 
244
286
  # NOTE: We reverse this so it's consistent with the array we get
245
287
  # from `github_client.compare(...)`
246
288
  args = { sha: new_tag, path: path }.compact
247
- github_client
248
- .commits(repo, **args)
249
- .reject { |c| previous_commit_shas.include?(c.sha) }.reverse
289
+ T.unsafe(github_client)
290
+ .commits(repo, **args)
291
+ .reject { |c| previous_commit_shas.include?(c.sha) }.reverse
250
292
  end
251
293
  return [] unless commits
252
294
 
@@ -261,16 +303,17 @@ module Dependabot
261
303
  []
262
304
  end
263
305
 
306
+ sig { returns(T::Array[T::Hash[Symbol, String]]) }
264
307
  def fetch_bitbucket_commits
265
- bitbucket_client
266
- .compare(source.repo, previous_tag, new_tag)
267
- .map do |commit|
268
- {
269
- message: commit.dig("summary", "raw"),
270
- sha: commit["hash"],
271
- html_url: commit.dig("links", "html", "href")
272
- }
273
- end
308
+ T.unsafe(bitbucket_client)
309
+ .compare(T.must(source).repo, previous_tag, new_tag)
310
+ .map do |commit|
311
+ {
312
+ message: commit.dig("summary", "raw"),
313
+ sha: commit["hash"],
314
+ html_url: commit.dig("links", "html", "href")
315
+ }
316
+ end
274
317
  rescue Dependabot::Clients::Bitbucket::NotFound,
275
318
  Dependabot::Clients::Bitbucket::Unauthorized,
276
319
  Dependabot::Clients::Bitbucket::Forbidden,
@@ -280,21 +323,23 @@ module Dependabot
280
323
  []
281
324
  end
282
325
 
326
+ sig { returns(T::Array[T::Hash[Symbol, String]]) }
283
327
  def fetch_gitlab_commits
284
- gitlab_client
285
- .compare(source.repo, previous_tag, new_tag)
286
- .commits
287
- .map do |commit|
288
- {
289
- message: commit["message"],
290
- sha: commit["id"],
291
- html_url: "#{source.url}/commit/#{commit['id']}"
292
- }
293
- end
328
+ T.unsafe(gitlab_client)
329
+ .compare(T.must(source).repo, previous_tag, new_tag)
330
+ .commits
331
+ .map do |commit|
332
+ {
333
+ message: commit["message"],
334
+ sha: commit["id"],
335
+ html_url: "#{T.must(source).url}/commit/#{commit['id']}"
336
+ }
337
+ end
294
338
  rescue Gitlab::Error::NotFound
295
339
  []
296
340
  end
297
341
 
342
+ sig { returns(T::Array[T::Hash[Symbol, String]]) }
298
343
  def fetch_azure_commits
299
344
  type = git_sha?(new_tag) ? "commit" : "tag"
300
345
  azure_client
@@ -315,54 +360,79 @@ module Dependabot
315
360
  []
316
361
  end
317
362
 
363
+ sig { returns(Dependabot::Clients::GitlabWithRetries) }
318
364
  def gitlab_client
319
- @gitlab_client ||= Dependabot::Clients::GitlabWithRetries
320
- .for_gitlab_dot_com(credentials: credentials)
365
+ @gitlab_client ||=
366
+ T.let(
367
+ Dependabot::Clients::GitlabWithRetries.for_gitlab_dot_com(credentials: credentials),
368
+ T.nilable(Dependabot::Clients::GitlabWithRetries)
369
+ )
321
370
  end
322
371
 
372
+ sig { returns(Dependabot::Clients::GithubWithRetries) }
323
373
  def github_client
324
- @github_client ||= Dependabot::Clients::GithubWithRetries
325
- .for_source(source: source, credentials: credentials)
374
+ @github_client ||=
375
+ T.let(
376
+ Dependabot::Clients::GithubWithRetries.for_source(source: source, credentials: credentials),
377
+ T.nilable(Dependabot::Clients::GithubWithRetries)
378
+ )
326
379
  end
327
380
 
381
+ sig { returns(Dependabot::Clients::Azure) }
328
382
  def azure_client
329
- @azure_client ||= Dependabot::Clients::Azure
330
- .for_source(source: source, credentials: credentials)
383
+ @azure_client ||=
384
+ T.let(
385
+ Dependabot::Clients::Azure.for_source(source: T.must(source), credentials: credentials),
386
+ T.nilable(Dependabot::Clients::Azure)
387
+ )
331
388
  end
332
389
 
390
+ sig { returns(Dependabot::Clients::BitbucketWithRetries) }
333
391
  def bitbucket_client
334
- @bitbucket_client ||= Dependabot::Clients::BitbucketWithRetries
335
- .for_bitbucket_dot_org(credentials: credentials)
392
+ @bitbucket_client ||=
393
+ T.let(
394
+ Dependabot::Clients::BitbucketWithRetries.for_bitbucket_dot_org(credentials: credentials),
395
+ T.nilable(Dependabot::Clients::BitbucketWithRetries)
396
+ )
336
397
  end
337
398
 
399
+ sig { returns(T::Boolean) }
338
400
  def part_of_monorepo?
339
401
  return false unless reliable_source_directory?
340
402
 
341
- ![nil, ".", "/"].include?(source.directory)
403
+ ![nil, ".", "/"].include?(T.must(source).directory)
342
404
  end
343
405
 
406
+ sig { returns(T.class_of(Dependabot::Version)) }
344
407
  def version_class
345
408
  dependency.version_class
346
409
  end
347
410
 
411
+ sig { returns(T.class_of(Dependabot::Requirement)) }
348
412
  def requirement_class
349
413
  dependency.requirement_class
350
414
  end
351
415
 
416
+ sig { params(version: T.nilable(String)).returns(T::Boolean) }
352
417
  def git_sha?(version)
353
418
  return false unless version
354
419
 
355
420
  version.match?(/^[0-9a-f]{40}$/)
356
421
  end
357
422
 
423
+ sig { returns(T::Boolean) }
358
424
  def reliable_source_directory?
359
425
  MetadataFinders::Base::PACKAGE_MANAGERS_WITH_RELIABLE_DIRECTORIES
360
426
  .include?(dependency.package_manager)
361
427
  end
362
428
 
429
+ sig { returns(String) }
363
430
  def default_gitlab_branch
364
431
  @default_gitlab_branch ||=
365
- gitlab_client.fetch_default_branch(source.repo)
432
+ T.let(
433
+ gitlab_client.fetch_default_branch(T.must(source).repo),
434
+ T.nilable(String)
435
+ )
366
436
  end
367
437
  end
368
438
  end