dependabot-common 0.244.0 → 0.246.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.
Files changed (28) hide show
  1. checksums.yaml +4 -4
  2. data/lib/dependabot/clients/bitbucket.rb +113 -5
  3. data/lib/dependabot/clients/bitbucket_with_retries.rb +34 -10
  4. data/lib/dependabot/clients/codecommit.rb +107 -12
  5. data/lib/dependabot/clients/github_with_retries.rb +61 -19
  6. data/lib/dependabot/clients/gitlab_with_retries.rb +60 -7
  7. data/lib/dependabot/dependency.rb +1 -1
  8. data/lib/dependabot/errors.rb +8 -2
  9. data/lib/dependabot/git_commit_checker.rb +4 -3
  10. data/lib/dependabot/metadata_finders/base/changelog_finder.rb +1 -1
  11. data/lib/dependabot/metadata_finders/base/commits_finder.rb +1 -1
  12. data/lib/dependabot/metadata_finders/base/release_finder.rb +1 -1
  13. data/lib/dependabot/pull_request_creator/azure.rb +80 -9
  14. data/lib/dependabot/pull_request_creator/bitbucket.rb +73 -9
  15. data/lib/dependabot/pull_request_creator/codecommit.rb +96 -25
  16. data/lib/dependabot/pull_request_creator/github.rb +162 -49
  17. data/lib/dependabot/pull_request_creator/gitlab.rb +109 -21
  18. data/lib/dependabot/pull_request_creator/message_builder.rb +239 -89
  19. data/lib/dependabot/pull_request_creator/pr_name_prefixer.rb +11 -9
  20. data/lib/dependabot/pull_request_creator.rb +32 -27
  21. data/lib/dependabot/pull_request_updater/azure.rb +75 -11
  22. data/lib/dependabot/pull_request_updater/github.rb +89 -28
  23. data/lib/dependabot/pull_request_updater/gitlab.rb +61 -12
  24. data/lib/dependabot/pull_request_updater.rb +1 -1
  25. data/lib/dependabot/shared_helpers.rb +19 -1
  26. data/lib/dependabot/update_checkers/base.rb +121 -31
  27. data/lib/dependabot.rb +1 -1
  28. metadata +3 -3
@@ -1,42 +1,61 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "octokit"
5
+ require "sorbet-runtime"
6
+ require "dependabot/credential"
5
7
 
6
8
  module Dependabot
7
9
  module Clients
8
10
  class GithubWithRetries
11
+ extend T::Sig
12
+
9
13
  DEFAULT_OPEN_TIMEOUT_IN_SECONDS = 2
10
14
  DEFAULT_READ_TIMEOUT_IN_SECONDS = 5
11
15
 
16
+ sig { returns(Integer) }
12
17
  def self.open_timeout_in_seconds
13
18
  ENV.fetch("DEPENDABOT_OPEN_TIMEOUT_IN_SECONDS", DEFAULT_OPEN_TIMEOUT_IN_SECONDS).to_i
14
19
  end
15
20
 
21
+ sig { returns(Integer) }
16
22
  def self.read_timeout_in_seconds
17
23
  ENV.fetch("DEPENDABOT_READ_TIMEOUT_IN_SECONDS", DEFAULT_READ_TIMEOUT_IN_SECONDS).to_i
18
24
  end
19
25
 
