reviewlette 0.0.6
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 +7 -0
- data/.gitignore +27 -0
- data/.rspec +1 -0
- data/Gemfile +17 -0
- data/Gemfile.lock +114 -0
- data/Guardfile +24 -0
- data/LICENSE +21 -0
- data/README.md +95 -0
- data/Rakefile +5 -0
- data/bin/reviewlette +7 -0
- data/lib/matching.rb +0 -0
- data/lib/reviewlette.rb +202 -0
- data/lib/reviewlette/database.rb +77 -0
- data/lib/reviewlette/exceptions.rb +7 -0
- data/lib/reviewlette/github_connection.rb +45 -0
- data/lib/reviewlette/graph_gen.rb +62 -0
- data/lib/reviewlette/mail.rb +27 -0
- data/lib/reviewlette/trello_connection.rb +79 -0
- data/lib/reviewlette/vacations.rb +38 -0
- data/lib/reviewlette/version.rb +3 -0
- data/prophet.rb +4 -0
- data/reviewlette.gemspec +26 -0
- data/spec/database_spec.rb +34 -0
- data/spec/github_connection_spec.rb +96 -0
- data/spec/reviewlette_spec.rb +367 -0
- data/spec/spec_helper.rb +23 -0
- data/spec/support/request_stubbing.rb +305 -0
- data/spec/trello_connection_spec.rb +202 -0
- data/spec/vacation_spec.rb +12 -0
- data/task +1 -0
- metadata +145 -0
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'sequel'
|
2
|
+
require 'fileutils'
|
3
|
+
module Reviewlette
|
4
|
+
|
5
|
+
class Database
|
6
|
+
|
7
|
+
FileUtils.mkdir_p("#{File.join(ENV['HOME'])}/.config/reviewlette/") unless Dir.exists?("#{ENV['HOME']}/.config/reviewlette")
|
8
|
+
FileUtils.cp ("#{File.dirname(__FILE__)}/../../reviewlette.db"), ("#{File.join(Dir.home)}" + '/.config/reviewlette/') unless File.exists?(("#{File.join(Dir.home)}" + '/.config/reviewlette/reviewlette.db'))
|
9
|
+
|
10
|
+
@path = "#{File.join(ENV['HOME'])}/.config/reviewlette"
|
11
|
+
DATABASE = Sequel.connect("sqlite://#{@path}/reviewlette.db")
|
12
|
+
|
13
|
+
attr_accessor :reviewer, :reviews
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
@reviewer = DATABASE.from(:reviewer)
|
17
|
+
@reviews = DATABASE.from(:reviews)
|
18
|
+
end
|
19
|
+
|
20
|
+
def count_up(reviewer)
|
21
|
+
pr_reviewer = @reviewer.where(:trello_name => reviewer).select(:trello_name).first.values.first
|
22
|
+
counter = @reviewer.where(:trello_name => pr_reviewer).select(:reviews).first.values.first
|
23
|
+
@reviewer.where(:trello_name => reviewer).update(:reviews => counter.next)
|
24
|
+
end
|
25
|
+
|
26
|
+
def add_pr_to_db(pr_name, reviewer)
|
27
|
+
@reviews.insert(:name => pr_name, :reviewer => reviewer, :created_at => Date.today)
|
28
|
+
count_up(reviewer)
|
29
|
+
end
|
30
|
+
|
31
|
+
def get_users_tel_entries
|
32
|
+
@reviewer.map([:tel_name]).flatten.select{|user| user unless user.nil?}
|
33
|
+
end
|
34
|
+
|
35
|
+
def get_users_gh_entries
|
36
|
+
@reviewer.map([:gh_name]).flatten.select{|user| user unless user.nil?}
|
37
|
+
end
|
38
|
+
|
39
|
+
def get_users_trello_entries
|
40
|
+
@reviewer.where(:vacation => 'false').map([:trello_name]).flatten.select{|user| user unless user.nil?}
|
41
|
+
end
|
42
|
+
|
43
|
+
def count_reviews(reviewer)
|
44
|
+
@reviews.where(:reviewer => reviewer).count
|
45
|
+
end
|
46
|
+
|
47
|
+
def find_gh_name_by_trello_name(trello_name)
|
48
|
+
@reviewer.where(:trello_name => trello_name).select(:gh_name).first.values.first
|
49
|
+
end
|
50
|
+
|
51
|
+
def set_vacation_flag(reviewer, state)
|
52
|
+
@reviewer.where(:tel_name => reviewer).update(:vacation => state)
|
53
|
+
end
|
54
|
+
|
55
|
+
def conscruct_graph_struct
|
56
|
+
data = []
|
57
|
+
get_users_trello_entries.each do |x|
|
58
|
+
data.push({ label: x, value: count_reviews(x) })
|
59
|
+
end
|
60
|
+
data
|
61
|
+
end
|
62
|
+
|
63
|
+
def conscruct_line_data
|
64
|
+
data = []
|
65
|
+
date_range = (Date.today - 7 )..(Date.today)
|
66
|
+
get_users_trello_entries.each do |name|
|
67
|
+
date_range.each do |date|
|
68
|
+
abc = {}
|
69
|
+
abc[:created_at] = date
|
70
|
+
abc[name] = @reviews.where(:reviewer => name, :created_at => date).select(:created_at).count
|
71
|
+
data.push(abc)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
data
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'octokit'
|
3
|
+
|
4
|
+
module Reviewlette
|
5
|
+
|
6
|
+
class GithubConnection
|
7
|
+
|
8
|
+
GITHUB_CONFIG = YAML.load_file("#{File.dirname(__FILE__)}/../../config/.github.yml")
|
9
|
+
|
10
|
+
attr_accessor :client, :repo
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
gh_connection
|
14
|
+
end
|
15
|
+
|
16
|
+
def gh_connection
|
17
|
+
@client = Octokit::Client.new(:access_token => GITHUB_CONFIG['token'])
|
18
|
+
end
|
19
|
+
|
20
|
+
def get_branch_name(pr_id, repo)
|
21
|
+
@client.pull_requests(repo)[pr_id].head.ref
|
22
|
+
end
|
23
|
+
|
24
|
+
def list_pulls(repo)
|
25
|
+
@client.pull_requests(repo)
|
26
|
+
end
|
27
|
+
|
28
|
+
def pull_merged?(repo, number)
|
29
|
+
client.pull_merged?(repo, number)
|
30
|
+
end
|
31
|
+
|
32
|
+
def add_assignee(repo, number, title, body, name)
|
33
|
+
@client.update_issue(repo, number, title, body, :assignee => name)
|
34
|
+
end
|
35
|
+
|
36
|
+
def comment_on_issue(repo, number, name, trello_card_url)
|
37
|
+
@client.add_comment(repo, number, "@#{name} is your reviewer :thumbsup: check #{trello_card_url}")
|
38
|
+
end
|
39
|
+
|
40
|
+
def list_issues(repo)
|
41
|
+
@client.list_issues(repo)
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'json'
|
2
|
+
require_relative 'database'
|
3
|
+
module Reviewlette
|
4
|
+
|
5
|
+
class Graphgenerator
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@db = Reviewlette::Database.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def write_to_graphs(filename, content)
|
12
|
+
File.open(filename, 'w') { |file| file.write(content) }
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
def model_graphs(data2, data, type)
|
17
|
+
@content = %Q|
|
18
|
+
<link rel="stylesheet" href="http://cdn.oesmith.co.uk/morris-0.5.1.css">
|
19
|
+
<script src="http://code.jquery.com/jquery-1.11.1.min.js"></script>
|
20
|
+
<script src="http://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.0/raphael-min.js"></script>
|
21
|
+
<script src="http://cdn.oesmith.co.uk/morris-0.5.1.min.js"></script>
|
22
|
+
<meta http-equiv="refresh" content="15" />
|
23
|
+
|
24
|
+
<div id="Donut" style="height: 250px;"></div>
|
25
|
+
<div id="Line" style="height: 250px;"></div>
|
26
|
+
<div id="Bar" style="height: 250px;"></div>
|
27
|
+
|
28
|
+
<script>
|
29
|
+
new Morris.#{type}({
|
30
|
+
element: 'Donut',
|
31
|
+
data: #{data},
|
32
|
+
xkey: 'label',
|
33
|
+
colors: ['#80BFFF', '#F0F0F0', '#0000FF', '#00FFFF', '#FF00FF', '#C0C0C0'],
|
34
|
+
ykeys: ['value'],
|
35
|
+
labels: ['Value']
|
36
|
+
});
|
37
|
+
</script>
|
38
|
+
|
39
|
+
<script>
|
40
|
+
new Morris.Line({
|
41
|
+
element: 'Line',
|
42
|
+
data: #{data2},
|
43
|
+
xkey: 'created_at',
|
44
|
+
colors: ['#80BFFF', '#F0F0F0', '#0000FF', '#00FFFF', '#FF00FF', '#C0C0C0'],
|
45
|
+
ykeys: #{@db.get_users_trello_entries},
|
46
|
+
labels: #{@db.get_users_trello_entries}
|
47
|
+
});
|
48
|
+
</script>
|
49
|
+
|
50
|
+
<script>
|
51
|
+
new Morris.Bar({
|
52
|
+
element: 'Bar',
|
53
|
+
data: #{data},
|
54
|
+
xkey: 'label',
|
55
|
+
colors: ['#80BFFF', '#F0F0F0', '#0000FF', '#00FFFF', '#FF00FF', '#C0C0C0'],
|
56
|
+
ykeys: ['value'],
|
57
|
+
labels: ['Value']
|
58
|
+
});
|
59
|
+
</script>|
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
|
2
|
+
require 'net/smtp'
|
3
|
+
|
4
|
+
module Supporter
|
5
|
+
class Mailer
|
6
|
+
def send_email(to,opts={})
|
7
|
+
opts[:server] ||= 'localhost'
|
8
|
+
opts[:from] ||= 'review@lette.com'
|
9
|
+
opts[:from_alias] ||= 'Reviewlette'
|
10
|
+
opts[:subject] ||= "Commanding Officer of the Week"
|
11
|
+
opts[:body] ||= ""
|
12
|
+
|
13
|
+
msg = <<END_OF_MESSAGE
|
14
|
+
From: #{opts[:from_alias]} <#{opts[:from]}>
|
15
|
+
To: <#{to}>
|
16
|
+
Subject: #{opts[:subject]}
|
17
|
+
#{opts[:body]}
|
18
|
+
END_OF_MESSAGE
|
19
|
+
|
20
|
+
Net::SMTP.start(opts[:server]) do |smtp|
|
21
|
+
smtp.send_message msg, opts[:from], to
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
## make this more generic in order to use it either for COotW and PairProgramming sessions and reviews
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'trello'
|
3
|
+
require 'logger'
|
4
|
+
require_relative 'database'
|
5
|
+
require_relative 'exceptions'
|
6
|
+
|
7
|
+
class Trello::Card
|
8
|
+
|
9
|
+
def assignees
|
10
|
+
@trello_connection = ::Reviewlette::TrelloConnection.new
|
11
|
+
member_ids.map{|id| @trello_connection.find_member_by_id(id)}
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
module Reviewlette
|
16
|
+
|
17
|
+
class TrelloConnection
|
18
|
+
|
19
|
+
attr_accessor :board
|
20
|
+
|
21
|
+
def initialize
|
22
|
+
setup_trello
|
23
|
+
end
|
24
|
+
|
25
|
+
def determine_reviewer(card)
|
26
|
+
raise AlreadyAssignedException, "Everyone on the team is assigned to the Card." if reviewer_exception_handler(card)
|
27
|
+
find_member_by_username(sample_reviewer(card))
|
28
|
+
end
|
29
|
+
|
30
|
+
def sample_reviewer(card)
|
31
|
+
(team - card.assignees.map(&:username)).sample
|
32
|
+
end
|
33
|
+
|
34
|
+
def reviewer_exception_handler(card)
|
35
|
+
(team - card.assignees.map(&:username)).count <= 0
|
36
|
+
end
|
37
|
+
|
38
|
+
def add_reviewer_to_card(reviewer, card)
|
39
|
+
card.add_member(reviewer) if reviewer
|
40
|
+
end
|
41
|
+
|
42
|
+
def comment_on_card(reviewer, card)
|
43
|
+
card.add_comment(reviewer) if reviewer
|
44
|
+
end
|
45
|
+
|
46
|
+
def move_card_to_list(card, column)
|
47
|
+
card.move_to_list(column)
|
48
|
+
end
|
49
|
+
|
50
|
+
def team
|
51
|
+
#where vacation is not false
|
52
|
+
@team ||= Reviewlette::Database.new.get_users_trello_entries
|
53
|
+
end
|
54
|
+
|
55
|
+
def find_column(column_name)
|
56
|
+
@board.lists.find {|x| x.name == column_name}
|
57
|
+
end
|
58
|
+
|
59
|
+
def find_member_by_username(username)
|
60
|
+
@board.members.find{|m| m.username == username}
|
61
|
+
end
|
62
|
+
|
63
|
+
def find_member_by_id(id)
|
64
|
+
@board.members.find{|m| m.id == id}
|
65
|
+
end
|
66
|
+
|
67
|
+
def find_card_by_id(id)
|
68
|
+
@board.cards.find{|c| c.short_id == id.to_i}
|
69
|
+
end
|
70
|
+
|
71
|
+
def setup_trello
|
72
|
+
Trello.configure do |config|
|
73
|
+
config.developer_public_key = ::Reviewlette::TRELLO_CONFIG1['consumerkey']
|
74
|
+
config.member_token = ::Reviewlette::TRELLO_CONFIG1['oauthtoken']
|
75
|
+
end
|
76
|
+
@board = Trello::Board.find(::Reviewlette::TRELLO_CONFIG1['board_id'])
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'net/telnet'
|
2
|
+
module Reviewlette
|
3
|
+
|
4
|
+
class Vacations
|
5
|
+
|
6
|
+
def self.find_vacations(username)
|
7
|
+
vacations = []
|
8
|
+
tn = Net::Telnet.new('Host' => 'present.suse.de', 'Port' => 9874, 'Binmode' => false)
|
9
|
+
collect = false
|
10
|
+
tn.cmd(username) do |data|
|
11
|
+
data.split("\n").each do |l|
|
12
|
+
collect = true if l =~ /^Absence/
|
13
|
+
next unless collect
|
14
|
+
if l[0,1] == "-"
|
15
|
+
collect = false
|
16
|
+
next
|
17
|
+
end
|
18
|
+
dates = []
|
19
|
+
l.split(" ").each do |date|
|
20
|
+
unless date =~ /#{Time.now.year}/
|
21
|
+
next
|
22
|
+
end
|
23
|
+
dates.push(date)
|
24
|
+
end
|
25
|
+
case dates.size
|
26
|
+
when 1
|
27
|
+
vacations.push("#{dates[0]}")
|
28
|
+
when 2
|
29
|
+
vacations.push("#{dates[0]} - #{dates[1]}")
|
30
|
+
else
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
tn.close
|
35
|
+
vacations
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/prophet.rb
ADDED
data/reviewlette.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'reviewlette/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "reviewlette"
|
8
|
+
spec.version = Reviewlette::VERSION
|
9
|
+
spec.authors = ["jschmid1"]
|
10
|
+
spec.email = ["jschmid@suse.de"]
|
11
|
+
spec.summary = %q{Randomly assignes a reviewer to your Pullrequest and corresponding Trello Card.}
|
12
|
+
spec.description = %q{Easy, fair and trackable labor division in your team.}
|
13
|
+
spec.homepage = "http://rubygems.org/gems/reviewlette"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_runtime_dependency 'ruby-trello', '=1.1.1'
|
22
|
+
spec.add_runtime_dependency 'octokit', '=3.1.0'
|
23
|
+
spec.add_runtime_dependency 'sequel', '=4.13.0'
|
24
|
+
spec.add_runtime_dependency 'sqlite3', '=1.3.9'
|
25
|
+
end
|
26
|
+
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Reviewlette::Database do
|
4
|
+
|
5
|
+
|
6
|
+
subject { Reviewlette::Database.new }
|
7
|
+
|
8
|
+
describe '#count_reviews' do
|
9
|
+
|
10
|
+
it 'counts the reviews done by a single user' do
|
11
|
+
to_be_counted = [1,2,3,4]
|
12
|
+
expect(subject.instance_variable_get(:@reviews)).to receive(:where).and_return to_be_counted
|
13
|
+
expect(to_be_counted).to receive(:count).and_return to_be_counted.count
|
14
|
+
subject.count_reviews(subject.reviewer.first.values[1])
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe '#get_users_gh_entries' do
|
19
|
+
it 'gets all github usernames in #Array' do
|
20
|
+
expect(subject.reviewer).to receive(:map).and_return [['jschmid']]
|
21
|
+
subject.get_users_gh_entries
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe '#add_pr_to_db' do
|
26
|
+
|
27
|
+
it 'writes the name of the pr to db' do
|
28
|
+
expect(subject.reviews).to receive(:insert)
|
29
|
+
expect(subject).to receive(:count_up).with(subject.reviewer.first.values[1])
|
30
|
+
subject.add_pr_to_db('review_123', subject.reviewer.first.values[1])
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Reviewlette::GithubConnection do
|
4
|
+
|
5
|
+
subject { Reviewlette::GithubConnection }
|
6
|
+
|
7
|
+
describe '.new' do
|
8
|
+
|
9
|
+
it 'sets up Github connection' do
|
10
|
+
config = Reviewlette::GithubConnection::GITHUB_CONFIG
|
11
|
+
expect(Octokit::Client).to receive(:new).with(:access_token => config['token'])
|
12
|
+
subject.new
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '#pull_merged?' do
|
17
|
+
let( :connection ) { subject.new }
|
18
|
+
|
19
|
+
it 'checks if the pull is merged' do
|
20
|
+
allow(connection.client).to receive(:pull_merged?).with('true', 6).and_return true
|
21
|
+
expect(connection.pull_merged?('true', 6)).to be true
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'checks if the pull is not merged' do
|
25
|
+
allow(connection.client).to receive(:pull_merged?).with('false', 5).and_return false
|
26
|
+
expect(connection.pull_merged?('false', 5)).to be false
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe '#add_assignee' do
|
31
|
+
let( :connection ) { subject.new }
|
32
|
+
|
33
|
+
it 'adds an assignee to the gh issue' do
|
34
|
+
params = [connection.repo, 4, 'title', 'body', 'name']
|
35
|
+
params2 = [connection.repo, 4, 'title', 'body', :assignee => 'name']
|
36
|
+
allow(connection.client).to receive(:update_issue).with(*params2).and_return true
|
37
|
+
expect(connection.add_assignee(*params)).to eq true
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'fails to add an assignee to the gh issue' do
|
41
|
+
params = [connection.repo, 4, 'title', 'body', 'name']
|
42
|
+
params2 = [connection.repo, 4, 'title', 'body', :assignee => 'name']
|
43
|
+
allow(connection.client).to receive(:update_issue).with(*params2).and_return false
|
44
|
+
expect(connection.add_assignee(*params)).to eq false
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe '#comment_on_issue' do
|
49
|
+
let( :connection ) { subject.new }
|
50
|
+
|
51
|
+
it 'comments on a given issue' do
|
52
|
+
params = [connection.repo, 4, '@name is your reviewer :thumbsup: check url']
|
53
|
+
params2 = [connection.repo, 4, 'name', 'url']
|
54
|
+
allow(connection.client).to receive(:add_comment).with(*params).and_return true
|
55
|
+
expect(connection.comment_on_issue(*params2)).to eq true
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'fails to comment on a given issue and fails' do
|
59
|
+
params = [connection.repo, 4, '@name is your reviewer :thumbsup: check url']
|
60
|
+
params2 = [connection.repo, 4, 'name', 'url']
|
61
|
+
allow(connection.client).to receive(:add_comment).with(*params).and_return false
|
62
|
+
expect(connection.comment_on_issue(*params2)).to eq false
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe '#list_issues' do
|
67
|
+
let( :connection ) { subject.new }
|
68
|
+
|
69
|
+
it 'fails to determine if an assignee is set' do
|
70
|
+
allow(connection.client).to receive_message_chain(:list_issues)
|
71
|
+
connection.list_issues(connection.repo)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe '#list_pulls' do
|
76
|
+
let( :connection ) { subject.new }
|
77
|
+
|
78
|
+
it 'lists a pullrequests for a given repository' do
|
79
|
+
expect(connection.client).to receive(:pull_requests)
|
80
|
+
connection.list_pulls(connection.repo)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe '#get_branch_name' do
|
85
|
+
let( :connection ) { subject.new }
|
86
|
+
|
87
|
+
it 'get branch name based on a repo and a pullrequest id' do
|
88
|
+
pulls = [double({ 'head' => double({ 'ref' => 'number'})})]
|
89
|
+
pr = pulls.first
|
90
|
+
expect(connection.client).to receive(:pull_requests).with(connection.repo).and_return pulls
|
91
|
+
expect(pulls).to receive(:[]).with(3).and_return pr
|
92
|
+
expect(pr).to receive(:head).and_return pr.head
|
93
|
+
connection.get_branch_name(3, connection.repo)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|