7438
|
1 |
# Copyright (C) 2007-8 Brendan Cully <brendan@kublai.com>
|
|
2 |
# Published under the GNU GPL
|
|
3 |
|
|
4 |
"""CIA notification
|
|
5 |
|
|
6 |
This is meant to be run as a changegroup or incoming hook.
|
|
7 |
To configure it, set the following options in your hgrc:
|
|
8 |
|
|
9 |
[cia]
|
|
10 |
# your registered CIA user name
|
|
11 |
user = foo
|
|
12 |
# the name of the project in CIA
|
|
13 |
project = foo
|
|
14 |
# the module (subproject) (optional)
|
|
15 |
#module = foo
|
|
16 |
# Append a diffstat to the log message (optional)
|
|
17 |
#diffstat = False
|
|
18 |
# Template to use for log messages (optional)
|
|
19 |
#template = {desc}\n{baseurl}/rev/{node}-- {diffstat}
|
|
20 |
# Style to use (optional)
|
|
21 |
#style = foo
|
|
22 |
# The URL of the CIA notification service (optional)
|
|
23 |
# You can use mailto: URLs to send by email, eg
|
|
24 |
# mailto:cia@cia.vc
|
|
25 |
# Make sure to set email.from if you do this.
|
|
26 |
#url = http://cia.vc/
|
|
27 |
# print message instead of sending it (optional)
|
|
28 |
#test = False
|
|
29 |
|
|
30 |
[hooks]
|
|
31 |
# one of these:
|
|
32 |
changegroup.cia = python:hgcia.hook
|
|
33 |
#incoming.cia = python:hgcia.hook
|
|
34 |
|
|
35 |
[web]
|
|
36 |
# If you want hyperlinks (optional)
|
|
37 |
baseurl = http://server/path/to/repo
|
|
38 |
"""
|
|
39 |
|
|
40 |
from mercurial.i18n import _
|
|
41 |
from mercurial.node import *
|
|
42 |
from mercurial import cmdutil, patch, templater, util, mail
|
|
43 |
import email.Parser
|
|
44 |
|
|
45 |
import xmlrpclib
|
|
46 |
from xml.sax import saxutils
|
|
47 |
|
|
48 |
socket_timeout = 30 # seconds
|
|
49 |
try:
|
|
50 |
# set a timeout for the socket so you don't have to wait so looooong
|
|
51 |
# when cia.vc is having problems. requires python >= 2.3:
|
|
52 |
import socket
|
|
53 |
socket.setdefaulttimeout(socket_timeout)
|
|
54 |
except:
|
|
55 |
pass
|
|
56 |
|
|
57 |
HGCIA_VERSION = '0.1'
|
|
58 |
HGCIA_URL = 'http://hg.kublai.com/mercurial/hgcia'
|
|
59 |
|
|
60 |
|
|
61 |
class ciamsg(object):
|
|
62 |
""" A CIA message """
|
|
63 |
def __init__(self, cia, ctx):
|
|
64 |
self.cia = cia
|
|
65 |
self.ctx = ctx
|
|
66 |
self.url = self.cia.url
|
|
67 |
|
|
68 |
def fileelem(self, path, uri, action):
|
|
69 |
if uri:
|
|
70 |
uri = ' uri=%s' % saxutils.quoteattr(uri)
|
|
71 |
return '<file%s action=%s>%s</file>' % (
|
|
72 |
uri, saxutils.quoteattr(action), saxutils.escape(path))
|
|
73 |
|
|
74 |
def fileelems(self):
|
|
75 |
n = self.ctx.node()
|
|
76 |
f = self.cia.repo.status(self.ctx.parents()[0].node(), n)
|
|
77 |
url = self.url or ''
|
|
78 |
elems = []
|
|
79 |
for path in f[0]:
|
|
80 |
uri = '%s/diff/%s/%s' % (url, short(n), path)
|
|
81 |
elems.append(self.fileelem(path, url and uri, 'modify'))
|
|
82 |
for path in f[1]:
|
|
83 |
# TODO: copy/rename ?
|
|
84 |
uri = '%s/file/%s/%s' % (url, short(n), path)
|
|
85 |
elems.append(self.fileelem(path, url and uri, 'add'))
|
|
86 |
for path in f[2]:
|
|
87 |
elems.append(self.fileelem(path, '', 'remove'))
|
|
88 |
|
|
89 |
return '\n'.join(elems)
|
|
90 |
|
|
91 |
def sourceelem(self, project, module=None, branch=None):
|
|
92 |
msg = ['<source>', '<project>%s</project>' % saxutils.escape(project)]
|
|
93 |
if module:
|
|
94 |
msg.append('<module>%s</module>' % saxutils.escape(module))
|
|
95 |
if branch:
|
|
96 |
msg.append('<branch>%s</branch>' % saxutils.escape(branch))
|
|
97 |
msg.append('</source>')
|
|
98 |
|
|
99 |
return '\n'.join(msg)
|
|
100 |
|
|
101 |
def diffstat(self):
|
|
102 |
class patchbuf:
|
|
103 |
def __init__(self):
|
|
104 |
self.lines = []
|
|
105 |
# diffstat is stupid
|
|
106 |
self.name = 'cia'
|
|
107 |
def write(self, data):
|
|
108 |
self.lines.append(data)
|
|
109 |
def close(self):
|
|
110 |
pass
|
|
111 |
|
|
112 |
n = self.ctx.node()
|
|
113 |
pbuf = patchbuf()
|
|
114 |
patch.export(self.cia.repo, [n], fp=pbuf)
|
|
115 |
return patch.diffstat(pbuf.lines) or ''
|
|
116 |
|
|
117 |
def logmsg(self):
|
|
118 |
diffstat = self.cia.diffstat and self.diffstat() or ''
|
|
119 |
self.cia.ui.pushbuffer()
|
|
120 |
self.cia.templater.show(self.ctx, changes=self.ctx.changeset(),
|
|
121 |
url=self.cia.url, diffstat=diffstat)
|
|
122 |
return self.cia.ui.popbuffer()
|
|
123 |
|
|
124 |
def xml(self):
|
|
125 |
n = short(self.ctx.node())
|
|
126 |
src = self.sourceelem(self.cia.project, module=self.cia.module,
|
|
127 |
branch=self.ctx.branch())
|
|
128 |
# unix timestamp
|
|
129 |
dt = self.ctx.date()
|
|
130 |
timestamp = dt[0]
|
|
131 |
|
|
132 |
author = saxutils.escape(self.ctx.user())
|
|
133 |
rev = '%d:%s' % (self.ctx.rev(), n)
|
|
134 |
log = saxutils.escape(self.logmsg())
|
|
135 |
|
|
136 |
url = self.url and '<url>%s/rev/%s</url>' % (saxutils.escape(self.url),
|
|
137 |
n) or ''
|
|
138 |
|
|
139 |
msg = """
|
|
140 |
<message>
|
|
141 |
<generator>
|
|
142 |
<name>Mercurial (hgcia)</name>
|
|
143 |
<version>%s</version>
|
|
144 |
<url>%s</url>
|
|
145 |
<user>%s</user>
|
|
146 |
</generator>
|
|
147 |
%s
|
|
148 |
<body>
|
|
149 |
<commit>
|
|
150 |
<author>%s</author>
|
|
151 |
<version>%s</version>
|
|
152 |
<log>%s</log>
|
|
153 |
%s
|
|
154 |
<files>%s</files>
|
|
155 |
</commit>
|
|
156 |
</body>
|
|
157 |
<timestamp>%d</timestamp>
|
|
158 |
</message>
|
|
159 |
""" % \
|
|
160 |
(HGCIA_VERSION, saxutils.escape(HGCIA_URL),
|
|
161 |
saxutils.escape(self.cia.user), src, author, rev, log, url,
|
|
162 |
self.fileelems(), timestamp)
|
|
163 |
|
|
164 |
return msg
|
|
165 |
|
|
166 |
|
|
167 |
class hgcia(object):
|
|
168 |
""" CIA notification class """
|
|
169 |
|
|
170 |
deftemplate = '{desc}'
|
|
171 |
dstemplate = '{desc}\n-- \n{diffstat}'
|
|
172 |
|
|
173 |
def __init__(self, ui, repo):
|
|
174 |
self.ui = ui
|
|
175 |
self.repo = repo
|
|
176 |
|
|
177 |
self.ciaurl = self.ui.config('cia', 'url', 'http://cia.vc')
|
|
178 |
self.user = self.ui.config('cia', 'user')
|
|
179 |
self.project = self.ui.config('cia', 'project')
|
|
180 |
self.module = self.ui.config('cia', 'module')
|
|
181 |
self.diffstat = self.ui.configbool('cia', 'diffstat')
|
|
182 |
self.emailfrom = self.ui.config('email', 'from')
|
|
183 |
self.dryrun = self.ui.configbool('cia', 'test')
|
|
184 |
self.url = self.ui.config('web', 'baseurl')
|
|
185 |
|
|
186 |
style = self.ui.config('cia', 'style')
|
|
187 |
template = self.ui.config('cia', 'template')
|
|
188 |
if not template:
|
|
189 |
template = self.diffstat and self.dstemplate or self.deftemplate
|
|
190 |
template = templater.parsestring(template, quoted=False)
|
|
191 |
t = cmdutil.changeset_templater(self.ui, self.repo, False, style, False)
|
|
192 |
t.use_template(template)
|
|
193 |
self.templater = t
|
|
194 |
|
|
195 |
def sendrpc(self, msg):
|
|
196 |
srv = xmlrpclib.Server(self.ciaurl)
|
|
197 |
srv.hub.deliver(msg)
|
|
198 |
|
|
199 |
def sendemail(self, address, data):
|
|
200 |
p = email.Parser.Parser()
|
|
201 |
msg = p.parsestr(data)
|
|
202 |
msg['Date'] = util.datestr(format="%a, %d %b %Y %H:%M:%S %1%2")
|
|
203 |
msg['To'] = address
|
|
204 |
msg['From'] = self.emailfrom
|
|
205 |
msg['Subject'] = 'DeliverXML'
|
|
206 |
msg['Content-type'] = 'text/xml'
|
|
207 |
msgtext = msg.as_string(0)
|
|
208 |
|
|
209 |
self.ui.status(_('hgcia: sending update to %s\n') % address)
|
|
210 |
mail.sendmail(self.ui, util.email(self.emailfrom),
|
|
211 |
[address], msgtext)
|
|
212 |
|
|
213 |
|
|
214 |
def hook(ui, repo, hooktype, node=None, url=None, **kwargs):
|
|
215 |
""" send CIA notification """
|
|
216 |
def sendmsg(cia, ctx):
|
|
217 |
msg = ciamsg(cia, ctx).xml()
|
|
218 |
if cia.dryrun:
|
|
219 |
ui.write(msg)
|
|
220 |
elif cia.ciaurl.startswith('mailto:'):
|
|
221 |
if not cia.emailfrom:
|
|
222 |
raise util.Abort(_('email.from must be defined when '
|
|
223 |
'sending by email'))
|
|
224 |
cia.sendemail(cia.ciaurl[7:], msg)
|
|
225 |
else:
|
|
226 |
cia.sendrpc(msg)
|
|
227 |
|
|
228 |
n = bin(node)
|
|
229 |
cia = hgcia(ui, repo)
|
|
230 |
if not cia.user:
|
|
231 |
ui.debug(_('cia: no user specified'))
|
|
232 |
return
|
|
233 |
if not cia.project:
|
|
234 |
ui.debug(_('cia: no project specified'))
|
|
235 |
return
|
|
236 |
if hooktype == 'changegroup':
|
|
237 |
start = repo.changelog.rev(n)
|
|
238 |
end = len(repo.changelog)
|
|
239 |
for rev in xrange(start, end):
|
|
240 |
n = repo.changelog.node(rev)
|
|
241 |
ctx = repo.changectx(n)
|
|
242 |
sendmsg(cia, ctx)
|
|
243 |
else:
|
|
244 |
ctx = repo.changectx(n)
|
|
245 |
sendmsg(cia, ctx)
|