##// END OF EJS Templates
encoding: add an encoding-aware lower function
encoding: add an encoding-aware lower function

File last commit:

r13878:a8d13ee0 default
r14069:e38846a7 default
Show More
notify.py
316 lines | 11.3 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
Dirkjan Ochtman
extensions: change descriptions for hook-providing extensions...
r8935 '''hooks for sending email notifications at commit/push time
Dirkjan Ochtman
help: better documentation intro for a few extensions
r7127
Martin Geisler
notify: wrap docstrings at 70 characters
r9266 Subscriptions can be managed through a hgrc file. Default mode is to
print messages to stdout, for testing and configuring.
Dirkjan Ochtman
help: better documentation intro for a few extensions
r7127
Martin Geisler
notify: wrap docstrings at 70 characters
r9266 To use, configure the notify extension and enable it in hgrc like
this::
Dirkjan Ochtman
help: better documentation intro for a few extensions
r7127
Martin Geisler
notify: fix indentation in module docstring
r9105 [extensions]
Martin Geisler
hgext: enable extensions without "hgext." prefix in help texts
r10112 notify =
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
# batch emails when many changesets incoming at one time
changegroup.notify = python:hgext.notify.hook
Dirkjan Ochtman
help: better documentation intro for a few extensions
r7127
Martin Geisler
notify: fix indentation in module docstring
r9105 [notify]
# config items go here
Dirkjan Ochtman
help: better documentation intro for a few extensions
r7127
Martin Geisler
commands: use minirst parser when displaying help
r9157 Required configuration items::
Dirkjan Ochtman
help: better documentation intro for a few extensions
r7127
Martin Geisler
notify: fix indentation in module docstring
r9105 config = /path/to/file # file containing subscriptions
Dirkjan Ochtman
help: better documentation intro for a few extensions
r7127
Martin Geisler
commands: use minirst parser when displaying help
r9157 Optional configuration items::
Martin Geisler
notify: cleanup module docstring
r9104
Martin Geisler
notify: fix indentation in module docstring
r9105 test = True # print messages to stdout for testing
strip = 3 # number of slashes to strip for url paths
domain = example.com # domain to use if committer missing domain
style = ... # style file to use when formatting email
template = ... # template to use when formatting email
incoming = ... # template to use when run as incoming hook
changegroup = ... # template when run as changegroup hook
maxdiff = 300 # max lines of diffs to include (0=none, -1=all)
maxsubject = 67 # truncate subject line longer than this
diffstat = True # add a diffstat before the diff content
sources = serve # notify if source of incoming changes in this list
# (serve == ssh or http, push, pull, bundle)
David Champion
notify: permit suppression of merge changeset notification...
r9516 merge = False # send notification for merges (default True)
Martin Geisler
notify: fix indentation in module docstring
r9105 [email]
from = user@host.com # email address to send as if none given
[web]
baseurl = http://hgserver/... # root of hg web site for browsing commits
Dirkjan Ochtman
help: better documentation intro for a few extensions
r7127
Martin Geisler
notify: wrap docstrings at 70 characters
r9266 The notify config file has same format as a regular hgrc file. It has
two sections so you can express subscriptions in whatever way is
handier for you.
Dirkjan Ochtman
help: better documentation intro for a few extensions
r7127
Martin Geisler
commands: use minirst parser when displaying help
r9157 ::
Martin Geisler
notify: fix indentation in module docstring
r9105 [usersubs]
# key is subscriber email, value is ","-separated list of glob patterns
user@host = pattern
Dirkjan Ochtman
help: better documentation intro for a few extensions
r7127
Martin Geisler
notify: fix indentation in module docstring
r9105 [reposubs]
# key is glob pattern, value is ","-separated list of subscriber emails
pattern = user@host
Dirkjan Ochtman
help: better documentation intro for a few extensions
r7127
Martin Geisler
notify: fix indentation in module docstring
r9105 Glob patterns are matched against path to repository root.
Dirkjan Ochtman
help: better documentation intro for a few extensions
r7127
Martin Geisler
notify: wrap docstrings at 70 characters
r9266 If you like, you can put notify config file in repository that users
can push changes to, they can manage their own subscriptions.
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
def url(self, path=None):
return self.ui.config('web', 'baseurl') + (path or self.root)
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
Vadim Gelfer
add email notification hook. hook written in python....
r2203 if hooktype == 'changegroup':
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)