Skip to content

Commit 16ae39e

Browse files
committed
initial commit
0 parents  commit 16ae39e

25 files changed

+699
-0
lines changed

.env

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
REVIEWBOARD_URI=http://localhost:8000
2+
REVIEWBOARD_USER=admin
3+
REVIEWBOARD_PASSWORD=admin
4+
GITLAB_ENDPOINT=http://localhost:10080/api/v3
5+
GITLAB_PRIVATE_TOKEN=tdUyLoh4vMhDpnMGusx1
6+
GITLAB_TRIGGER_LABEL=reviewboard
7+
GIT_REPO_PATH=repositories

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/repositories/

Gemfile

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
source 'https://rubygems.org'
2+
gem 'sinatra'
3+
gem 'thin'
4+
gem 'httmultiparty'
5+
gem 'gitlab'
6+
gem 'dotenv'
7+
gem 'pry'
8+
gem 'awesome_print'
9+
group :development do
10+
gem 'rubocop'
11+
gem 'minitest'
12+
gem 'minitest-reporters'
13+
gem 'minitest-filesystem'
14+
gem 'rake'
15+
end

Gemfile.lock

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
GEM
2+
remote: https://rubygems.org/
3+
specs:
4+
ansi (1.5.0)
5+
ast (2.0.0)
6+
astrolabe (1.3.0)
7+
parser (>= 2.2.0.pre.3, < 3.0)
8+
awesome_print (1.6.1)
9+
builder (3.2.2)
10+
coderay (1.1.0)
11+
daemons (1.2.2)
12+
dotenv (2.0.2)
13+
eventmachine (1.0.7)
14+
gitlab (3.4.0)
15+
httparty
16+
terminal-table
17+
httmultiparty (0.3.16)
18+
httparty (>= 0.7.3)
19+
mimemagic
20+
multipart-post
21+
httparty (0.13.5)
22+
json (~> 1.8)
23+
multi_xml (>= 0.5.2)
24+
json (1.8.3)
25+
method_source (0.8.2)
26+
mimemagic (0.3.0)
27+
minitest (5.4.3)
28+
minitest-filesystem (1.2.0)
29+
minitest-reporters (1.0.17)
30+
ansi
31+
builder
32+
minitest (>= 5.0)
33+
ruby-progressbar
34+
multi_xml (0.5.5)
35+
multipart-post (2.0.0)
36+
parser (2.3.0.pre.2)
37+
ast (>= 1.1, < 3.0)
38+
powerpack (0.1.1)
39+
pry (0.10.1)
40+
coderay (~> 1.1.0)
41+
method_source (~> 0.8.1)
42+
slop (~> 3.4)
43+
rack (1.6.1)
44+
rack-protection (1.5.3)
45+
rack
46+
rainbow (2.0.0)
47+
rake (10.4.2)
48+
rubocop (0.32.1)
49+
astrolabe (~> 1.3)
50+
parser (>= 2.2.2.5, < 3.0)
51+
powerpack (~> 0.1)
52+
rainbow (>= 1.99.1, < 3.0)
53+
ruby-progressbar (~> 1.4)
54+
ruby-progressbar (1.7.5)
55+
sinatra (1.4.6)
56+
rack (~> 1.4)
57+
rack-protection (~> 1.4)
58+
tilt (>= 1.3, < 3)
59+
slop (3.6.0)
60+
terminal-table (1.4.5)
61+
thin (1.6.3)
62+
daemons (~> 1.0, >= 1.0.9)
63+
eventmachine (~> 1.0)
64+
rack (~> 1.0)
65+
tilt (2.0.1)
66+
67+
PLATFORMS
68+
ruby
69+
70+
DEPENDENCIES
71+
awesome_print
72+
dotenv
73+
gitlab
74+
httmultiparty
75+
minitest
76+
minitest-filesystem
77+
minitest-reporters
78+
pry
79+
rake
80+
rubocop
81+
sinatra
82+
thin

Rakefile

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
require 'bundler'
2+
require 'rake/testtask'
3+
4+
desc 'Default Task'
5+
task default: [:test]
6+
7+
Rake::TestTask.new(:test) do |t|
8+
t.libs << 'lib'
9+
t.libs << 'test'
10+
t.pattern = 'test/**/test_*.rb'
11+
end

app.rb

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
$LOAD_PATH << File.expand_path(File.dirname(__FILE__)) + '/lib'
2+
3+
require 'hooks/reviewboard'
4+
require 'git_service'
5+
require 'reviewboard/client'
6+
7+
Dotenv.load
8+
9+
post '/' do
10+
request.body.rewind
11+
@request_payload = JSON.parse request.body.read
12+
# ap @request_payload
13+
14+
reviewboard_hook.process(@request_payload)
15+
end
16+
17+
def reviewboard_hook
18+
@reviewboard_hook ||= Hooks::Reviewboard.new(
19+
Reviewboard::Client.new(
20+
base_uri: ENV['REVIEWBOARD_URI'],
21+
basic_auth: {
22+
username: ENV['REVIEWBOARD_USER'],
23+
password: ENV['REVIEWBOARD_PASSWORD']
24+
}
25+
),
26+
Gitlab.client(
27+
endpoint: ENV['GITLAB_ENDPOINT'],
28+
private_token: ENV['GITLAB_PRIVATE_TOKEN']
29+
),
30+
GitService.new(ENV['GIT_REPO_PATH']),
31+
label: ENV['GITLAB_TRIGGER_LABEL']
32+
)
33+
end