20
- DEFAULT_CLIENT_ARGS = {
21
- connection_options: {
22
- request: {
23
- open_timeout: open_timeout_in_seconds,
24
- timeout: read_timeout_in_seconds
26
+ DEFAULT_CLIENT_ARGS = T.let(
27
+ {
28
+ connection_options: {
29
+ request: {
30
+ open_timeout: open_timeout_in_seconds,
31
+ timeout: read_timeout_in_seconds
32
+ }
25
33
  }
26
- }
27
- }.freeze
28
-
29
- RETRYABLE_ERRORS = [
30
- Faraday::ConnectionFailed,
31
- Faraday::TimeoutError,
32
- Octokit::InternalServerError,
33
- Octokit::BadGateway
34
- ].freeze
34
+ }.freeze,
35
+ T::Hash[Symbol, T.untyped]
36
+ )
37
+
38
+ RETRYABLE_ERRORS = T.let(
39
+ [
40
+ Faraday::ConnectionFailed,
41
+ Faraday::TimeoutError,
42
+ Octokit::InternalServerError,
43
+ Octokit::BadGateway
44
+ ].freeze,
45
+ T::Array[T.class_of(StandardError)]
46
+ )
35
47
 
36
48
  #######################
37
49
  # Constructor methods #
38
50
  #######################
39
51
 
52
+ sig do
53
+ params(
54
+ source: Dependabot::Source,
55
+ credentials: T::Array[Dependabot::Credential]
56
+ )
57
+ .returns(Dependabot::Clients::GithubWithRetries)
58
+ end
40
59
  def self.for_source(source:, credentials:)
41
60
  access_tokens =
42
61
  credentials
@@ -51,6 +70,7 @@ module Dependabot
51
70
  )
52
71
  end
53
72
 
73
+ sig { params(credentials: T::Array[Dependabot::Credential]).returns(Dependabot::Clients::GithubWithRetries) }
54
74
  def self.for_github_dot_com(credentials:)
55
75
  access_tokens =
56
76
  credentials
@@ -66,6 +86,7 @@ module Dependabot
66
86
  # VCS Interface #
67
87
  #################
68
88
 
89
+ sig { params(repo: String, branch: String).returns(String) }
69
90
  def fetch_commit(repo, branch)
70
91
  response = T.unsafe(self).ref(repo, "heads/#{branch}")
71
92
 
@@ -74,6 +95,7 @@ module Dependabot
74
95
  response.object.sha
75
96
  end
76
97
 
98
+ sig { params(repo: String).returns(String) }
77
99
  def fetch_default_branch(repo)
78
100
  T.unsafe(self).repository(repo).default_branch
79
101
  end
@@ -82,6 +104,7 @@ module Dependabot
82
104
  # Proxying #
83
105
  ############
84
106
 
107
+ sig { params(max_retries: T.nilable(Integer), args: T.untyped).void }
85
108
  def initialize(max_retries: 3, **args)
86
109
  args = DEFAULT_CLIENT_ARGS.merge(args)
87
110
 
@@ -106,11 +129,23 @@ module Dependabot
106
129
  end
107
130
  end
108
131
 
109
- @clients = access_tokens.map do |token|
110
- Octokit::Client.new(args.merge(access_token: token))
111
- end
132
+ @clients = T.let(
133
+ access_tokens.map do |token|
134
+ Octokit::Client.new(args.merge(access_token: token))
135
+ end,
136
+ T::Array[Octokit::Client]
137
+ )
112
138
  end
113
139
 
140
+ # TODO: Create all the methods that are called on the client
141
+ sig do
142
+ params(
143
+ method_name: T.any(Symbol, String),
144
+ args: T.untyped,
145
+ block: T.nilable(T.proc.returns(T.untyped))
146
+ )
147
+ .returns(T.untyped)
148
+ end
114
149
  def method_missing(method_name, *args, &block)
115
150
  untried_clients = @clients.dup
116
151
  client = untried_clients.pop
@@ -118,7 +153,7 @@ module Dependabot
118
153
  begin
119
154
  if client.respond_to?(method_name)
120
155
  mutatable_args = args.map(&:dup)
121
- client.public_send(method_name, *mutatable_args, &block)
156
+ T.unsafe(client).public_send(method_name, *mutatable_args, &block)
122
157
  else
123
158
  super
124
159
  end
@@ -129,6 +164,13 @@ module Dependabot
129
164
  end
130
165
  end
131
166
 
167
+ sig do
168
+ params(
169
+ method_name: Symbol,
170
+ include_private: T::Boolean
171
+ )
172
+ .returns(T::Boolean)
173
+ end
132
174
  def respond_to_missing?(method_name, include_private = false)
133
175
  @clients.first.respond_to?(method_name) || super
134
176
  end
