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