##// END OF EJS Templates
fancyopts: add copyright and license header
fancyopts: add copyright and license header

File last commit:

r8225:46293a0c default
r8230:ec98f35e default
Show More
notify.py
289 lines | 10.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
# GNU General Public License version 2, incorporated herein by reference.
Dirkjan Ochtman
help: better documentation intro for a few extensions
r7127
'''hook extension to email notifications on commits/pushes
Subscriptions can be managed through hgrc. Default mode is to print
messages to stdout, for testing and configuring.
To use, configure notify extension and enable in hgrc like this:
[extensions]
hgext.notify =
[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
[notify]
# config items go in here
config items:
REQUIRED:
config = /path/to/file # file containing subscriptions
OPTIONAL:
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)
[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
notify config file has same format as regular hgrc. it has two
sections so you can express subscriptions in whatever way is handier
for you.
[usersubs]
# key is subscriber email, value is ","-separated list of glob patterns
user@host = pattern
[reposubs]
# key is glob pattern, value is ","-separated list of subscriber emails
pattern = user@host
Martin Geisler
expand "repo" to "repository" in help texts
r8027 glob patterns are matched against path to repository root.
Dirkjan Ochtman
help: better documentation intro for a few extensions
r7127
Martin Geisler
notify, commands: word-wrap help strings
r8029 if you like, you can put notify config file in repository that users
can push changes to, they can manage their own subscriptions.'''
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
import email.Parser, 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()
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
path = path[c+1:]
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)
Dirkjan Ochtman
notify: use contexts more pervasively
r7726 def node(self, ctx):
Vadim Gelfer
add email notification hook. hook written in python....
r2203 '''format one changeset.'''
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'),
Dirkjan Ochtman
notify: use contexts more pervasively
r7726 root=self.repo.root, webroot=self.root)
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()
Matt Mackall
fix notify with new ui buffering
r3739 msg = p.parsestr(data)
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']
# store remaining headers
headers = msg.items()
Christian Ebert
notify: mime-encode messages...
r7116 # create fresh mime message from msg body
text = msg.get_payload()
# for notification prefer readability over data precision
Christian Ebert
notify: no charset conversion when testing...
r7498 msg = mail.mimeencode(self.ui, text, self.charsets, self.test)
Christian Ebert
notify: fix neglect of custom headers set via template...
r7658 # 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))
if maxsubject and len(subject) > maxsubject:
subject = subject[:maxsubject-3] + '...'
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
msgtext = msg.as_string(0)
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))
Dirkjan Ochtman
notify: use contexts more pervasively
r7726 prev = ctx.parents()[0].node()
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
expand "repo" to "repository" in help texts
r8027 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):
Dirkjan Ochtman
notify: use contexts more pervasively
r7726 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()
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 count = end - start
for rev in xrange(start, end):
Dirkjan Ochtman
notify: use contexts more pervasively
r7726 n.node(repo[rev])
n.diff(ctx, repo['tip'])
Vadim Gelfer
add email notification hook. hook written in python....
r2203 else:
count = 1
Dirkjan Ochtman
notify: use contexts more pervasively
r7726 n.node(ctx)
n.diff(ctx)
Matt Mackall
fix notify with new ui buffering
r3739 data = ui.popbuffer()
Dirkjan Ochtman
notify: use contexts more pervasively
r7726 n.send(ctx, count, data)