dependabot-common 0.129.3 → 0.129.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: caa3a193af53d69a8c06fc60d972a0571418b6192dacf2ce20b2b7ddc9011b60
4
- data.tar.gz: d9f6046100711920da02ee7c5a7cf5049d67e3d0c2c614c0ad39bccb31445354
3
+ metadata.gz: afed21fb500f1de6d025f3824fa4a64eff41d0816d44106e044f4a54de984bfe
4
+ data.tar.gz: 3c8c4a17267398cc4efbdc4974a1f8dc78e71aa74f08e194029cb01438938457
5
5
  SHA512:
6
- metadata.gz: f5f0891b60e259e31374cd338c0e155d9dfee28bbe3ee252149b46a9849c5a5bda070aa91384270970e430d331a047a673116a07dc262f70df1708510216993a
7
- data.tar.gz: afcb0cc9ef39a72177eb3880815c139a4d904b860aa257b8c14d4d8427786d85c9ba11862a4581066255b1fc1c19492ea4772be620a96c9e93159f887408101b
6
+ metadata.gz: 7539dc5753d2f66e23cc7b2f5f77b8d7a01e39480b08df5362e08e5012c7249613edc0e25c2dbce80968e40034fdd64b3c4d10adb5ac190a7e870d2288786f05
7
+ data.tar.gz: 6e776353cf20627d05453b2334dbd112f7ab1bf48e959be8f88093d4a4ea74416c557161e14eafc9ef0dd8049d10c52aef7474f2c95541378a0470d34bb047dd
@@ -12,6 +12,19 @@ module Dependabot
12
12
 
13
13
  class Forbidden < StandardError; end
14
14
 
15
+ #######################
16
+ # Constructor methods #
17
+ #######################
18
+
19
+ def self.for_source(source:, credentials:)
20
+ credential =
21
+ credentials.
22
+ select { |cred| cred["type"] == "git_source" }.
23
+ find { |cred| cred["host"] == source.hostname }
24
+
25
+ new(credentials: credential)
26
+ end
27
+
15
28
  ##########
16
29
  # Client #
17
30
  ##########
@@ -53,6 +66,81 @@ module Dependabot
53
66
  response.body
54
67
  end
55
68
 
69
+ def commits(repo, branch_name = nil)
70
+ commits_path = "#{repo}/commits/#{branch_name}?pagelen=100"
71
+ next_page_url = base_url + commits_path
72
+ paginate({ "next" => next_page_url })
73
+ end
74
+
75
+ def branch(repo, branch_name)
76
+ branch_path = "#{repo}/refs/branches/#{branch_name}"
77
+ response = get(base_url + branch_path)
78
+
79
+ JSON.parse(response.body)
80
+ end
81
+
82
+ def pull_requests(repo, source_branch, target_branch)
83
+ pr_path = "#{repo}/pullrequests"
84
+ # Get pull requests with any status
85
+ pr_path += "?status=OPEN&status=MERGED&status=DECLINED&status=SUPERSEDED"
86
+ next_page_url = base_url + pr_path
87
+ pull_requests = paginate({ "next" => next_page_url })
88
+
89
+ pull_requests unless source_branch && target_branch
90
+
91
+ pull_requests.select do |pr|
92
+ pr_source_branch = pr.fetch("source").fetch("branch").fetch("name")
93
+ pr_target_branch = pr.fetch("destination").fetch("branch").fetch("name")
94
+ pr_source_branch == source_branch && pr_target_branch == target_branch
95
+ end
96
+ end
97
+
98
+ # rubocop:disable Metrics/ParameterLists
99
+ def create_commit(repo, branch_name, base_commit, commit_message, files,
100
+ author_details)
101
+ parameters = {
102
+ message: commit_message, # TODO: Format markup in commit message
103
+ author: "#{author_details.fetch(:name)} <#{author_details.fetch(:email)}>",
104
+ parents: base_commit,
105
+ branch: branch_name
106
+ }
107
+
108
+ files.each do |file|
109
+ absolute_path = file.name.start_with?("/") ? file.name : "/" + file.name
110
+ parameters[absolute_path] = file.content
111
+ end
112
+
113
+ body = encode_form_parameters(parameters)
114
+
115
+ commit_path = "#{repo}/src"
116
+ post(base_url + commit_path, body, "application/x-www-form-urlencoded")
117
+ end
118
+ # rubocop:enable Metrics/ParameterLists
119
+
120
+ # rubocop:disable Metrics/ParameterLists
121
+ def create_pull_request(repo, pr_name, source_branch, target_branch,
122
+ pr_description, _labels, _work_item = nil)
123
+ content = {
124
+ title: pr_name,
125
+ source: {
126
+ branch: {
127
+ name: source_branch
128
+ }
129
+ },
130
+ destination: {
131
+ branch: {
132
+ name: target_branch
133
+ }
134
+ },
135
+ description: pr_description,
136
+ close_source_branch: true
137
+ }
138
+
139
+ pr_path = "#{repo}/pullrequests"
140
+ post(base_url + pr_path, content.to_json)
141
+ end
142
+ # rubocop:enable Metrics/ParameterLists
143
+
56
144
  def tags(repo)
