dependabot-common 0.244.0 → 0.246.0

Sign up to get free protection for your applications and to get access to all the features.
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