config.ru

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
require 'rubygems'
2+
require 'bundler'
3+
4+
Bundler.require
5+
6+
require './app'
7+
run Sinatra::Application

lib/git_service.rb

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
require 'digest/sha1'
2+
require 'open3'
3+
require 'tempfile'
4+
5+
# class
6+
class GitService
7+
class Exception < StandardError
8+
end
9+
10+
def initialize(repo_path)
11+
@repo_path = repo_path
12+
end
13+
14+
def create_diff(repo, from, to)
15+
Dir.mkdir(@repo_path) unless Dir.exist?(@repo_path)
16+
hash = Digest::SHA1.hexdigest(repo)
17+
repo_path = File.join(@repo_path, hash)
18+
if Dir.exist?(repo_path)
19+
command 'git remote update --prune', chdir: repo_path
20+
else
21+
command 'git', 'clone', '--mirror', repo, hash, chdir: @repo_path
22+
end
23+
file = Tempfile.new('git_diff')
24+
25+
begin
26+
command "git diff --full-index #{from}...#{to} > #{file.path}", chdir: repo_path
27+
28+
yield file.path
29+
ensure
30+
file.unlink
31+
end
32+
end
33+
34+
private
35+
36+
def command(*command)
37+
ret = ''
38+
Open3.popen3(*command) do |_stdin, stdout, stderr, wait_thr|
39+
ret = stdout.gets
40+
raise Exception.new(stderr.gets.chomp) unless wait_thr.value == 0
41+
end
42+
ret
43+
end
44+
end

lib/hooks/reviewboard.rb

+102
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
module Hooks
2+
# Transfers merge request changes to reviewboard
3+
class Reviewboard
4+
def initialize(reviewboard, gitlab, git, options = {})
5+
@reviewboard = reviewboard
6+
@gitlab = gitlab
7+
@git = git
8+
@options = options
9+
end
10+
11+
def process(merge_request)
12+
attr = merge_request.fetch('object_attributes')
13+
14+
return unless label_matches?(attr)
15+
16+
review_id = review_id_from_comments(
17+
attr.fetch('source_project_id'),
18+
attr.fetch('id')
19+
)
20+
21+
unless review_id.nil?
22+
review = @reviewboard.review_request(review_id)
23+
return if review.fetch('commit_id') == attr.fetch('last_commit').fetch('id')
24+
end
25+
26+
@git.create_diff(
27+
attr.fetch('source').fetch('ssh_url'),
28+
attr.fetch('target_branch'),
29+
attr.fetch('source_branch')
30+
) do |file|
31+
if review_id.nil?
32+
review_id = create_review_request(merge_request, file)
33+
@gitlab.create_merge_request_comment(
34+
attr.fetch('source_project_id'),
35+
attr.fetch('id'),
36+
"REVIEW_ID: #{review_id}"
37+
)
38+
else
39+
update_review_request(review_id, merge_request, file)
40+
end
41+
end
42+
43+
@gitlab.create_merge_request_comment(
44+
attr.fetch('source_project_id'),
45+
attr.fetch('id'),
46+
@reviewboard.last_diff_uri(review_id)
47+
)
48+
end
49+
50+
def label_matches?(attr)
51+
return true unless @options.key?(:label)
52+
53+
@gitlab.merge_request(
54+
attr.fetch('source_project_id'),
55+
attr.fetch('id')
56+
).labels.include?(@options[:label])
57+
end
58+
59+
def update_review_request(review_id, merge_request, file)
60+
attr = merge_request.fetch('object_attributes')
61+
62+
@reviewboard.sync_review_request(
63+
review_id: review_id,
64+
draft_params:
65+
{
66+
public: true,
67+
commit_id: attr.fetch('last_commit').fetch('id')
68+
},
69+
diff: file
70+
)
71+
end
72+
73+
def create_review_request(merge_request, file)
74+
attr = merge_request.fetch('object_attributes')
75+
repo = attr.fetch('source')
76+
77+
@reviewboard.sync_review_request(
78+
create_params:
79+
{
80+
repository: repo.fetch('name'),
81+
submit_as: merge_request.fetch('user').fetch('username')
82+
},
83+
draft_params:
84+
{
85+
branch: attr.fetch('source_branch'),
86+
summary: attr.fetch('title'),
87+
description: attr.fetch('url'),
88+
public: true,
89+
commit_id: attr.fetch('last_commit').fetch('id')
90+
},
91+
diff: file
92+
)
93+
end
94+
95+
def review_id_from_comments(pid, mid)
96+
@gitlab.merge_request_comments(pid, mid).each do |comment|
97+
return $1.to_i if /REVIEW_ID: (\d+)/.match(comment.note)
98+
end
99+
nil
100+
end
101+
end
102+
end

0 commit comments

Comments
 (0)