@@ -1,12 +1,20 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "gitlab"
5
+ require "sorbet-runtime"
6
+
7
+ require "dependabot/credential"
5
8
 
6
9
  module Dependabot
7
10
  module Clients
8
11
  class GitlabWithRetries
9
- RETRYABLE_ERRORS = [Gitlab::Error::BadGateway].freeze
12
+ extend T::Sig
13
+
14
+ RETRYABLE_ERRORS = T.let(
15
+ [Gitlab::Error::BadGateway].freeze,
16
+ T::Array[T.class_of(Gitlab::Error::ResponseError)]
17
+ )
10
18
 
11
19
  class ContentEncoding
12
20
  BASE64 = "base64"
@@ -17,6 +25,13 @@ module Dependabot
17
25
  # Constructor methods #
18
26
  #######################
19
27
 
28
+ sig do
29
+ params(
30
+ source: Dependabot::Source,
31
+ credentials: T::Array[Dependabot::Credential]
32
+ )
33
+ .returns(Dependabot::Clients::GitlabWithRetries)
34
+ end
20
35
  def self.for_source(source:, credentials:)
21
36
  access_token =
22
37
  credentials
@@ -31,6 +46,7 @@ module Dependabot
31
46
  )
32
47
  end
33
48
 
49
+ sig { params(credentials: T::Array[Dependabot::Credential]).returns(Dependabot::Clients::GitlabWithRetries) }
34
50
  def self.for_gitlab_dot_com(credentials:)
35
51
  access_token =
36
52
  credentials
@@ -49,10 +65,12 @@ module Dependabot
49
65
  # VCS Interface #
50
66
  #################
51
67
 
68
+ sig { params(repo: String, branch: String).returns(String) }
52
69
  def fetch_commit(repo, branch)
53
70
  T.unsafe(self).branch(repo, branch).commit.id
54
71
  end
55
72
 
73
+ sig { params(repo: String).returns(String) }
56
74
  def fetch_default_branch(repo)
57
75
  T.unsafe(self).project(repo).default_branch
58
76
  end
@@ -61,9 +79,10 @@ module Dependabot
61
79
  # Proxying #
62
80
  ############
63
81
 
82
+ sig { params(max_retries: T.nilable(Integer), args: T.untyped).void }
64
83
  def initialize(max_retries: 3, **args)
65
- @max_retries = max_retries || 3
66
- @client = ::Gitlab::Client.new(args)
84
+ @max_retries = T.let(max_retries || 3, Integer)
85
+ @client = T.let(::Gitlab::Client.new(args), ::Gitlab::Client)
67
86
  end
68
87
 
69
88
  # Create commit in gitlab repo with correctly mapped file actions
@@ -74,32 +93,63 @@ module Dependabot
74
93
  # @param [Array<Dependabot::DependencyFile>] files
75
94
  # @param [Hash] options
76
95
  # @return [Gitlab::ObjectifiedHash]
96
+ sig do
97
+ params(
98
+ repo: String,
99
+ branch_name: String,
100
+ commit_message: String,
101
+ files: T::Array[Dependabot::DependencyFile],
102
+ options: T.untyped
103
+ )
104
+ .returns(Gitlab::ObjectifiedHash)
105
+ end
77
106
  def create_commit(repo, branch_name, commit_message, files, **options)
78
107
  @client.create_commit(
79
108
  repo,
80
109
  branch_name,
81
110
  commit_message,
82
111
  file_actions(files),
83
- **options
112
+ options
84
113
  )
85
114
  end
86
115
 
116
+ # TODO: Create all the methods that are called on the client
117
+ sig do
118
+ params(
119
+ method_name: T.any(Symbol, String),
120
+ args: T.untyped,
121
+ block: T.nilable(T.proc.returns(T.untyped))
122
+ )
123
+ .returns(T.untyped)
124
+ end
87
125
  def method_missing(method_name, *args, &block)
88
126
  retry_connection_failures do
89
127
  if @client.respond_to?(method_name)
90
128
  mutatable_args = args.map(&:dup)
