9367a78 by Francois Marier at 2011-04-29 1
#!/usr/bin/python
2
#
2e6d31e by Francois Marier at 2012-02-22 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
9367a78 by Francois Marier at 2011-04-29 7
#
2e6d31e by Francois Marier at 2012-02-22 8
# Copyright (C) 2011, 2012 Catalyst IT (http://www.catalyst.net.nz)
9367a78 by Francois Marier at 2011-04-29 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
2e6d31e by Francois Marier at 2012-02-22 29
from xml.dom.minidom import Document
30
import xmlrpclib
9367a78 by Francois Marier at 2011-04-29 31
fd45e91 by Francois Marier at 2011-05-31 32
FROM_ADDRESS = 'dev@mahara.org'
33
TO_ADDRESS_SUFFIX = '@bugs.launchpad.net'
2e6d31e by Francois Marier at 2012-02-22 34
CIA_PROJECT = 'Mahara'
35
CIA_SERVER = 'http://cia.vc'
9367a78 by Francois Marier at 2011-04-29 36
BASE_DIR = '/home/gerrit/mahara_reviews'
37
2e6d31e by Francois Marier at 2012-02-22 38
def send_notifications(change_url, project, branch, submitter, commit):
9367a78 by Francois Marier at 2011-04-29 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
224a5c1 by Francois Marier at 2011-05-09 43
    bug_regexp = '[Bb]ug:? *#?([0-9]+)'
9367a78 by Francois Marier at 2011-04-29 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)
fd45e91 by Francois Marier at 2011-05-31 51
            send_bug_mail(token, change_url, project, commit, submitter, branch, git_log)
52
2e6d31e by Francois Marier at 2012-02-22 53
    submit_to_cia(project, commit, branch, git_log)
54
fd45e91 by Francois Marier at 2011-05-31 55
def send_bug_mail(bug_number, change_url, project, commit, submitter, branch, git_log):
56
57
    to_address = bug_number + TO_ADDRESS_SUFFIX
9367a78 by Francois Marier at 2011-04-29 58
59
    gitorious_url = 'http://gitorious.org/mahara/%s/commit/%s' % (project, commit)
60
    body = '''Reviewed:  %s
61
Committed: %s
62
Submitter: %s
fd45e91 by Francois Marier at 2011-05-31 63
Branch:    %s\n''' % (change_url, gitorious_url, submitter, branch)
9367a78 by Francois Marier at 2011-04-29 64
65
    msg = MIMEText(body + '\n' + git_log)
66
    msg['Subject'] = 'A change has been merged'
67
    msg['From'] = FROM_ADDRESS
fd45e91 by Francois Marier at 2011-05-31 68
    msg['To'] = to_address
9367a78 by Francois Marier at 2011-04-29 69
70
    s = smtplib.SMTP()
71
    s.connect()
fd45e91 by Francois Marier at 2011-05-31 72
    s.sendmail(FROM_ADDRESS, [to_address], msg.as_string())
9367a78 by Francois Marier at 2011-04-29 73
    s.quit()
74
2e6d31e by Francois Marier at 2012-02-22 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
9367a78 by Francois Marier at 2011-04-29 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:
2e6d31e by Francois Marier at 2012-02-22 171
        send_notifications(change_url, project, branch, submitter, commit)
9367a78 by Francois Marier at 2011-04-29 172
    else:
173
        print 'Missing arguments'
174
	return 1
175
176
    return 0;
177
178
if __name__ == '__main__':
179
    sys.exit(main())