forked from github/github-services
/
bugzilla.rb
144 lines (131 loc) · 4.48 KB
/
bugzilla.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
class Service::Bugzilla < Service
string :server_url, :username
password :password
boolean :central_repository
def receive_push
# Check for settings
if data['server_url'].to_s.empty?
raise_config_error "Bugzilla url not set"
end
if data['username'].to_s.empty?
raise_config_error "username not set"
end
if data['password'].to_s.empty?
raise_config_error "password not set"
end
# Post comments on all bugs identified in commits
repository = payload['repository']['url'].to_s
commits = payload['commits'].collect{|c| Commit.new(c)}
bug_commits = sort_commits(commits)
bugs_to_close = []
bug_commits.each_pair do | bug, commits |
if data['central_repository']
# Only include first line of message if commit already mentioned
commit_messages = commits.collect{|c| c.comment(bug_mentions_commit?(bug, c))}
else
# Don't include commits already mentioned
commit_messages = commits.select{|c| !bug_mentions_commit?(bug, c)}.collect{|c| c.comment}
end
post_bug_comment(bug, repository, commit_messages)
if commits.collect{|c| c.closes}.any?
bugs_to_close << bug
end
end
# Close bugs
if data['central_repository']
close_bugs(bugs_to_close)
end
end
attr_writer :xmlrpc_client # Can define own server for testing
def xmlrpc_client
# XMLRPC client to communicate with Bugzilla server
@xmlrpc_client ||= begin
client = XMLRPC::Client.new2("#{data['server_url'].to_s}/xmlrpc.cgi")
client.call('User.login', {'login' => data['username'].to_s, 'password' => data['password'].to_s})
client
rescue XMLRPC::FaultException
raise_config_error "Invalid login details"
rescue SocketError, RuntimeError
raise_config_error "Invalid server url"
end
end
def sort_commits(commits)
# Sort commits into a hash of arrays based on bug id
bug_commits = Hash.new{|k,v| k[v] = []}
commits.each do |commit|
commit.bugs.each do |bug|
bug_commits[bug] << commit
end
end
return bug_commits
end
def bug_mentions_commit?(bug_id, commit)
# Check if a bug already mentions a commit.
# This is to avoid repeating commits that have
# been pushed to another person's repository
result = xmlrpc_client.call('Bug.comments', {'ids' => [bug_id]})
all_comments = result['bugs']["#{bug_id}"]['comments'].collect{|c| c['text']}.join("\n")
all_comments.include? commit.id
rescue XMLRPC::FaultException, RuntimeError
# Bug doesn't exist or Bugzilla version doesn't support getting comments
false
end
def post_bug_comment(bug, repository, commit_messages)
# Post a comment on an individual bug
if commit_messages.length == 0
return
end
if commit_messages.length > 1
message = "Commits pushed to #{repository}\n\n"
else
message = "Commit pushed to #{repository}\n\n"
end
message += commit_messages.join("\n\n")
begin
xmlrpc_client.call('Bug.add_comment', {'id' => bug, 'comment' => message})
rescue XMLRPC::FaultException
# Bug doesn't exist or user can't add comments, do nothing
rescue RuntimeError
raise_config_error "Bugzilla version doesn't support adding comments"
end
end
def close_bugs(bug_ids)
if bug_ids.length > 0
begin
xmlrpc_client.call('Bug.update', {'ids' => bug_ids, 'status' => 'RESOLVED', 'resolution' => 'FIXED'})
rescue XMLRPC::FaultException, RuntimeError
# Bug doesn't exist, user can't close bug, or version < 4.0 that doesn't support Bug.update.
# Do nothing
end
end
end
class Commit
attr_reader :closes, :bugs, :url, :id
def initialize(commit_hash)
@id = commit_hash['id'].to_s
@url = commit_hash['url'].to_s
@message = commit_hash['message'].to_s
@closes = false
# Get the list of bugs mentioned in this commit message
message_re = /((close|fix|address)e?(s|d)? )?(ticket|bug|tracker item|issue)s?:? *([\d ,\+&#and]+)/i
if (@message =~ message_re) != nil
if $1
@closes = true
end
@bugs = $5.split(/[^\d]+/).select{|b| !b.empty?}.collect{|b| Integer(b)}
else
@bugs = []
end
end
def comment(first_line_only=false)
# Comment contents for a commit
output = "#{@url}\n"
if first_line_only
output += @message.lines.first.strip
else
output += @message.strip
end
return output
end
end
end