##// END OF EJS Templates
hgcia: Set default value of strip to -1 (issue2891)...
Cédric Krier -
r14850:a95242af stable
parent child Browse files
Show More
@@ -1,272 +1,279 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}{webroot}/rev/{node}-- {diffstat}
21 #template = {desc}\\n{baseurl}{webroot}/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 # number of slashes to strip for url paths
31 # number of slashes to strip for url paths
32 #strip = 0
32 #strip = 0
33
33
34 [hooks]
34 [hooks]
35 # one of these:
35 # one of these:
36 changegroup.cia = python:hgcia.hook
36 changegroup.cia = python:hgcia.hook
37 #incoming.cia = python:hgcia.hook
37 #incoming.cia = python:hgcia.hook
38
38
39 [web]
39 [web]
40 # If you want hyperlinks (optional)
40 # If you want hyperlinks (optional)
41 baseurl = http://server/path/to/repo
41 baseurl = http://server/path/to/repo
42 """
42 """
43
43
44 from mercurial.i18n import _
44 from mercurial.i18n import _
45 from mercurial.node import bin, short
45 from mercurial.node import bin, short
46 from mercurial import cmdutil, patch, templater, util, mail
46 from mercurial import cmdutil, patch, templater, util, mail
47 import email.Parser
47 import email.Parser
48
48
49 import xmlrpclib
49 import xmlrpclib
50 from xml.sax import saxutils
50 from xml.sax import saxutils
51
51
52 socket_timeout = 30 # seconds
52 socket_timeout = 30 # seconds
53 try:
53 try:
54 # set a timeout for the socket so you don't have to wait so looooong
54 # set a timeout for the socket so you don't have to wait so looooong
55 # when cia.vc is having problems. requires python >= 2.3:
55 # when cia.vc is having problems. requires python >= 2.3:
56 import socket
56 import socket
57 socket.setdefaulttimeout(socket_timeout)
57 socket.setdefaulttimeout(socket_timeout)
58 except:
58 except:
59 pass
59 pass
60
60
61 HGCIA_VERSION = '0.1'
61 HGCIA_VERSION = '0.1'
62 HGCIA_URL = 'http://hg.kublai.com/mercurial/hgcia'
62 HGCIA_URL = 'http://hg.kublai.com/mercurial/hgcia'
63
63
64
64
65 class ciamsg(object):
65 class ciamsg(object):
66 """ A CIA message """
66 """ A CIA message """
67 def __init__(self, cia, ctx):
67 def __init__(self, cia, ctx):
68 self.cia = cia
68 self.cia = cia
69 self.ctx = ctx
69 self.ctx = ctx
70 self.url = self.cia.url
70 self.url = self.cia.url
71 if self.url:
71 if self.url:
72 self.url += self.cia.root
72 self.url += self.cia.root
73
73
74 def fileelem(self, path, uri, action):
74 def fileelem(self, path, uri, action):
75 if uri:
75 if uri:
76 uri = ' uri=%s' % saxutils.quoteattr(uri)
76 uri = ' uri=%s' % saxutils.quoteattr(uri)
77 return '<file%s action=%s>%s</file>' % (
77 return '<file%s action=%s>%s</file>' % (
78 uri, saxutils.quoteattr(action), saxutils.escape(path))
78 uri, saxutils.quoteattr(action), saxutils.escape(path))
79
79
80 def fileelems(self):
80 def fileelems(self):
81 n = self.ctx.node()
81 n = self.ctx.node()
82 f = self.cia.repo.status(self.ctx.p1().node(), n)
82 f = self.cia.repo.status(self.ctx.p1().node(), n)
83 url = self.url or ''
83 url = self.url or ''
84 if url and url[-1] == '/':
85 url = url[:-1]
84 elems = []
86 elems = []
85 for path in f[0]:
87 for path in f[0]:
86 uri = '%s/diff/%s/%s' % (url, short(n), path)
88 uri = '%s/diff/%s/%s' % (url, short(n), path)
87 elems.append(self.fileelem(path, url and uri, 'modify'))
89 elems.append(self.fileelem(path, url and uri, 'modify'))
88 for path in f[1]:
90 for path in f[1]:
89 # TODO: copy/rename ?
91 # TODO: copy/rename ?
90 uri = '%s/file/%s/%s' % (url, short(n), path)
92 uri = '%s/file/%s/%s' % (url, short(n), path)
91 elems.append(self.fileelem(path, url and uri, 'add'))
93 elems.append(self.fileelem(path, url and uri, 'add'))
92 for path in f[2]:
94 for path in f[2]:
93 elems.append(self.fileelem(path, '', 'remove'))
95 elems.append(self.fileelem(path, '', 'remove'))
94
96
95 return '\n'.join(elems)
97 return '\n'.join(elems)
96
98
97 def sourceelem(self, project, module=None, branch=None):
99 def sourceelem(self, project, module=None, branch=None):
98 msg = ['<source>', '<project>%s</project>' % saxutils.escape(project)]
100 msg = ['<source>', '<project>%s</project>' % saxutils.escape(project)]
99 if module:
101 if module:
100 msg.append('<module>%s</module>' % saxutils.escape(module))
102 msg.append('<module>%s</module>' % saxutils.escape(module))
101 if branch:
103 if branch:
102 msg.append('<branch>%s</branch>' % saxutils.escape(branch))
104 msg.append('<branch>%s</branch>' % saxutils.escape(branch))
103 msg.append('</source>')
105 msg.append('</source>')
104
106
105 return '\n'.join(msg)
107 return '\n'.join(msg)
106
108
107 def diffstat(self):
109 def diffstat(self):
108 class patchbuf(object):
110 class patchbuf(object):
109 def __init__(self):
111 def __init__(self):
110 self.lines = []
112 self.lines = []
111 # diffstat is stupid
113 # diffstat is stupid
112 self.name = 'cia'
114 self.name = 'cia'
113 def write(self, data):
115 def write(self, data):
114 self.lines.append(data)
116 self.lines.append(data)
115 def close(self):
117 def close(self):
116 pass
118 pass
117
119
118 n = self.ctx.node()
120 n = self.ctx.node()
119 pbuf = patchbuf()
121 pbuf = patchbuf()
120 cmdutil.export(self.cia.repo, [n], fp=pbuf)
122 cmdutil.export(self.cia.repo, [n], fp=pbuf)
121 return patch.diffstat(pbuf.lines) or ''
123 return patch.diffstat(pbuf.lines) or ''
122
124
123 def logmsg(self):
125 def logmsg(self):
124 diffstat = self.cia.diffstat and self.diffstat() or ''
126 diffstat = self.cia.diffstat and self.diffstat() or ''
125 self.cia.ui.pushbuffer()
127 self.cia.ui.pushbuffer()
126 self.cia.templater.show(self.ctx, changes=self.ctx.changeset(),
128 self.cia.templater.show(self.ctx, changes=self.ctx.changeset(),
127 baseurl=self.cia.ui.config('web', 'baseurl'),
129 baseurl=self.cia.ui.config('web', 'baseurl'),
128 url=self.url, diffstat=diffstat,
130 url=self.url, diffstat=diffstat,
129 webroot=self.cia.root)
131 webroot=self.cia.root)
130 return self.cia.ui.popbuffer()
132 return self.cia.ui.popbuffer()
131
133
132 def xml(self):
134 def xml(self):
133 n = short(self.ctx.node())
135 n = short(self.ctx.node())
134 src = self.sourceelem(self.cia.project, module=self.cia.module,
136 src = self.sourceelem(self.cia.project, module=self.cia.module,
135 branch=self.ctx.branch())
137 branch=self.ctx.branch())
136 # unix timestamp
138 # unix timestamp
137 dt = self.ctx.date()
139 dt = self.ctx.date()
138 timestamp = dt[0]
140 timestamp = dt[0]
139
141
140 author = saxutils.escape(self.ctx.user())
142 author = saxutils.escape(self.ctx.user())
141 rev = '%d:%s' % (self.ctx.rev(), n)
143 rev = '%d:%s' % (self.ctx.rev(), n)
142 log = saxutils.escape(self.logmsg())
144 log = saxutils.escape(self.logmsg())
143
145
144 url = self.url and '<url>%s/rev/%s</url>' % (saxutils.escape(self.url),
146 url = self.url
145 n) or ''
147 if url and url[-1] == '/':
148 url = url[:-1]
149 url = url and '<url>%s/rev/%s</url>' % (saxutils.escape(url), n) or ''
146
150
147 msg = """
151 msg = """
148 <message>
152 <message>
149 <generator>
153 <generator>
150 <name>Mercurial (hgcia)</name>
154 <name>Mercurial (hgcia)</name>
151 <version>%s</version>
155 <version>%s</version>
152 <url>%s</url>
156 <url>%s</url>
153 <user>%s</user>
157 <user>%s</user>
154 </generator>
158 </generator>
155 %s
159 %s
156 <body>
160 <body>
157 <commit>
161 <commit>
158 <author>%s</author>
162 <author>%s</author>
159 <version>%s</version>
163 <version>%s</version>
160 <log>%s</log>
164 <log>%s</log>
161 %s
165 %s
162 <files>%s</files>
166 <files>%s</files>
163 </commit>
167 </commit>
164 </body>
168 </body>
165 <timestamp>%d</timestamp>
169 <timestamp>%d</timestamp>
166 </message>
170 </message>
167 """ % \
171 """ % \
168 (HGCIA_VERSION, saxutils.escape(HGCIA_URL),
172 (HGCIA_VERSION, saxutils.escape(HGCIA_URL),
169 saxutils.escape(self.cia.user), src, author, rev, log, url,
173 saxutils.escape(self.cia.user), src, author, rev, log, url,
170 self.fileelems(), timestamp)
174 self.fileelems(), timestamp)
171
175
172 return msg
176 return msg
173
177
174
178
175 class hgcia(object):
179 class hgcia(object):
176 """ CIA notification class """
180 """ CIA notification class """
177
181
178 deftemplate = '{desc}'
182 deftemplate = '{desc}'
179 dstemplate = '{desc}\n-- \n{diffstat}'
183 dstemplate = '{desc}\n-- \n{diffstat}'
180
184
181 def __init__(self, ui, repo):
185 def __init__(self, ui, repo):
182 self.ui = ui
186 self.ui = ui
183 self.repo = repo
187 self.repo = repo
184
188
185 self.ciaurl = self.ui.config('cia', 'url', 'http://cia.vc')
189 self.ciaurl = self.ui.config('cia', 'url', 'http://cia.vc')
186 self.user = self.ui.config('cia', 'user')
190 self.user = self.ui.config('cia', 'user')
187 self.project = self.ui.config('cia', 'project')
191 self.project = self.ui.config('cia', 'project')
188 self.module = self.ui.config('cia', 'module')
192 self.module = self.ui.config('cia', 'module')
189 self.diffstat = self.ui.configbool('cia', 'diffstat')
193 self.diffstat = self.ui.configbool('cia', 'diffstat')
190 self.emailfrom = self.ui.config('email', 'from')
194 self.emailfrom = self.ui.config('email', 'from')
191 self.dryrun = self.ui.configbool('cia', 'test')
195 self.dryrun = self.ui.configbool('cia', 'test')
192 self.url = self.ui.config('web', 'baseurl')
196 self.url = self.ui.config('web', 'baseurl')
193 self.stripcount = int(self.ui.config('cia', 'strip', 0))
197 # Default to -1 for backward compatibility
198 self.stripcount = int(self.ui.config('cia', 'strip', -1))
194 self.root = self.strip(self.repo.root)
199 self.root = self.strip(self.repo.root)
195
200
196 style = self.ui.config('cia', 'style')
201 style = self.ui.config('cia', 'style')
197 template = self.ui.config('cia', 'template')
202 template = self.ui.config('cia', 'template')
198 if not template:
203 if not template:
199 template = self.diffstat and self.dstemplate or self.deftemplate
204 template = self.diffstat and self.dstemplate or self.deftemplate
200 template = templater.parsestring(template, quoted=False)
205 template = templater.parsestring(template, quoted=False)
201 t = cmdutil.changeset_templater(self.ui, self.repo, False, None,
206 t = cmdutil.changeset_templater(self.ui, self.repo, False, None,
202 style, False)
207 style, False)
203 t.use_template(template)
208 t.use_template(template)
204 self.templater = t
209 self.templater = t
205
210
206 def strip(self, path):
211 def strip(self, path):
207 '''strip leading slashes from local path, turn into web-safe path.'''
212 '''strip leading slashes from local path, turn into web-safe path.'''
208
213
209 path = util.pconvert(path)
214 path = util.pconvert(path)
210 count = self.stripcount
215 count = self.stripcount
216 if count < 0:
217 return ''
211 while count > 0:
218 while count > 0:
212 c = path.find('/')
219 c = path.find('/')
213 if c == -1:
220 if c == -1:
214 break
221 break
215 path = path[c + 1:]
222 path = path[c + 1:]
216 count -= 1
223 count -= 1
217 return path
224 return path
218
225
219 def sendrpc(self, msg):
226 def sendrpc(self, msg):
220 srv = xmlrpclib.Server(self.ciaurl)
227 srv = xmlrpclib.Server(self.ciaurl)
221 res = srv.hub.deliver(msg)
228 res = srv.hub.deliver(msg)
222 if res is not True and res != 'queued.':
229 if res is not True and res != 'queued.':
223 raise util.Abort(_('%s returned an error: %s') %
230 raise util.Abort(_('%s returned an error: %s') %
224 (self.ciaurl, res))
231 (self.ciaurl, res))
225
232
226 def sendemail(self, address, data):
233 def sendemail(self, address, data):
227 p = email.Parser.Parser()
234 p = email.Parser.Parser()
228 msg = p.parsestr(data)
235 msg = p.parsestr(data)
229 msg['Date'] = util.datestr(format="%a, %d %b %Y %H:%M:%S %1%2")
236 msg['Date'] = util.datestr(format="%a, %d %b %Y %H:%M:%S %1%2")
230 msg['To'] = address
237 msg['To'] = address
231 msg['From'] = self.emailfrom
238 msg['From'] = self.emailfrom
232 msg['Subject'] = 'DeliverXML'
239 msg['Subject'] = 'DeliverXML'
233 msg['Content-type'] = 'text/xml'
240 msg['Content-type'] = 'text/xml'
234 msgtext = msg.as_string()
241 msgtext = msg.as_string()
235
242
236 self.ui.status(_('hgcia: sending update to %s\n') % address)
243 self.ui.status(_('hgcia: sending update to %s\n') % address)
237 mail.sendmail(self.ui, util.email(self.emailfrom),
244 mail.sendmail(self.ui, util.email(self.emailfrom),
238 [address], msgtext)
245 [address], msgtext)
239
246
240
247
241 def hook(ui, repo, hooktype, node=None, url=None, **kwargs):
248 def hook(ui, repo, hooktype, node=None, url=None, **kwargs):
242 """ send CIA notification """
249 """ send CIA notification """
243 def sendmsg(cia, ctx):
250 def sendmsg(cia, ctx):
244 msg = ciamsg(cia, ctx).xml()
251 msg = ciamsg(cia, ctx).xml()
245 if cia.dryrun:
252 if cia.dryrun:
246 ui.write(msg)
253 ui.write(msg)
247 elif cia.ciaurl.startswith('mailto:'):
254 elif cia.ciaurl.startswith('mailto:'):
248 if not cia.emailfrom:
255 if not cia.emailfrom:
249 raise util.Abort(_('email.from must be defined when '
256 raise util.Abort(_('email.from must be defined when '
250 'sending by email'))
257 'sending by email'))
251 cia.sendemail(cia.ciaurl[7:], msg)
258 cia.sendemail(cia.ciaurl[7:], msg)
252 else:
259 else:
253 cia.sendrpc(msg)
260 cia.sendrpc(msg)
254
261
255 n = bin(node)
262 n = bin(node)
256 cia = hgcia(ui, repo)
263 cia = hgcia(ui, repo)
257 if not cia.user:
264 if not cia.user:
258 ui.debug('cia: no user specified')
265 ui.debug('cia: no user specified')
259 return
266 return
260 if not cia.project:
267 if not cia.project:
261 ui.debug('cia: no project specified')
268 ui.debug('cia: no project specified')
262 return
269 return
263 if hooktype == 'changegroup':
270 if hooktype == 'changegroup':
264 start = repo.changelog.rev(n)
271 start = repo.changelog.rev(n)
265 end = len(repo.changelog)
272 end = len(repo.changelog)
266 for rev in xrange(start, end):
273 for rev in xrange(start, end):
267 n = repo.changelog.node(rev)
274 n = repo.changelog.node(rev)
268 ctx = repo.changectx(n)
275 ctx = repo.changectx(n)
269 sendmsg(cia, ctx)
276 sendmsg(cia, ctx)
270 else:
277 else:
271 ctx = repo.changectx(n)
278 ctx = repo.changectx(n)
272 sendmsg(cia, ctx)
279 sendmsg(cia, ctx)
@@ -1,54 +1,92 b''
1 Test the CIA extension
1 Test the CIA extension
2
2
3 $ cat >> $HGRCPATH <<EOF
3 $ cat >> $HGRCPATH <<EOF
4 > [extensions]
4 > [extensions]
5 > hgcia=
5 > hgcia=
6 >
6 >
7 > [hooks]
7 > [hooks]
8 > changegroup.cia = python:hgext.hgcia.hook
8 > changegroup.cia = python:hgext.hgcia.hook
9 >
9 >
10 > [web]
10 > [web]
11 > baseurl = http://hgserver/
11 > baseurl = http://hgserver/
12 >
12 >
13 > [cia]
13 > [cia]
14 > user = testuser
14 > user = testuser
15 > project = testproject
15 > project = testproject
16 > test = True
16 > test = True
17 > EOF
17 > EOF
18
18
19 $ hg init src
19 $ hg init src
20 $ hg init cia
20 $ hg init cia
21 $ cd src
21 $ cd src
22 $ echo foo > foo
22 $ echo foo > foo
23 $ hg ci -Amfoo
23 $ hg ci -Amfoo
24 adding foo
24 adding foo
25 $ hg push ../cia
25 $ hg push ../cia
26 pushing to ../cia
26 pushing to ../cia
27 searching for changes
27 searching for changes
28 adding changesets
28 adding changesets
29 adding manifests
29 adding manifests
30 adding file changes
30 adding file changes
31 added 1 changesets with 1 changes to 1 files
31 added 1 changesets with 1 changes to 1 files
32
32
33 <message>
33 <message>
34 <generator>
34 <generator>
35 <name>Mercurial (hgcia)</name>
35 <name>Mercurial (hgcia)</name>
36 <version>0.1</version>
36 <version>0.1</version>
37 <url>http://hg.kublai.com/mercurial/hgcia</url>
37 <url>http://hg.kublai.com/mercurial/hgcia</url>
38 <user>testuser</user>
38 <user>testuser</user>
39 </generator>
39 </generator>
40 <source>
40 <source>
41 <project>testproject</project>
41 <project>testproject</project>
42 <branch>default</branch>
42 <branch>default</branch>
43 </source>
43 </source>
44 <body>
44 <body>
45 <commit>
45 <commit>
46 <author>test</author>
46 <author>test</author>
47 <version>0:e63c23eaa88a</version>
47 <version>0:e63c23eaa88a</version>
48 <log>foo</log>
48 <log>foo</log>
49 <url>http://hgserver/$TESTTMP/cia/rev/e63c23eaa88a</url>
49 <url>http://hgserver/rev/e63c23eaa88a</url>
50 <files><file uri="http://hgserver/$TESTTMP/cia/file/e63c23eaa88a/foo" action="add">foo</file></files>
50 <files><file uri="http://hgserver/file/e63c23eaa88a/foo" action="add">foo</file></files>
51 </commit>
51 </commit>
52 </body>
52 </body>
53 <timestamp>0</timestamp>
53 <timestamp>0</timestamp>
54 </message>
54 </message>
55
56 $ cat >> $HGRCPATH <<EOF
57 > strip = 0
58 > EOF
59
60 $ echo bar > bar
61 $ hg ci -Ambar
62 adding bar
63 $ hg push ../cia
64 pushing to ../cia
65 searching for changes
66 adding changesets
67 adding manifests
68 adding file changes
69 added 1 changesets with 1 changes to 1 files
70
71 <message>
72 <generator>
73 <name>Mercurial (hgcia)</name>
74 <version>0.1</version>
75 <url>http://hg.kublai.com/mercurial/hgcia</url>
76 <user>testuser</user>
77 </generator>
78 <source>
79 <project>testproject</project>
80 <branch>default</branch>
81 </source>
82 <body>
83 <commit>
84 <author>test</author>
85 <version>1:c0c7cf58edc5</version>
86 <log>bar</log>
87 <url>http://hgserver/$TESTTMP/cia/rev/c0c7cf58edc5</url>
88 <files><file uri="http://hgserver/$TESTTMP/cia/file/c0c7cf58edc5/bar" action="add">bar</file></files>
89 </commit>
90 </body>
91 <timestamp>0</timestamp>
92 </message>
General Comments 0
You need to be logged in to leave comments. Login now