vx-service_connector 0.2.12 → 0.2.13
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 +4 -4
- data/Gemfile +2 -0
- data/lib/vx/service_connector.rb +3 -0
- data/lib/vx/service_connector/bitbucket.rb +51 -0
- data/lib/vx/service_connector/bitbucket/commits.rb +30 -0
- data/lib/vx/service_connector/bitbucket/deploy_keys.rb +29 -0
- data/lib/vx/service_connector/bitbucket/files.rb +21 -0
- data/lib/vx/service_connector/bitbucket/hooks.rb +58 -0
- data/lib/vx/service_connector/bitbucket/notices.rb +11 -0
- data/lib/vx/service_connector/bitbucket/payload.rb +136 -0
- data/lib/vx/service_connector/bitbucket/repos.rb +56 -0
- data/lib/vx/service_connector/bitbucket/session.rb +113 -0
- data/lib/vx/service_connector/error.rb +1 -0
- data/lib/vx/service_connector/version.rb +1 -1
- data/spec/fixtures/bitbucket/add_deploy_key.json +7 -0
- data/spec/fixtures/bitbucket/commit.json +72 -0
- data/spec/fixtures/bitbucket/create_hook.json +12 -0
- data/spec/fixtures/bitbucket/deploy_keys.json +1 -0
- data/spec/fixtures/bitbucket/hooks.json +38 -0
- data/spec/fixtures/bitbucket/payload/created_pull_request.json +120 -0
- data/spec/fixtures/bitbucket/payload/declined_pull_request.json +80 -0
- data/spec/fixtures/bitbucket/payload/foreign_pull_request.json +120 -0
- data/spec/fixtures/bitbucket/payload/push.json +38 -0
- data/spec/fixtures/bitbucket/payload/updated_pull_request.json +80 -0
- data/spec/fixtures/bitbucket/repos.json +206 -0
- data/spec/fixtures/bitbucket/user_privileges.json +6 -0
- data/spec/fixtures/bitbucket/user_repo.json +63 -0
- data/spec/fixtures/bitbucket/user_repos.json +197 -0
- data/spec/lib/bitbucket_payload_spec.rb +82 -0
- data/spec/lib/bitbucket_spec.rb +144 -0
- data/spec/support/bitbucket_web_mocks.rb +87 -0
- data/vx-service_connector.gemspec +4 -3
- metadata +59 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 54d71cae077981d1705c726931668bf9b2340cb9
|
4
|
+
data.tar.gz: c0dea00e6d39a55757f96b6bd6bc6cb9e4428182
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bbb12a18dd04bb71320c037805d831f1e809fbe32b0d91055dfdcbd4287b6fd395dacb32fc67df1509f4654c0b19af8910f02b3bfc323f79910d4affc54d82da
|
7
|
+
data.tar.gz: 07315c7f4b03f472551857bbda073f7a8c934dce4fe324b15730194243fc5dd4d7de0fcf72089c08bf3ba34dca0e97d6ee9d7f9f2a33f2f8ee5339bb293a5fce
|
data/Gemfile
CHANGED
data/lib/vx/service_connector.rb
CHANGED
@@ -9,6 +9,7 @@ module Vx
|
|
9
9
|
autoload :Github, File.expand_path("../service_connector/github", __FILE__)
|
10
10
|
autoload :GitlabV5, File.expand_path("../service_connector/gitlab_v5", __FILE__)
|
11
11
|
autoload :GitlabV6, File.expand_path("../service_connector/gitlab_v6", __FILE__)
|
12
|
+
autoload :Bitbucket, File.expand_path("../service_connector/bitbucket", __FILE__)
|
12
13
|
autoload :Model, File.expand_path("../service_connector/model", __FILE__)
|
13
14
|
|
14
15
|
extend self
|
@@ -19,6 +20,8 @@ module Vx
|
|
19
20
|
Github
|
20
21
|
when :gitlab_v6
|
21
22
|
GitlabV6
|
23
|
+
when :bitbucket
|
24
|
+
Bitbucket
|
22
25
|
else
|
23
26
|
raise ArgumentError, "Serivice for #{name.inspect} is not defined"
|
24
27
|
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Vx
|
2
|
+
module ServiceConnector
|
3
|
+
Bitbucket = Struct.new(:login, :options) do
|
4
|
+
|
5
|
+
include ServiceConnector::Base
|
6
|
+
|
7
|
+
def repos
|
8
|
+
Bitbucket::Repos.new(session).to_a
|
9
|
+
end
|
10
|
+
|
11
|
+
def organizations
|
12
|
+
[]
|
13
|
+
end
|
14
|
+
|
15
|
+
def hooks(repo)
|
16
|
+
Bitbucket::Hooks.new(session, repo)
|
17
|
+
end
|
18
|
+
|
19
|
+
def deploy_keys(repo)
|
20
|
+
Bitbucket::DeployKeys.new(session, repo)
|
21
|
+
end
|
22
|
+
|
23
|
+
def notices(repo)
|
24
|
+
Bitbucket::Notices.new(session, repo)
|
25
|
+
end
|
26
|
+
|
27
|
+
def files(repo)
|
28
|
+
Bitbucket::Files.new(session, repo)
|
29
|
+
end
|
30
|
+
|
31
|
+
def payload(repo, params)
|
32
|
+
Bitbucket::Payload.new(session, params).build
|
33
|
+
end
|
34
|
+
|
35
|
+
def commits(repo, options = {})
|
36
|
+
Bitbucket::Commits.new(session, repo)
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def create_session
|
42
|
+
self.class::Session.new(login, options)
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
Dir[File.expand_path("../bitbucket/*.rb", __FILE__)].each do |f|
|
50
|
+
require f
|
51
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Vx
|
2
|
+
module ServiceConnector
|
3
|
+
class Bitbucket
|
4
|
+
Commits = Struct.new(:session, :repo) do
|
5
|
+
def last(options = {})
|
6
|
+
begin
|
7
|
+
commits = session.get "api/1.0/repositories/#{repo.full_name}/changesets/?limit=1"
|
8
|
+
commit = commits["changesets"].first
|
9
|
+
author, email = commit["raw_author"].split(/[<>]/)
|
10
|
+
sha = commit["raw_node"]
|
11
|
+
branch = commit["branch"] || 'master'
|
12
|
+
Model::Payload.from_hash(
|
13
|
+
skip: false,
|
14
|
+
pull_request?: false,
|
15
|
+
branch: branch,
|
16
|
+
branch_label: branch,
|
17
|
+
sha: sha,
|
18
|
+
message: commit["message"],
|
19
|
+
author: author.strip,
|
20
|
+
author_email: email,
|
21
|
+
web_url: session.endpoint.to_s + "/#{repo.full_name}/commits/#{sha}"
|
22
|
+
)
|
23
|
+
rescue RequestError
|
24
|
+
nil
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Vx
|
2
|
+
module ServiceConnector
|
3
|
+
class Bitbucket
|
4
|
+
DeployKeys = Struct.new(:session, :repo) do
|
5
|
+
|
6
|
+
def all
|
7
|
+
begin
|
8
|
+
session.get "api/1.0/repositories/#{repo.full_name}/deploy-keys?pagelen=100"
|
9
|
+
rescue RequestError
|
10
|
+
[]
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def create(key_name, public_key)
|
15
|
+
session.post "api/1.0/repositories/#{repo.full_name}/deploy-keys", label: key_name, key: public_key
|
16
|
+
end
|
17
|
+
|
18
|
+
def destroy(key_name)
|
19
|
+
all.select do |key|
|
20
|
+
key['label'] == key_name
|
21
|
+
end.map do |key|
|
22
|
+
session.delete "api/1.0/repositories/#{repo.full_name}/deploy-keys/#{key['pk']}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'base64'
|
2
|
+
|
3
|
+
module Vx
|
4
|
+
module ServiceConnector
|
5
|
+
class Bitbucket
|
6
|
+
Files = Struct.new(:session, :repo) do
|
7
|
+
|
8
|
+
def get(sha, path)
|
9
|
+
begin
|
10
|
+
re = session.get("api/1.0/repositories/#{repo.full_name}/src/#{sha}/#{path}")
|
11
|
+
re['source']
|
12
|
+
rescue RequestError => e
|
13
|
+
$stderr.puts "ERROR: #{e.inspect}"
|
14
|
+
nil
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Vx
|
2
|
+
module ServiceConnector
|
3
|
+
class Bitbucket
|
4
|
+
Hooks = Struct.new(:session, :repo) do
|
5
|
+
|
6
|
+
def all
|
7
|
+
begin
|
8
|
+
session.get hooks_url
|
9
|
+
rescue RequestError
|
10
|
+
[]
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def create(url, token)
|
15
|
+
session.post(
|
16
|
+
hooks_url,
|
17
|
+
'type' => 'POST',
|
18
|
+
'URL' => url
|
19
|
+
)
|
20
|
+
session.post(
|
21
|
+
hooks_url,
|
22
|
+
'type' => 'Pull Request POST',
|
23
|
+
'URL' => url,
|
24
|
+
'create/edit/merge/decline' => 'on',
|
25
|
+
'comments' => 'off',
|
26
|
+
'approve/unapprove' => 'off'
|
27
|
+
)
|
28
|
+
end
|
29
|
+
|
30
|
+
def destroy(url_mask)
|
31
|
+
all.select do |hook|
|
32
|
+
url = extract_url(hook)
|
33
|
+
url && url =~ /#{Regexp.escape url_mask}/
|
34
|
+
end.map do |hook|
|
35
|
+
session.delete hook_url(hook['id'])
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def extract_url(hook)
|
42
|
+
fields = hook['service']['fields']
|
43
|
+
url = fields.select{|f| f['name'] == 'URL' }.map{|f| f['value'] }.first
|
44
|
+
url
|
45
|
+
end
|
46
|
+
|
47
|
+
def hooks_url
|
48
|
+
"api/1.0/repositories/#{repo.full_name}/services"
|
49
|
+
end
|
50
|
+
|
51
|
+
def hook_url(id)
|
52
|
+
"#{hooks_url}/#{id}"
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
module Vx
|
2
|
+
module ServiceConnector
|
3
|
+
class Bitbucket
|
4
|
+
Payload = Struct.new(:session, :params) do
|
5
|
+
|
6
|
+
def build
|
7
|
+
ServiceConnector::Model::Payload.new(
|
8
|
+
!!ignore?,
|
9
|
+
pull_request?,
|
10
|
+
pull_request_number,
|
11
|
+
branch,
|
12
|
+
branch_label,
|
13
|
+
sha,
|
14
|
+
message,
|
15
|
+
author,
|
16
|
+
author_email,
|
17
|
+
web_url
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def pull_request?
|
24
|
+
!key? 'repository'
|
25
|
+
end
|
26
|
+
|
27
|
+
def pull_request_number
|
28
|
+
if pull_request? && pull_request.key?('id')
|
29
|
+
pull_request['id']
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def sha
|
34
|
+
if pull_request?
|
35
|
+
pull_request['source']['commit']['hash']
|
36
|
+
else
|
37
|
+
head_commit['raw_node']
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def branch
|
42
|
+
@branch ||= begin
|
43
|
+
if pull_request?
|
44
|
+
pull_request['source']['branch']['name']
|
45
|
+
else
|
46
|
+
head_commit['branch']
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def branch_label
|
52
|
+
branch
|
53
|
+
end
|
54
|
+
|
55
|
+
def web_url
|
56
|
+
if pull_request?
|
57
|
+
pull_request['links'] ? pull_request['links']['html']['href'] : nil
|
58
|
+
else
|
59
|
+
"https://bitbucket.org#{params['repository']['absolute_url']}commits/#{head_commit['raw_node']}"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def message
|
64
|
+
if pull_request?
|
65
|
+
commit_for_pull_request["message"]
|
66
|
+
else
|
67
|
+
head_commit['message']
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def author
|
72
|
+
if pull_request?
|
73
|
+
commit_for_pull_request["author"]["user"]["display_name"]
|
74
|
+
else
|
75
|
+
head_commit['author']
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def author_email
|
80
|
+
if pull_request?
|
81
|
+
commit_for_pull_request["author"]["raw"][/.*<([^>]*)/,1]
|
82
|
+
else
|
83
|
+
head_commit['raw_author'][/.*<([^>]*)/,1]
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def close_pull_request?
|
88
|
+
pull_request? && (pull_request['state'] && %w(DECLINED MERGED).include?(pull_request['state']))
|
89
|
+
end
|
90
|
+
|
91
|
+
def pull_request_head_repo_full_name
|
92
|
+
pull_request['source']['repository']['full_name']
|
93
|
+
end
|
94
|
+
|
95
|
+
def pull_request_base_repo_full_name
|
96
|
+
pull_request['destination']['repository']['full_name']
|
97
|
+
end
|
98
|
+
|
99
|
+
def foreign_pull_request?
|
100
|
+
pull_request_head_repo_full_name != pull_request_base_repo_full_name
|
101
|
+
end
|
102
|
+
|
103
|
+
def ignore?
|
104
|
+
if pull_request?
|
105
|
+
close_pull_request? || !foreign_pull_request?
|
106
|
+
else
|
107
|
+
sha == '0000000000000000000000000000000000000000'
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def commit_for_pull_request
|
112
|
+
@commit_for_pull_request ||= begin
|
113
|
+
session.get pull_request['source']['commit']['links']['self']['href']
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def head_commit
|
118
|
+
params['commits'].last || {}
|
119
|
+
end
|
120
|
+
|
121
|
+
def pull_request
|
122
|
+
params.first.last
|
123
|
+
end
|
124
|
+
|
125
|
+
def key?(name)
|
126
|
+
params.key? name
|
127
|
+
end
|
128
|
+
|
129
|
+
def [](val)
|
130
|
+
params[val]
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Vx
|
2
|
+
module ServiceConnector
|
3
|
+
class Bitbucket
|
4
|
+
Repos = Struct.new(:session) do
|
5
|
+
|
6
|
+
def to_a
|
7
|
+
@repos ||= user_repositories
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def user_repositories
|
13
|
+
res = session.get("api/1.0/user/repositories")
|
14
|
+
res.select do |repo|
|
15
|
+
git?(repo) && repo_access?(repo['owner'])
|
16
|
+
end.map do |repo|
|
17
|
+
repo_to_model repo
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def repo_to_model(repo)
|
22
|
+
name = repo['owner'] + "/" + repo['slug']
|
23
|
+
Model::Repo.new(
|
24
|
+
name,
|
25
|
+
name,
|
26
|
+
repo['is_private'],
|
27
|
+
"git@#{session.endpoint.host}/#{name}.git",
|
28
|
+
"#{session.endpoint}/#{name}",
|
29
|
+
repo['description']
|
30
|
+
)
|
31
|
+
end
|
32
|
+
|
33
|
+
def team_admin
|
34
|
+
@team_admin ||= begin
|
35
|
+
values = session.get("api/1.0/user/privileges")['teams']
|
36
|
+
values.select{|k,v| v == 'admin' }.keys
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def login
|
41
|
+
session.login
|
42
|
+
end
|
43
|
+
|
44
|
+
def git?(repo)
|
45
|
+
repo['scm'] == 'git'
|
46
|
+
end
|
47
|
+
|
48
|
+
def repo_access?(repo_owner)
|
49
|
+
repo_owner == login ||
|
50
|
+
team_admin.include?(repo_owner)
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
require 'oauth'
|
2
|
+
require 'json'
|
3
|
+
require 'ostruct'
|
4
|
+
require 'uri'
|
5
|
+
|
6
|
+
module Vx
|
7
|
+
module ServiceConnector
|
8
|
+
class Bitbucket
|
9
|
+
Session = Struct.new(:login, :options) do
|
10
|
+
|
11
|
+
def get(url)
|
12
|
+
wrap do
|
13
|
+
res = agent.get request_url(url)
|
14
|
+
response! res
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def post(url, options = {})
|
19
|
+
wrap do
|
20
|
+
res = agent.post request_url(url), options
|
21
|
+
response! res
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def delete(url)
|
26
|
+
wrap do
|
27
|
+
res = agent.delete request_url(url)
|
28
|
+
response! res
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def endpoint
|
33
|
+
@endpoint ||= URI("https://bitbucket.org")
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.test
|
37
|
+
{
|
38
|
+
consumer_key: "key",
|
39
|
+
consumer_secret: "secret",
|
40
|
+
token: "token",
|
41
|
+
token_secret: "token secret"
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
|
48
|
+
def request_url(url)
|
49
|
+
if url.include?(endpoint.to_s)
|
50
|
+
url
|
51
|
+
else
|
52
|
+
"#{endpoint}/#{url}"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def response!(res)
|
57
|
+
if (200..204).include?(res.code.to_i)
|
58
|
+
if res.header['Content-Type'].include?("application/json")
|
59
|
+
::JSON.parse(res.body)
|
60
|
+
else
|
61
|
+
res.body
|
62
|
+
end
|
63
|
+
else
|
64
|
+
raise RequestError, res.body
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def wrap
|
69
|
+
begin
|
70
|
+
yield
|
71
|
+
rescue Errno::ETIMEDOUT => e
|
72
|
+
raise RequestError, e
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def validate_options!
|
77
|
+
unless options.is_a?(Hash) && options.keys.sort == [:consumer_key, :consumer_secret, :token, :token_secret]
|
78
|
+
raise InvalidArguments, options
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def agent
|
83
|
+
@agent ||= begin
|
84
|
+
validate_options!
|
85
|
+
consumer = OAuth::Consumer.new(
|
86
|
+
options[:consumer_key], options[:consumer_secret],
|
87
|
+
site: endpoint.to_s
|
88
|
+
)
|
89
|
+
token = OAuth::AccessToken.new consumer, options[:token], options[:token_secret]
|
90
|
+
=begin
|
91
|
+
Sawyer::Agent.new(endpoint) do |http|
|
92
|
+
http.headers['content-type'] = 'application/json'
|
93
|
+
http.headers['accept'] = 'application/json'
|
94
|
+
http.ssl.verify = false
|
95
|
+
http.options[:timeout] = 5
|
96
|
+
http.options[:open_timeout] = 5
|
97
|
+
http.request :oauth,
|
98
|
+
consumer_key: "7j4RYZEd8YTQdfTWkJ",
|
99
|
+
consumer_secret: "Y8fSdaDK4GxKzvJn3tKRzyyYXctYSbUV",
|
100
|
+
token: "WhFvryHuZ82XLKW6aP",
|
101
|
+
token_secret: "zLSQe2DwhA4mfzwnPA53pKqLSyZgX5XH"
|
102
|
+
ignore_extra_keys: true
|
103
|
+
http.response :logger
|
104
|
+
end
|
105
|
+
=end
|
106
|
+
token
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|