1
#!/usr/bin/python
2
#
3
# Gerrit Launchpad and CIA Hook, inspired by:
4
#   https://github.com/hobbs/jirret
5
#   http://cia.vc/clients/git/ciabot.bash
6
#   http://cia.vc/clients/bzr/cia_bzr.py
7
#
8
# Copyright (C) 2011, 2012 Catalyst IT (http://www.catalyst.net.nz)
9
#
10
# This program is free software: you can redistribute it and/or modify
11
# it under the terms of the GNU General Public License as published by
12
# the Free Software Foundation, either version 3 of the License, or
13
# (at your option) any later version.
14
#
15
# This program is distributed in the hope that it will be useful,
16
# but WITHOUT ANY WARRANTY; without even the implied warranty of
17
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
# GNU General Public License for more details.
19
#
20
# You should have received a copy of the GNU General Public License
21
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
22
23
from email.mime.text import MIMEText
24
from getopt import getopt
25
import re
26
import smtplib
27
import subprocess
28
import sys
29
from xml.dom.minidom import Document
30
import xmlrpclib
31
32
FROM_ADDRESS = 'dev@mahara.org'
33
TO_ADDRESS_SUFFIX = '@bugs.launchpad.net'
34
CIA_PROJECT = 'Mahara'
35
CIA_SERVER = 'http://cia.vc'
36
BASE_DIR = '/home/gerrit/mahara_reviews'
37
38
def send_notifications(change_url, project, branch, submitter, commit):
39
    # Extract git log of all merged commits
40
    git_log = subprocess.Popen(['git', '--git-dir=' + BASE_DIR + '/git/' + project + '.git', 'log', '--no-merges', commit + '^1..' + commit], stdout=subprocess.PIPE).communicate()[0]
41
42
    # Find bug numbers referenced in the git log
43
    bug_regexp = '[Bb]ug:? *#?([0-9]+)'
44
    tokens = re.split(bug_regexp, git_log)
45
46
    # Extract unique bug numbers
47
    bugs = []
48
    for token in tokens:
49
        if re.match('^\d+$', token) and (token not in bugs):
50
            bugs.append(token)
51
            send_bug_mail(token, change_url, project, commit, submitter, branch, git_log)
52
53
    submit_to_cia(project, commit, branch, git_log)
54
55
def send_bug_mail(bug_number, change_url, project, commit, submitter, branch, git_log):
56
57
    to_address = bug_number + TO_ADDRESS_SUFFIX
58
59
    gitorious_url = 'http://gitorious.org/mahara/%s/commit/%s' % (project, commit)
60
    body = '''Reviewed:  %s
61
Committed: %s
62
Submitter: %s
63
Branch:    %s\n''' % (change_url, gitorious_url, submitter, branch)
64
65
    msg = MIMEText(body + '\n' + git_log)
66
    msg['Subject'] = 'A change has been merged'
67
    msg['From'] = FROM_ADDRESS
68
    msg['To'] = to_address
69
70
    s = smtplib.SMTP()
71
    s.connect()
72
    s.sendmail(FROM_ADDRESS, [to_address], msg.as_string())
73
    s.quit()
74
75
def append_commit(commits, document, project, full_commit_id, author, log):
76
    if not full_commit_id or not author:
77
        return
78
79
    revision = subprocess.Popen(['git', '--git-dir=' + BASE_DIR + '/git/' + project + '.git', 'rev-parse', '--short', full_commit_id], stdout=subprocess.PIPE).communicate()[0].strip()
80
81
    gitorious_url = 'http://gitorious.org/mahara/%s/commit/%s' % (project, full_commit_id)
82
    commit = document.createElement('commit')
83
84
    commit_author = document.createElement('author')
85
    commit_author.appendChild(document.createTextNode(author))
86
    commit.appendChild(commit_author)
87
88
    commit_revision = document.createElement('revision')
89
    commit_revision.appendChild(document.createTextNode(revision))
90
    commit.appendChild(commit_revision)
91
92
    commit_log = document.createElement('log')
93
    commit_log.appendChild(document.createTextNode(log))
94
    commit.appendChild(commit_log)
95
96
    commit_url = document.createElement('url')
97
    commit_url.appendChild(document.createTextNode(gitorious_url))
98
    commit.appendChild(commit_url)
99
100
    commits.append(commit)
101
102
def generate_commits(document, project, git_log):
103
    commits = []
104
    commit_id = author = log = None
105
    for line in git_log.splitlines():
106
        if line.startswith('commit'):
107
            append_commit(commits, document, project, commit_id, author, log)
108
            commit_id = line[7:]
109
            author = log = None
110
        elif line.startswith('Author:'):
111
            full_author = line[8:]
112
            author = re.search('^(.+) <[^>]+>$', full_author).group(1)
113
        elif line.startswith('    ') and not log:
114
            log = line[4:]
115
116
    append_commit(commits, document, project, commit_id, author, log)
117
    return commits
118
119
def submit_to_cia(project, commit, branch, git_log):
120
    doc = Document()
121
    message = doc.createElement('message')
122
    generator = doc.createElement('generator')
123
    generator_name = doc.createElement('name')
124
    generator_name.appendChild(doc.createTextNode('Mahara custom CIA script'))
125
    generator_version = doc.createElement('version')
126
    generator_version.appendChild(doc.createTextNode('1.0'))
127
    generator_url = doc.createElement('url')
128
    generator_url.appendChild(doc.createTextNode('http://gitorious.org/mahara/mahara-scripts/blobs/master/change-merged'))
129
    generator.appendChild(generator_name)
130
    generator.appendChild(generator_version)
131
    generator.appendChild(generator_url)
132
    message.appendChild(generator)
133
134
    source = doc.createElement('source')
135
    source_project = doc.createElement('project')
136
    source_project.appendChild(doc.createTextNode(CIA_PROJECT))
137
    source_branch = doc.createElement('branch')
138
    source_branch.appendChild(doc.createTextNode(branch))
139
    source.appendChild(source_project)
140
    source.appendChild(source_branch)
141
    message.appendChild(source)
142
143
    body = doc.createElement('body')
144
    message.appendChild(body)
145
    doc.appendChild(message)
146
147
    for commit in generate_commits(doc, project, git_log):
148
        body.appendChild(commit)
149
        xmlrpclib.ServerProxy(CIA_SERVER).hub.deliver(doc.toxml())
150
        body.removeChild(commit)
151
152
def main():
153
    # https://gerrit.googlecode.com/svn/documentation/2.1.6/config-hooks.html#change-merged
154
    gerrit_args = ['change=', 'change-url=', 'project=', 'branch=', 'submitter=', 'commit=']
155
    args, unused = getopt(sys.argv[1:], '', gerrit_args)
156
157
    change_url = project = branch = submitter = commit = None
158
    for argname, argv in args:
159
        if argname == '--change-url':
160
            change_url = argv
161
        elif argname == '--project':
162
            project = argv
163
        elif argname == '--branch':
164
            branch = argv
165
        elif argname == '--submitter':
166
            submitter = argv
167
        elif argname == '--commit':
168
            commit = argv
169
170
    if change_url and project and branch and submitter and commit:
171
        send_notifications(change_url, project, branch, submitter, commit)
172
    else:
173
        print 'Missing arguments'
174
	return 1
175
176
    return 0;
177
178
if __name__ == '__main__':
179
    sys.exit(main())