91
- @client.public_send(method_name, *mutatable_args, &block)
129
+ T.unsafe(@client).public_send(method_name, *mutatable_args, &block)
92
130
  else
93
131
  super
94
132
  end
95
133
  end
96
134
  end
97
135
 
136
+ sig do
137
+ params(
138
+ method_name: Symbol,
139
+ include_private: T::Boolean
140
+ )
141
+ .returns(T::Boolean)
142
+ end
98
143
  def respond_to_missing?(method_name, include_private = false)
99
144
  @client.respond_to?(method_name) || super
100
145
  end
101
146
 
102
- def retry_connection_failures
147
+ sig do
148
+ type_parameters(:T)
149
+ .params(_blk: T.proc.returns(T.type_parameter(:T)))
150
+ .returns(T.type_parameter(:T))
151
+ end
152
+ def retry_connection_failures(&_blk)
103
153
  retry_attempt = 0
104
154
 
105
155
  begin
@@ -116,6 +166,7 @@ module Dependabot
116
166
  #
117
167
  # @param [Array<Dependabot::DependencyFile>] files
118
168
  # @return [Array<Hash>]
169
+ sig { params(files: T::Array[Dependabot::DependencyFile]).returns(T::Array[T::Hash[Symbol, T.untyped]]) }
119
170
  def file_actions(files)
120
171
  files.map do |file|
