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)
|