57
145
  path = "#{repo}/refs/tags?pagelen=100"
58
146
  response = get(base_url + path)
@@ -90,6 +178,28 @@ module Dependabot
90
178
  response
91
179
  end
92
180
 
181
+ def post(url, body, content_type = "application/json")
182
+ response = Excon.post(
183
+ url,
184
+ body: body,
185
+ user: credentials&.fetch("username", nil),
186
+ password: credentials&.fetch("password", nil),
187
+ idempotent: false,
188
+ **SharedHelpers.excon_defaults(
189
+ headers: auth_header.merge(
190
+ {
191
+ "Content-Type" => content_type
192
+ }
193
+ )
194
+ )
195
+ )
196
+ raise Unauthorized if response.status == 401
197
+ raise Forbidden if response.status == 403
198
+ raise NotFound if response.status == 404
199
+
200
+ response
201
+ end
202
+
93
203
  private
94
204
 
95
205
  def auth_header_for(token)
@@ -98,6 +208,37 @@ module Dependabot
98
208
  { "Authorization" => "Bearer #{token}" }
99
209
  end
100
210
 
211
+ def encode_form_parameters(parameters)
212
+ parameters.map do |key, value|
213
+ URI.encode_www_form_component(key.to_s) + "=" + URI.encode_www_form_component(value.to_s)
214
+ end.join("&")
215
+ end
216
+
217
+ # Takes a hash with optional `values` and `next` fields
218
+ # Returns an enumerator.
219
+ #
220
+ # Can be used a few ways:
221
+ # With GET:
222
+ # paginate ({"next" => url})
223
+ # or
224
+ # paginate(JSON.parse(get(url).body))
225
+ #
226
+ # With POST (for endpoints that provide POST methods for long query parameters)
227
+ # response = post(url, body)
228
+ # first_page = JSON.parse(repsonse.body)
229
+ # paginate(first_page)
230
+ def paginate(page)
231
+ Enumerator.new do |yielder|
232
+ loop do
233
+ page.fetch("values", []).each { |value| yielder << value }
234
+ break unless page.key?("next")
235
+
236
+ next_page_url = page.fetch("next")
237
+ page = JSON.parse(get(next_page_url).body)
238
+ end
239
+ end
240
+ end
241
+
101
242
  attr_reader :auth_header
102
243
  attr_reader :credentials
103
244
 
@@ -5,6 +5,7 @@ require "dependabot/metadata_finders"
5
5
  module Dependabot
6
6
  class PullRequestCreator
7
7
  require "dependabot/pull_request_creator/azure"
8
+ require "dependabot/pull_request_creator/bitbucket"
8
9
  require "dependabot/pull_request_creator/codecommit"
9
10
  require "dependabot/pull_request_creator/github"
10
11
  require "dependabot/pull_request_creator/gitlab"
@@ -88,6 +89,7 @@ module Dependabot
88
89
  when "github" then github_creator.create
89
90
  when "gitlab" then gitlab_creator.create
90
91
  when "azure" then azure_creator.create
92
+ when "bitbucket" then bitbucket_creator.create
91
93
  when "codecommit" then codecommit_creator.create
92
94
  else raise "Unsupported provider #{source.provider}"
93
95
  end
@@ -162,6 +164,22 @@ module Dependabot
162
164
  )
163
165
  end
164
166
 