121
172
  {
@@ -131,6 +182,7 @@ module Dependabot
131
182
  #
132
183
  # @param [Dependabot::DependencyFile] file
133
184
  # @return [String]
185
+ sig { params(file: Dependabot::DependencyFile).returns(String) }
134
186
  def file_action(file)
135
187
  if file.operation == Dependabot::DependencyFile::Operation::DELETE
136
188
  "delete"
@@ -145,6 +197,7 @@ module Dependabot
145
197
  #
146
198
  # @param [Dependabot::DependencyFile] file
147
199
  # @return [String]
200
+ sig { params(file: Dependabot::DependencyFile).returns(String) }
148
201
  def file_encoding(file)
149
202
  return ContentEncoding::BASE64 if file.content_encoding == Dependabot::DependencyFile::ContentEncoding::BASE64
150
203
 
@@ -93,7 +93,7 @@ module Dependabot
93
93
  # TODO: Make version a Dependabot::Version everywhere
94
94
  version: T.nilable(T.any(String, Dependabot::Version)),
95
95
  previous_version: T.nilable(String),
96
- previous_requirements: T.nilable(T::Array[T::Hash[String, String]]),
96
+ previous_requirements: T.nilable(T::Array[T::Hash[T.any(Symbol, String), T.untyped]]),
97
97
  subdependency_metadata: T.nilable(T::Array[T::Hash[T.any(Symbol, String), String]]),
98
98
  removed: T::Boolean,
99
99
  metadata: T.nilable(T::Hash[T.any(Symbol, String), String])
@@ -48,7 +48,10 @@ module Dependabot
48
48
  when Dependabot::DependencyFileNotFound
49
49
  {
50
50
  "error-type": "dependency_file_not_found",
51
- "error-detail": { "file-path": error.file_path }
51
+ "error-detail": {
52
+ message: error.message,
53
+ "file-path": error.file_path
54
+ }
52
55
  }
53
56
  when Dependabot::OutOfDisk
54
57
  {
@@ -108,7 +111,10 @@ module Dependabot
108
111
  when Dependabot::DependencyFileNotFound
109
112
  {
110
113
  "error-type": "dependency_file_not_found",
111
- "error-detail": { "file-path": error.file_path }
114
+ "error-detail": {
115
+ message: error.message,
116
+ "file-path": error.file_path
117
+ }
112
118
  }
113
119
  when Dependabot::PathDependenciesNotReachable
114
120
  {
@@ -367,7 +367,8 @@ module Dependabot
367
367
  client = Clients::GithubWithRetries
368
368
  .for_github_dot_com(credentials: credentials)
369
369
 
370
- client.compare(listing_source_repo, ref1, ref2).status
370
+ # TODO: create this method instead of relying on method_missing
371
+ T.unsafe(client).compare(listing_source_repo, ref1, ref2).status
371
372
  end
372
373
 
373
374
  sig { params(ref1: String, ref2: String).returns(String) }
@@ -375,7 +376,7 @@ module Dependabot
375
376
  client = Clients::GitlabWithRetries
376
377
  .for_gitlab_dot_com(credentials: credentials)
377
378
 
378
- comparison = client.compare(listing_source_repo, ref1, ref2)
379
+ comparison = T.unsafe(client).compare(listing_source_repo, ref1, ref2)
379
380
 
380
381
  if comparison.commits.none? then "behind"
381
382
  elsif comparison.compare_same_ref then "identical"
@@ -393,7 +394,7 @@ module Dependabot
393
394
  client = Clients::BitbucketWithRetries
394
395
  .for_bitbucket_dot_org(credentials: credentials)
395
396
 
396
- response = client.get(url)
397
+ response = T.unsafe(client).get(url)
397
398
 
398
399
  # Conservatively assume that ref2 is ahead in the equality case, of
399
400
  # if we get an unexpected format (e.g., due to a 404)
@@ -460,7 +460,7 @@ module Dependabot
460
460
  def github_client
461
461
  @github_client ||=
462
462
  T.let(
463
- Dependabot::Clients::GithubWithRetries.for_source(source: source, credentials: credentials),
463
+ Dependabot::Clients::GithubWithRetries.for_source(source: T.must(source), credentials: credentials),
464
464
  T.nilable(Dependabot::Clients::GithubWithRetries)
465
465
  )
466
466
  end
@@ -373,7 +373,7 @@ module Dependabot
373
373
  def github_client
374
374
  @github_client ||=
375
375
  T.let(
376
- Dependabot::Clients::GithubWithRetries.for_source(source: source, credentials: credentials),
376
+ Dependabot::Clients::GithubWithRetries.for_source(source: T.must(source), credentials: credentials),
377
377
  T.nilable(Dependabot::Clients::GithubWithRetries)
378
378
  )
379
379
  end
@@ -359,7 +359,7 @@ module Dependabot
359
359
  def github_client
360
360
  @github_client ||=
361
361
  T.let(
362
- Dependabot::Clients::GithubWithRetries.for_source(source: source, credentials: credentials),
362
+ Dependabot::Clients::GithubWithRetries.for_source(source: T.must(source), credentials: credentials),
363
363
  T.nilable(Dependabot::Clients::GithubWithRetries)
364
364
  )
365
365
  end
@@ -1,21 +1,79 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
+ require "sorbet-runtime"
5
+
6
+ require "dependabot/credential"
4
7
  require "dependabot/clients/azure"
5
8
  require "dependabot/pull_request_creator"
6
9
 
7
10
  module Dependabot
8
11
  class PullRequestCreator
9
12
  class Azure
10
- attr_reader :source, :branch_name, :base_commit, :credentials,
11
- :files, :commit_message, :pr_description, :pr_name,
12
- :author_details, :labeler, :reviewers, :assignees, :work_item
13
+ extend T::Sig
14
+
15
+ sig { returns(Dependabot::Source) }
16
+ attr_reader :source
17
+
18
+ sig { returns(String) }
19
+ attr_reader :branch_name
20
+
21
+ sig { returns(String) }
22
+ attr_reader :base_commit
23
+
24
+ sig { returns(T::Array[Dependabot::Credential]) }
25
+ attr_reader :credentials
26
+
27
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
28
+ attr_reader :files
29
+
30
+ sig { returns(String) }
31
+ attr_reader :commit_message
32
+
33
+ sig { returns(String) }
34
+ attr_reader :pr_description
35
+
36
+ sig { returns(String) }
37
+ attr_reader :pr_name
38
+
39
+ sig { returns(T.nilable(T::Hash[Symbol, String])) }
40
+ attr_reader :author_details
41
+
42
+ sig { returns(Dependabot::PullRequestCreator::Labeler) }
43
+ attr_reader :labeler
44
+
45
+ sig { returns(T.nilable(T::Array[String])) }
46
+ attr_reader :reviewers
47
+
48
+ sig { returns(T.nilable(T::Array[String])) }
49
+ attr_reader :assignees
50
+
51
+ sig { returns(T.nilable(Integer)) }
52
+ attr_reader :work_item
13
53
 
14
54
  # Azure DevOps limits PR descriptions to a max of 4,000 characters in UTF-16 encoding:
15
55
  # https://developercommunity.visualstudio.com/content/problem/608770/remove-4000-character-limit-on-pull-request-descri.html
16
56
  PR_DESCRIPTION_MAX_LENGTH = 3_999 # 0 based count
17
57
  PR_DESCRIPTION_ENCODING = Encoding::UTF_16
18
58
 
59
+ sig do
60
+ params(
61
+ source: Dependabot::Source,
62
+ branch_name: String,
63
+ base_commit: String,
64
+ credentials: T::Array[Dependabot::Credential],
65
+ files: T::Array[Dependabot::DependencyFile],
66
+ commit_message: String,
67
+ pr_description: String,
68
+ pr_name: String,
69
+ author_details: T.nilable(T::Hash[Symbol, String]),
70
+ labeler: Dependabot::PullRequestCreator::Labeler,
71
+ reviewers: T.nilable(T::Array[String]),
72
+ assignees: T.nilable(T::Array[String]),
73
+ work_item: T.nilable(Integer)
74
+ )
75
+ .void
76
+ end
19
77
  def initialize(source:, branch_name:, base_commit:, credentials:,
20
78
  files:, commit_message:, pr_description:, pr_name:,
21
79
  author_details:, labeler:, reviewers: nil, assignees: nil, work_item: nil)
@@ -34,6 +92,7 @@ module Dependabot
34
92
  @work_item = work_item
35
93
  end
36
94
 
95
+ sig { returns(T.nilable(Excon::Response)) }
37
96
  def create
38
97
  return if branch_exists? && pull_request_exists?
39
98
 
@@ -46,20 +105,26 @@ module Dependabot
46
105
 
47
106
  private
48
107
 
108
+ sig { returns(Dependabot::Clients::Azure) }
49
109
  def azure_client_for_source
50
110
  @azure_client_for_source ||=
51
- Dependabot::Clients::Azure.for_source(
52
- source: source,
53
- credentials: credentials
111
+ T.let(
112
+ Dependabot::Clients::Azure.for_source(
113
+ source: source,
114
+ credentials: credentials
115
+ ),
116
+ T.nilable(Dependabot::Clients::Azure)
54
117
  )
55
118
  end
56
119
 
120
+ sig { returns(T::Boolean) }
57
121
  def branch_exists?
58
- azure_client_for_source.branch(branch_name)
122
+ !azure_client_for_source.branch(branch_name).nil?
59
123
  rescue ::Dependabot::Clients::Azure::NotFound
60
124
  false
61
125
  end
62
126
 
127
+ sig { returns(T::Boolean) }
63
128
  def pull_request_exists?
64
129
  azure_client_for_source.pull_requests(
65
130
  branch_name,
@@ -67,6 +132,7 @@ module Dependabot
67
132
  ).any?
68
133
  end
69
134
 
135
+ sig { void }
70
136
  def create_commit
71
137
  author = author_details&.slice(:name, :email, :date)
72
138
  author = nil unless author&.any?
@@ -80,6 +146,7 @@ module Dependabot
80
146
  )
81
147
  end
82
148
 
149
+ sig { returns(Excon::Response) }
83
150
  def create_pull_request
84
151
  azure_client_for_source.create_pull_request(
85
152
  pr_name,
@@ -93,9 +160,13 @@ module Dependabot
93
160
  )
94
161
  end
95
162
 
163
+ sig { returns(String) }
96
164
  def default_branch
97
165
  @default_branch ||=
98
- azure_client_for_source.fetch_default_branch(source.repo)
166
+ T.let(
167
+ azure_client_for_source.fetch_default_branch(source.repo),
168
+ T.nilable(String)
169
+ )
99
170
  end
100
171
  end
101
172
  end