Mercurial > hg
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() |