##// END OF EJS Templates
rebase: allow revsets for source and base args
rebase: allow revsets for source and base args

File last commit:

r14940:d78b9235 default
r15269:b12362ab default
Show More
notify.py
360 lines | 12.6 KiB | text/x-python | PythonLexer
Vadim Gelfer
add email notification hook. hook written in python....
r2203 # notify.py - email notifications for mercurial
#
# Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
#
Martin Geisler
updated license to be explicit about GPL version 2
r8225 # This software may be used and distributed according to the terms of the
Matt Mackall
Update license to GPLv2+
r10263 # GNU General Public License version 2 or any later version.
Dirkjan Ochtman
help: better documentation intro for a few extensions
r7127
Patrick Mezard
notify: rewrite user documentation...
r14940 '''hooks for sending email push notifications
Dirkjan Ochtman
help: better documentation intro for a few extensions
r7127
Patrick Mezard
notify: rewrite user documentation...
r14940 This extension let you run hooks sending email notifications when
changesets are being pushed, from the sending or receiving side.
Dirkjan Ochtman
help: better documentation intro for a few extensions
r7127
Patrick Mezard
notify: rewrite user documentation...
r14940 First, enable the extension as explained in :hg:`help extensions`, and
register the hook you want to run. ``incoming`` and ``outgoing`` hooks
are run by the changesets receiver while the ``outgoing`` one is for
the sender::
Dirkjan Ochtman
help: better documentation intro for a few extensions
r7127
Martin Geisler
notify: fix indentation in module docstring
r9105 [hooks]
# one email for each incoming changeset
incoming.notify = python:hgext.notify.hook
Patrick Mezard
notify: rewrite user documentation...
r14940 # one email for all incoming changesets
Martin Geisler
notify: fix indentation in module docstring
r9105 changegroup.notify = python:hgext.notify.hook
Patrick Mezard
notify: rewrite user documentation...
r14940
# one email for all outgoing changesets
Ingo Bressler
notify: send changesets on 'outgoing' hook, updated doc
r14617 outgoing.notify = python:hgext.notify.hook
Dirkjan Ochtman
help: better documentation intro for a few extensions
r7127
Patrick Mezard
notify: rewrite user documentation...
r14940 Now the hooks are running, subscribers must be assigned to
repositories. Use the ``[usersubs]`` section to map repositories to a
given email or the ``[reposubs]`` section to map emails to a single
repository::
Martin Geisler
commands: use minirst parser when displaying help
r9157
Martin Geisler
notify: fix indentation in module docstring
r9105 [usersubs]
Patrick Mezard
notify: rewrite user documentation...
r14940 # key is subscriber email, value is a comma-separated list of glob
# patterns
Martin Geisler
notify: fix indentation in module docstring
r9105 user@host = pattern
Dirkjan Ochtman
help: better documentation intro for a few extensions
r7127
Martin Geisler
notify: fix indentation in module docstring
r9105 [reposubs]
Patrick Mezard
notify: rewrite user documentation...
r14940 # key is glob pattern, value is a comma-separated list of subscriber
# emails
Martin Geisler
notify: fix indentation in module docstring
r9105 pattern = user@host
Dirkjan Ochtman
help: better documentation intro for a few extensions
r7127
Patrick Mezard
notify: rewrite user documentation...
r14940 Glob patterns are matched against absolute path to repository
root. The subscriptions can be defined in their own file and
referenced with::
[notify]
config = /path/to/subscriptionsfile
Alternatively, they can be added to Mercurial configuration files by
setting the previous entry to an empty value.
At this point, notifications should be generated but will not be sent until you
set the ``notify.test`` entry to ``False``.
Notifications content can be tweaked with the following configuration entries:
notify.test
If ``True``, print messages to stdout instead of sending them. Default: True.
notify.sources
Space separated list of change sources. Notifications are sent only
if it includes the incoming or outgoing changes source. Incoming
sources can be ``serve`` for changes coming from http or ssh,
``pull`` for pulled changes, ``unbundle`` for changes added by
:hg:`unbundle` or ``push`` for changes being pushed
locally. Outgoing sources are the same except for ``unbundle`` which
is replaced by ``bundle``. Default: serve.
notify.strip
Number of leading slashes to strip from url paths. By default, notifications
references repositories with their absolute path. ``notify.strip`` let you
turn them into relative paths. For example, ``notify.strip=3`` will change
``/long/path/repository`` into ``repository``. Default: 0.
notify.domain
If subscribers emails or the from email have no domain set, complete them
with this value.
Dirkjan Ochtman
help: better documentation intro for a few extensions
r7127
Patrick Mezard
notify: rewrite user documentation...
r14940 notify.style
Style file to use when formatting emails.
notify.template
Template to use when formatting emails.
notify.incoming
Template to use when run as incoming hook, override ``notify.template``.
notify.outgoing
Template to use when run as outgoing hook, override ``notify.template``.
notify.changegroup
Template to use when running as changegroup hook, override
``notify.template``.
notify.maxdiff
Maximum number of diff lines to include in notification email. Set to 0
to disable the diff, -1 to include all of it. Default: 300.
notify.maxsubject
Maximum number of characters in emails subject line. Default: 67.
notify.diffstat
Set to True to include a diffstat before diff content. Default: True.
notify.merge
If True, send notifications for merge changesets. Default: True.
If set, the following entries will also be used to customize the notifications:
email.from
Email ``From`` address to use if none can be found in generated email content.
web.baseurl
Root repository browsing URL to combine with repository paths when making
references. See also ``notify.strip``.
Martin Geisler
notify: wrapped docstrings at 78 characters
r9068 '''
Vadim Gelfer
add email notification hook. hook written in python....
r2203
Matt Mackall
Simplify i18n imports
r3891 from mercurial.i18n import _
Matt Mackall
Replace demandload with new demandimport
r3877 from mercurial import patch, cmdutil, templater, util, mail
Christian Ebert
notify: do not mime encode multipart templates...
r9313 import email.Parser, email.Errors, fnmatch, socket, time
Vadim Gelfer
add email notification hook. hook written in python....
r2203
# template for single changeset can include email headers.
single_template = '''
Subject: changeset in {webroot}: {desc|firstline|strip}
From: {author}
changeset {node|short} in {root}
details: {baseurl}{webroot}?cmd=changeset;node={node|short}
description:
\t{desc|tabindent|strip}
'''.lstrip()
# template for multiple changesets should not contain email headers,
# because only first set of headers will be used and result will look
# strange.
multiple_template = '''
changeset {node|short} in {root}
details: {baseurl}{webroot}?cmd=changeset;node={node|short}
summary: {desc|firstline}
'''
deftemplates = {
'changegroup': multiple_template,
Thomas Arendsen Hein
Fixed indentation in hgext/notify.py
r4498 }
Bryan O'Sullivan
patch queue: notify.patch
r2201
class notifier(object):
Vadim Gelfer
add email notification hook. hook written in python....
r2203 '''email notification class.'''
def __init__(self, ui, repo, hooktype):
Bryan O'Sullivan
patch queue: notify.patch
r2201 self.ui = ui
Vadim Gelfer
notify: add debug output. do not fail if no config file....
r2329 cfg = self.ui.config('notify', 'config')
if cfg:
Matt Mackall
ui: fold readsections into readconfig...
r8142 self.ui.readconfig(cfg, sections=['usersubs', 'reposubs'])
Bryan O'Sullivan
patch queue: notify.patch
r2201 self.repo = repo
Vadim Gelfer
add email notification hook. hook written in python....
r2203 self.stripcount = int(self.ui.config('notify', 'strip', 0))
Bryan O'Sullivan
patch queue: notify.patch
r2201 self.root = self.strip(self.repo.root)
Vadim Gelfer
add email notification hook. hook written in python....
r2203 self.domain = self.ui.config('notify', 'domain')
Christian Ebert
notify: no charset conversion when testing...
r7498 self.test = self.ui.configbool('notify', 'test', True)
Christian Ebert
notify: mime-encode messages...
r7116 self.charsets = mail._charsets(self.ui)
Vadim Gelfer
add email notification hook. hook written in python....
r2203 self.subs = self.subscribers()
David Champion
notify: permit suppression of merge changeset notification...
r9516 self.merge = self.ui.configbool('notify', 'merge', True)
Vadim Gelfer
add email notification hook. hook written in python....
r2203
mapfile = self.ui.config('notify', 'style')
template = (self.ui.config('notify', hooktype) or
self.ui.config('notify', 'template'))
Matt Mackall
fix notify with new ui buffering
r3739 self.t = cmdutil.changeset_templater(self.ui, self.repo,
Jim Correia
add --git option to commands supporting --patch (log, incoming, history, tip)...
r7762 False, None, mapfile, False)
Vadim Gelfer
add email notification hook. hook written in python....
r2203 if not mapfile and not template:
template = deftemplates.get(hooktype) or single_template
if template:
template = templater.parsestring(template, quoted=False)
self.t.use_template(template)
Bryan O'Sullivan
patch queue: notify.patch
r2201
def strip(self, path):
Vadim Gelfer
add email notification hook. hook written in python....
r2203 '''strip leading slashes from local path, turn into web-safe path.'''
Bryan O'Sullivan
patch queue: notify.patch
r2201 path = util.pconvert(path)
count = self.stripcount
Vadim Gelfer
notify: fix off by one error.
r2326 while count > 0:
Bryan O'Sullivan
patch queue: notify.patch
r2201 c = path.find('/')
if c == -1:
break
Matt Mackall
many, many trivial check-code fixups
r10282 path = path[c + 1:]
Bryan O'Sullivan
patch queue: notify.patch
r2201 count -= 1
return path
Vadim Gelfer
add email notification hook. hook written in python....
r2203 def fixmail(self, addr):
'''try to clean up email addresses.'''
Matt Mackall
templater: move email function to util
r5975 addr = util.email(addr.strip())
Alexis S. L. Carvalho
notify: don't try to fix addresses if notify.domain is not set
r4094 if self.domain:
a = addr.find('@localhost')
if a != -1:
addr = addr[:a]
if '@' not in addr:
return addr + '@' + self.domain
Vadim Gelfer
add email notification hook. hook written in python....
r2203 return addr
Bryan O'Sullivan
patch queue: notify.patch
r2201 def subscribers(self):
Vadim Gelfer
add email notification hook. hook written in python....
r2203 '''return list of email addresses of subscribers to this repo.'''
Martin Geisler
notify: turned a set-like dict into a real set
r8154 subs = set()
Vadim Gelfer
add email notification hook. hook written in python....
r2203 for user, pats in self.ui.configitems('usersubs'):
for pat in pats.split(','):
if fnmatch.fnmatch(self.repo.root, pat.strip()):
Martin Geisler
notify: turned a set-like dict into a real set
r8154 subs.add(self.fixmail(user))
Bryan O'Sullivan
patch queue: notify.patch
r2201 for pat, users in self.ui.configitems('reposubs'):
Vadim Gelfer
add email notification hook. hook written in python....
r2203 if fnmatch.fnmatch(self.repo.root, pat):
for user in users.split(','):
Martin Geisler
notify: turned a set-like dict into a real set
r8154 subs.add(self.fixmail(user))
Christian Ebert
notify: no charset conversion when testing...
r7498 return [mail.addressencode(self.ui, s, self.charsets, self.test)
Martin Geisler
notify: turned a set-like dict into a real set
r8154 for s in sorted(subs)]
Bryan O'Sullivan
patch queue: notify.patch
r2201
Bryan O'Sullivan
notify: make it possible to pass extra info into templates
r9486 def node(self, ctx, **props):
David Champion
notify: permit suppression of merge changeset notification...
r9516 '''format one changeset, unless it is a suppressed merge.'''
if not self.merge and len(ctx.parents()) > 1:
return False
Dirkjan Ochtman
notify: use contexts more pervasively
r7726 self.t.show(ctx, changes=ctx.changeset(),
Vadim Gelfer
add email notification hook. hook written in python....
r2203 baseurl=self.ui.config('web', 'baseurl'),
Bryan O'Sullivan
notify: make it possible to pass extra info into templates
r9486 root=self.repo.root, webroot=self.root, **props)
David Champion
notify: permit suppression of merge changeset notification...
r9516 return True
Vadim Gelfer
add email notification hook. hook written in python....
r2203
Vadim Gelfer
localrepository.addchangegroup: add more source infos to hooks
r2230 def skipsource(self, source):
'''true if incoming changes from this source should be skipped.'''
ok_sources = self.ui.config('notify', 'sources', 'serve').split()
return source not in ok_sources
Dirkjan Ochtman
notify: use contexts more pervasively
r7726 def send(self, ctx, count, data):
Vadim Gelfer
add email notification hook. hook written in python....
r2203 '''send message.'''
p = email.Parser.Parser()
Christian Ebert
notify: do not mime encode multipart templates...
r9313 try:
msg = p.parsestr(data)
except email.Errors.MessageParseError, inst:
raise util.Abort(inst)
Vadim Gelfer
add email notification hook. hook written in python....
r2203
Christian Ebert
notify: mime-encode messages...
r7116 # store sender and subject
sender, subject = msg['From'], msg['Subject']
Christian Ebert
notify: fix neglect of custom headers set via template...
r7658 del msg['From'], msg['Subject']
Christian Ebert
notify: do not mime encode multipart templates...
r9313
if not msg.is_multipart():
# create fresh mime message from scratch
# (multipart templates must take care of this themselves)
headers = msg.items()
payload = msg.get_payload()
# for notification prefer readability over data precision
msg = mail.mimeencode(self.ui, payload, self.charsets, self.test)
# reinstate custom headers
for k, v in headers:
msg[k] = v
Christian Ebert
notify: mime-encode messages...
r7116
Christian Ebert
notify: remove subfunctions that are called only once
r7705 msg['Date'] = util.datestr(format="%a, %d %b %Y %H:%M:%S %1%2")
Vadim Gelfer
add email notification hook. hook written in python....
r2203
Christian Ebert
notify: remove subfunctions that are called only once
r7705 # try to make subject line exist and be useful
if not subject:
if count > 1:
subject = _('%s: %d new changesets') % (self.root, count)
else:
Dirkjan Ochtman
notify: use contexts more pervasively
r7726 s = ctx.description().lstrip().split('\n', 1)[0].rstrip()
Christian Ebert
notify: remove subfunctions that are called only once
r7705 subject = '%s: %s' % (self.root, s)
maxsubject = int(self.ui.config('notify', 'maxsubject', 67))
Yuya Nishihara
notify: use util.ellipsis() to truncate long subject
r13202 if maxsubject:
subject = util.ellipsis(subject, maxsubject)
Christian Ebert
notify: remove subfunctions that are called only once
r7705 msg['Subject'] = mail.headencode(self.ui, subject,
self.charsets, self.test)
Vadim Gelfer
add email notification hook. hook written in python....
r2203
Christian Ebert
notify: remove subfunctions that are called only once
r7705 # try to make message have proper sender
if not sender:
sender = self.ui.config('email', 'from') or self.ui.username()
if '@' not in sender or '@localhost' in sender:
sender = self.fixmail(sender)
msg['From'] = mail.addressencode(self.ui, sender,
self.charsets, self.test)
Vadim Gelfer
add email notification hook. hook written in python....
r2203
Dirkjan Ochtman
notify: use contexts more pervasively
r7726 msg['X-Hg-Notification'] = 'changeset %s' % ctx
Vadim Gelfer
add email notification hook. hook written in python....
r2203 if not msg['Message-Id']:
msg['Message-Id'] = ('<hg.%s.%s.%s@%s>' %
Dirkjan Ochtman
notify: use contexts more pervasively
r7726 (ctx, int(time.time()),
Vadim Gelfer
add email notification hook. hook written in python....
r2203 hash(self.repo.root), socket.getfqdn()))
Vadim Gelfer
localrepository.addchangegroup: add more source infos to hooks
r2230 msg['To'] = ', '.join(self.subs)
Vadim Gelfer
add email notification hook. hook written in python....
r2203
Nicolas Dumazet
for calls expecting bool args, pass bool instead of int...
r9136 msgtext = msg.as_string()
Christian Ebert
notify: no charset conversion when testing...
r7498 if self.test:
Vadim Gelfer
add email notification hook. hook written in python....
r2203 self.ui.write(msgtext)
if not msgtext.endswith('\n'):
self.ui.write('\n')
Bryan O'Sullivan
patch queue: notify.patch
r2201 else:
Vadim Gelfer
notify: add debug output. do not fail if no config file....
r2329 self.ui.status(_('notify: sending %d subscribers %d changes\n') %
Thomas Arendsen Hein
Fixed indentation in hgext/notify.py
r4498 (len(self.subs), count))
Matt Mackall
templater: move email function to util
r5975 mail.sendmail(self.ui, util.email(msg['From']),
Matt Mackall
Move ui.sendmail to mail.connect/sendmail
r2889 self.subs, msgtext)
Bryan O'Sullivan
patch queue: notify.patch
r2201
Dirkjan Ochtman
notify: use contexts more pervasively
r7726 def diff(self, ctx, ref=None):
Vadim Gelfer
add email notification hook. hook written in python....
r2203 maxdiff = int(self.ui.config('notify', 'maxdiff', 300))
Matt Mackall
misc: replace .parents()[0] with p1()
r13878 prev = ctx.p1().node()
Dirkjan Ochtman
notify: use contexts more pervasively
r7726 ref = ref and ref.node() or ctx.node()
Dirkjan Ochtman
patch: turn patch.diff() into a generator...
r7308 chunks = patch.diff(self.repo, prev, ref, opts=patch.diffopts(self.ui))
difflines = ''.join(chunks).splitlines()
divy@chelsio.com
notify: fix diffstat printing...
r6979
Matt Doar
Add support for diffstat in commit emails, and move diffstat from...
r3096 if self.ui.configbool('notify', 'diffstat', True):
s = patch.diffstat(difflines)
Sean Dague
Prevent type exception on concatenation if diffstat returns None....
r4077 # s may be nil, don't include the header if it is
if s:
self.ui.write('\ndiffstat:\n\n%s' % s)
Dirkjan Ochtman
notify: use contexts more pervasively
r7726
Benoît Allard
notify: print diffstat even if maxline == 0
r6305 if maxdiff == 0:
return
Dirkjan Ochtman
notify: use contexts more pervasively
r7726 elif maxdiff > 0 and len(difflines) > maxdiff:
msg = _('\ndiffs (truncated from %d to %d lines):\n\n')
self.ui.write(msg % (len(difflines), maxdiff))
Vadim Gelfer
add email notification hook. hook written in python....
r2203 difflines = difflines[:maxdiff]
elif difflines:
Matt Mackall
fix notify with new ui buffering
r3739 self.ui.write(_('\ndiffs (%d lines):\n\n') % len(difflines))
Dirkjan Ochtman
notify: use contexts more pervasively
r7726
divy@chelsio.com
notify: fix diffstat printing...
r6979 self.ui.write("\n".join(difflines))
Bryan O'Sullivan
patch queue: notify.patch
r2201
Vadim Gelfer
localrepository.addchangegroup: add more source infos to hooks
r2230 def hook(ui, repo, hooktype, node=None, source=None, **kwargs):
Vadim Gelfer
add email notification hook. hook written in python....
r2203 '''send email notifications to interested subscribers.
if used as changegroup hook, send one email for all changesets in
changegroup. else send one email per changeset.'''
Dirkjan Ochtman
notify: use contexts more pervasively
r7726
Vadim Gelfer
add email notification hook. hook written in python....
r2203 n = notifier(ui, repo, hooktype)
Dirkjan Ochtman
notify: use contexts more pervasively
r7726 ctx = repo[node]
Vadim Gelfer
notify: add debug output. do not fail if no config file....
r2329 if not n.subs:
Martin Geisler
do not attempt to translate ui.debug output
r9467 ui.debug('notify: no subscribers to repository %s\n' % n.root)
Vadim Gelfer
notify: add debug output. do not fail if no config file....
r2329 return
if n.skipsource(source):
Martin Geisler
do not attempt to translate ui.debug output
r9467 ui.debug('notify: changes have source "%s" - skipping\n' % source)
Vadim Gelfer
localrepository.addchangegroup: add more source infos to hooks
r2230 return
Dirkjan Ochtman
notify: use contexts more pervasively
r7726
Matt Mackall
fix notify with new ui buffering
r3739 ui.pushbuffer()
David Champion
notify: permit suppression of merge changeset notification...
r9516 data = ''
count = 0
Ingo Bressler
notify: send changesets on 'outgoing' hook, updated doc
r14617 if hooktype == 'changegroup' or hooktype == 'outgoing':
Dirkjan Ochtman
notify: use contexts more pervasively
r7726 start, end = ctx.rev(), len(repo)
Vadim Gelfer
add email notification hook. hook written in python....
r2203 for rev in xrange(start, end):
David Champion
notify: permit suppression of merge changeset notification...
r9516 if n.node(repo[rev]):
count += 1
else:
data += ui.popbuffer()
ui.note(_('notify: suppressing notification for merge %d:%s\n') %
(rev, repo[rev].hex()[:12]))
ui.pushbuffer()
if count:
n.diff(ctx, repo['tip'])
Vadim Gelfer
add email notification hook. hook written in python....
r2203 else:
David Champion
notify: permit suppression of merge changeset notification...
r9516 if not n.node(ctx):
ui.popbuffer()
ui.note(_('notify: suppressing notification for merge %d:%s\n') %
(ctx.rev(), ctx.hex()[:12]))
return
count += 1
Dirkjan Ochtman
notify: use contexts more pervasively
r7726 n.diff(ctx)
David Champion
notify: permit suppression of merge changeset notification...
r9516 data += ui.popbuffer()
if count:
n.send(ctx, count, data)