comparison hgext/notify.py @ 17754:19e9bf7c0927

notify: support revset selection for subscriptions A repo pattern for any notify configuration contains a glob matching the path to the repo. Additionally, it may now contain a revset spec, separated from the glob by '#'. Example: [reposubs] */widgets#branch(release) = qa-team@example.com This sends to ``qa-team@example.com`` whenever a changeset on the ``release`` branch triggers a notification in any repository ending in ``widgets``. This patch was completely done by David Champion <dgc@uchicago.edu> with me making tiny changes to his tests.
author Michal Sznajder <michalsznajder@gmail.com>
date Mon, 13 Aug 2012 22:42:10 +0200
parents bdf8c6c61c9b
children efee1f35fcae
comparison
equal deleted inserted replaced
17753:69d5078d760d 17754:19e9bf7c0927
28 be assigned to repositories. The ``[usersubs]`` section maps multiple 28 be assigned to repositories. The ``[usersubs]`` section maps multiple
29 repositories to a given recipient. The ``[reposubs]`` section maps 29 repositories to a given recipient. The ``[reposubs]`` section maps
30 multiple recipients to a single repository:: 30 multiple recipients to a single repository::
31 31
32 [usersubs] 32 [usersubs]
33 # key is subscriber email, value is a comma-separated list of repo glob 33 # key is subscriber email, value is a comma-separated list of repo patterns
34 # patterns
35 user@host = pattern 34 user@host = pattern
36 35
37 [reposubs] 36 [reposubs]
38 # key is glob pattern, value is a comma-separated list of subscriber 37 # key is repo pattern, value is a comma-separated list of subscriber emails
39 # emails
40 pattern = user@host 38 pattern = user@host
41 39
42 Glob patterns are matched against absolute path to repository 40 A ``pattern`` is a ``glob`` matching the absolute path to a repository,
43 root. 41 optionally combined with a revset expression. A revset expression, if
42 present, is separated from the glob by a hash. Example::
43
44 [reposubs]
45 */widgets#branch(release) = qa-team@example.com
46
47 This sends to ``qa-team@example.com`` whenever a changeset on the ``release``
48 branch triggers a notification in any repository ending in ``widgets``.
44 49
45 In order to place them under direct user management, ``[usersubs]`` and 50 In order to place them under direct user management, ``[usersubs]`` and
46 ``[reposubs]`` sections may be placed in a separate ``hgrc`` file and 51 ``[reposubs]`` sections may be placed in a separate ``hgrc`` file and
47 incorporated by reference:: 52 incorporated by reference::
48 53
215 def subscribers(self): 220 def subscribers(self):
216 '''return list of email addresses of subscribers to this repo.''' 221 '''return list of email addresses of subscribers to this repo.'''
217 subs = set() 222 subs = set()
218 for user, pats in self.ui.configitems('usersubs'): 223 for user, pats in self.ui.configitems('usersubs'):
219 for pat in pats.split(','): 224 for pat in pats.split(','):
225 if '#' in pat:
226 pat, revs = pat.split('#', 1)
227 else:
228 revs = None
220 if fnmatch.fnmatch(self.repo.root, pat.strip()): 229 if fnmatch.fnmatch(self.repo.root, pat.strip()):
221 subs.add(self.fixmail(user)) 230 subs.add((self.fixmail(user), revs))
222 for pat, users in self.ui.configitems('reposubs'): 231 for pat, users in self.ui.configitems('reposubs'):
232 if '#' in pat:
233 pat, revs = pat.split('#', 1)
234 else:
235 revs = None
223 if fnmatch.fnmatch(self.repo.root, pat): 236 if fnmatch.fnmatch(self.repo.root, pat):
224 for user in users.split(','): 237 for user in users.split(','):
225 subs.add(self.fixmail(user)) 238 subs.add((self.fixmail(user), revs))
226 return [mail.addressencode(self.ui, s, self.charsets, self.test) 239 return [(mail.addressencode(self.ui, s, self.charsets, self.test), r)
227 for s in sorted(subs)] 240 for s, r in sorted(subs)]
228 241
229 def node(self, ctx, **props): 242 def node(self, ctx, **props):
230 '''format one changeset, unless it is a suppressed merge.''' 243 '''format one changeset, unless it is a suppressed merge.'''
231 if not self.merge and len(ctx.parents()) > 1: 244 if not self.merge and len(ctx.parents()) > 1:
232 return False 245 return False
240 ok_sources = self.ui.config('notify', 'sources', 'serve').split() 253 ok_sources = self.ui.config('notify', 'sources', 'serve').split()
241 return source not in ok_sources 254 return source not in ok_sources
242 255
243 def send(self, ctx, count, data): 256 def send(self, ctx, count, data):
244 '''send message.''' 257 '''send message.'''
258
259 # Select subscribers by revset
260 subs = set()
261 for sub, spec in self.subs:
262 if spec is None:
263 subs.add(sub)
264 continue
265 revs = self.repo.revs('%r and %d:', spec, ctx.rev())
266 if len(revs):
267 subs.add(sub)
268 continue
269 if len(subs) == 0:
270 self.ui.debug('notify: no subscribers to selected repo '
271 'and revset\n')
272 return
245 273
246 p = email.Parser.Parser() 274 p = email.Parser.Parser()
247 try: 275 try:
248 msg = p.parsestr(data) 276 msg = p.parsestr(data)
249 except email.Errors.MessageParseError, inst: 277 except email.Errors.MessageParseError, inst:
290 msg['X-Hg-Notification'] = 'changeset %s' % ctx 318 msg['X-Hg-Notification'] = 'changeset %s' % ctx
291 if not msg['Message-Id']: 319 if not msg['Message-Id']:
292 msg['Message-Id'] = ('<hg.%s.%s.%s@%s>' % 320 msg['Message-Id'] = ('<hg.%s.%s.%s@%s>' %
293 (ctx, int(time.time()), 321 (ctx, int(time.time()),
294 hash(self.repo.root), socket.getfqdn())) 322 hash(self.repo.root), socket.getfqdn()))
295 msg['To'] = ', '.join(self.subs) 323 msg['To'] = ', '.join(sorted(subs))
296 324
297 msgtext = msg.as_string() 325 msgtext = msg.as_string()
298 if self.test: 326 if self.test:
299 self.ui.write(msgtext) 327 self.ui.write(msgtext)
300 if not msgtext.endswith('\n'): 328 if not msgtext.endswith('\n'):
301 self.ui.write('\n') 329 self.ui.write('\n')
302 else: 330 else:
303 self.ui.status(_('notify: sending %d subscribers %d changes\n') % 331 self.ui.status(_('notify: sending %d subscribers %d changes\n') %
304 (len(self.subs), count)) 332 (len(subs), count))
305 mail.sendmail(self.ui, util.email(msg['From']), 333 mail.sendmail(self.ui, util.email(msg['From']),
306 self.subs, msgtext, mbox=self.mbox) 334 subs, msgtext, mbox=self.mbox)
307 335
308 def diff(self, ctx, ref=None): 336 def diff(self, ctx, ref=None):
309 337
310 maxdiff = int(self.ui.config('notify', 'maxdiff', 300)) 338 maxdiff = int(self.ui.config('notify', 'maxdiff', 300))
311 prev = ctx.p1().node() 339 prev = ctx.p1().node()