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