167
+ def bitbucket_creator
168
+ Bitbucket.new(
169
+ source: source,
170
+ branch_name: branch_namer.new_branch_name,
171
+ base_commit: base_commit,
172
+ credentials: credentials,
173
+ files: files,
174
+ commit_message: message_builder.commit_message,
175
+ pr_description: message_builder.pr_message,
176
+ pr_name: message_builder.pr_name,
177
+ author_details: author_details,
178
+ labeler: labeler,
179
+ work_item: provider_metadata&.fetch(:work_item, nil)
180
+ )
181
+ end
182
+
165
183
  def codecommit_creator
166
184
  Codecommit.new(
167
185
  source: source,
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dependabot/clients/bitbucket"
4
+ require "dependabot/pull_request_creator"
5
+
6
+ module Dependabot
7
+ class PullRequestCreator
8
+ class Bitbucket
9
+ attr_reader :source, :branch_name, :base_commit, :credentials,
10
+ :files, :commit_message, :pr_description, :pr_name,
11
+ :author_details, :labeler, :work_item
12
+
13
+ def initialize(source:, branch_name:, base_commit:, credentials:,
14
+ files:, commit_message:, pr_description:, pr_name:,
15
+ author_details:, labeler: nil, work_item: nil)
16
+ @source = source
17
+ @branch_name = branch_name
18
+ @base_commit = base_commit
19
+ @credentials = credentials
20
+ @files = files
21
+ @commit_message = commit_message
22
+ @pr_description = pr_description
23
+ @pr_name = pr_name
24
+ @author_details = author_details
25
+ @labeler = labeler
26
+ @work_item = work_item
27
+ end
28
+
29
+ def create
30
+ return if branch_exists? && pull_request_exists?
31
+
32
+ # FIXME: Copied from Azure, but not verified whether this is true
33
+ # For Bitbucket we create or update a branch in the same request as creating
34
+ # a commit (so we don't need create or update branch logic here)
35
+ create_commit
36
+
37
+ create_pull_request
38
+ end
39
+
40
+ private
41
+
42
+ def bitbucket_client_for_source
43
+ @bitbucket_client_for_source ||=
44
+ Dependabot::Clients::Bitbucket.for_source(
45
+ source: source,
46
+ credentials: credentials
47
+ )
48
+ end
49
+
50
+ def branch_exists?
51
+ bitbucket_client_for_source.branch(source.repo, branch_name)
52
+ rescue Clients::Bitbucket::NotFound
53
+ false
54
+ end
55
+
56
+ def pull_request_exists?
57
+ bitbucket_client_for_source.pull_requests(
58
+ source.repo,
59
+ branch_name,
60
+ source.branch || default_branch
61
+ ).any?
62
+ end
63
+
64
+ def create_commit
65
+ author = author_details&.slice(:name, :email)
66
+ author = nil unless author&.any?
67
+
68
+ bitbucket_client_for_source.create_commit(
69
+ source.repo,
70
+ branch_name,
71
+ base_commit,
72
+ commit_message,
73
+ files,
74
+ author
75
+ )
76
+ end
77
+
78
+ def create_pull_request
79
+ bitbucket_client_for_source.create_pull_request(
80
+ source.repo,
81
+ pr_name,
82
+ branch_name,
83
+ source.branch || default_branch,
84
+ pr_description,
85
+ labeler&.labels_for_pr,
86
+ work_item
87
+ )
88
+ end
89
+
90
+ def default_branch
91
+ @default_branch ||=
92
+ bitbucket_client_for_source.fetch_default_branch(source.repo)
93
+ end
94
+ end
95
+ end
96
+ end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "dependabot/clients/azure"
4
+ require "dependabot/clients/bitbucket"
4
5
  require "dependabot/clients/codecommit"
5
6
  require "dependabot/clients/github_with_retries"
6
7
  require "dependabot/clients/gitlab_with_retries"
@@ -264,6 +265,7 @@ module Dependabot
264
265
  when "github" then recent_github_commit_messages
265
266
  when "gitlab" then recent_gitlab_commit_messages
266
267
  when "azure" then recent_azure_commit_messages
268
+ when "bitbucket" then recent_bitbucket_commit_messages
267
269
  when "codecommit" then recent_codecommit_commit_messages
268
270
  else raise "Unsupported provider: #{source.provider}"
269
271
  end
@@ -307,6 +309,18 @@ module Dependabot
307
309
  map(&:strip)
308
310
  end
309
311
 
312
+ def recent_bitbucket_commit_messages
313
+ @recent_bitbucket_commit_messages ||=
314
+ bitbucket_client_for_source.commits(source.repo)
315
+
316
+ @recent_bitbucket_commit_messages.
317
+ reject { |c| bitbucket_commit_author_email(c) == dependabot_email }.
318
+ map { |c| c.fetch("message", nil) }.
319
+ compact.
320
+ reject { |m| m.start_with?("Merge") }.
321
+ map(&:strip)
322
+ end
323
+
310
324
  def recent_codecommit_commit_messages
311
325
  @recent_codecommit_commit_messages ||=
312
326
  codecommit_client_for_source.commits
@@ -324,6 +338,7 @@ module Dependabot
324
338
  when "github" then last_github_dependabot_commit_message
325
339
  when "gitlab" then last_gitlab_dependabot_commit_message
326
340
  when "azure" then last_azure_dependabot_commit_message
341
+ when "bitbucket" then last_bitbucket_dependabot_commit_message
327
342
  when "codecommit" then last_codecommit_dependabot_commit_message
328
343
  else raise "Unsupported provider: #{source.provider}"
329
344
  end
@@ -365,6 +380,16 @@ module Dependabot
365
380
  strip
366
381
  end
367
382
 
383
+ def last_bitbucket_dependabot_commit_message
384
+ @recent_bitbucket_commit_messages ||=
385
+ bitbucket_client_for_source.commits(source.repo)
386
+
387
+ @recent_bitbucket_commit_messages.
388
+ find { |c| bitbucket_commit_author_email(c) == dependabot_email }&.
389
+ fetch("message", nil)&.
390
+ strip
391
+ end
392
+
368
393
  def last_codecommit_dependabot_commit_message
369
394
  @recent_codecommit_commit_messages ||=
370
395
  codecommit_client_for_source.commits(source.repo)
@@ -379,6 +404,11 @@ module Dependabot
379
404
  commit.fetch("author").fetch("email", "")
380
405
  end
381
406
 
407
+ def bitbucket_commit_author_email(commit)
408
+ matches = commit.fetch("author").fetch("raw").match(/<(.*)>/)
409
+ matches ? matches[1] : ""
410
+ end
411
+
382
412
  def github_client_for_source
383
413
  @github_client_for_source ||=
384
414
  Dependabot::Clients::GithubWithRetries.for_source(
@@ -403,6 +433,14 @@ module Dependabot
403
433
  )
404
434
  end
405
435
 
436
+ def bitbucket_client_for_source
437
+ @bitbucket_client_for_source ||=
438
+ Dependabot::Clients::Bitbucket.for_source(
439
+ source: source,
440
+ credentials: credentials
441
+ )
442
+ end
443
+
406
444
  def codecommit_client_for_source
407
445
  @codecommit_client_for_source ||=
408
446
  Dependabot::Clients::CodeCommit.for_source(
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Dependabot
4
- VERSION = "0.129.3"
4
+ VERSION = "0.129.4"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dependabot-common
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.129.3
4
+ version: 0.129.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dependabot
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-01-05 00:00:00.000000000 Z
11
+ date: 2021-01-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aws-sdk-codecommit
@@ -168,20 +168,6 @@ dependencies:
168
168
  - - "~>"
169
169
  - !ruby/object:Gem::Version
170
170
  version: '2.0'
171
- - !ruby/object:Gem::Dependency
172
- name: parseconfig
173
- requirement: !ruby/object:Gem::Requirement
174
- requirements:
175
- - - "~>"
176
- - !ruby/object:Gem::Version
177
- version: '1.0'
178
- type: :runtime
179
- prerelease: false
180
- version_requirements: !ruby/object:Gem::Requirement
181
- requirements:
182
- - - "~>"
183
- - !ruby/object:Gem::Version
184
- version: '1.0'
185
171
  - !ruby/object:Gem::Dependency
186
172
  name: parser
187
173
  requirement: !ruby/object:Gem::Requirement
@@ -396,6 +382,7 @@ files:
396
382
  - lib/dependabot/metadata_finders/base/release_finder.rb
397
383
  - lib/dependabot/pull_request_creator.rb
398
384
  - lib/dependabot/pull_request_creator/azure.rb
385
+ - lib/dependabot/pull_request_creator/bitbucket.rb
399
386
  - lib/dependabot/pull_request_creator/branch_namer.rb
400
387
  - lib/dependabot/pull_request_creator/codecommit.rb
401
388
  - lib/dependabot/pull_request_creator/commit_signer.rb