changeset_obsoleted.py
143 lines
| 3.9 KiB
| text/x-python
|
PythonLexer
Joerg Sonnenberger
|
r44897 | # Copyright 2020 Joerg Sonnenberger <joerg@bec.de> | ||
# | ||||
# This software may be used and distributed according to the terms of the | ||||
# GNU General Public License version 2 or any later version. | ||||
"""changeset_obsoleted is a hook to send a mail when an | ||||
existing draft changeset is obsoleted by an obsmarker without successor. | ||||
Correct message threading requires the same messageidseed to be used for both | ||||
the original notification and the new mail. | ||||
Usage: | ||||
[notify] | ||||
messageidseed = myseed | ||||
[hooks] | ||||
Aay Jay Chan
|
r45951 | txnclose.changeset_obsoleted = \ | ||
Joerg Sonnenberger
|
r44897 | python:hgext.hooklib.changeset_obsoleted.hook | ||
""" | ||||
import email.errors as emailerrors | ||||
import email.utils as emailutils | ||||
from mercurial.i18n import _ | ||||
from mercurial import ( | ||||
encoding, | ||||
error, | ||||
Martin von Zweigbergk
|
r45828 | formatter, | ||
Joerg Sonnenberger
|
r44897 | logcmdutil, | ||
mail, | ||||
obsutil, | ||||
pycompat, | ||||
registrar, | ||||
) | ||||
from mercurial.utils import dateutil | ||||
from .. import notify | ||||
configtable = {} | ||||
configitem = registrar.configitem(configtable) | ||||
configitem( | ||||
Augie Fackler
|
r46554 | b'notify_obsoleted', | ||
b'domain', | ||||
default=None, | ||||
Joerg Sonnenberger
|
r44897 | ) | ||
configitem( | ||||
Augie Fackler
|
r46554 | b'notify_obsoleted', | ||
b'messageidseed', | ||||
default=None, | ||||
Joerg Sonnenberger
|
r44897 | ) | ||
configitem( | ||||
b'notify_obsoleted', | ||||
b'template', | ||||
default=b'''Subject: changeset abandoned | ||||
This changeset has been abandoned. | ||||
''', | ||||
) | ||||
def _report_commit(ui, repo, ctx): | ||||
domain = ui.config(b'notify_obsoleted', b'domain') or ui.config( | ||||
b'notify', b'domain' | ||||
) | ||||
messageidseed = ui.config( | ||||
b'notify_obsoleted', b'messageidseed' | ||||
) or ui.config(b'notify', b'messageidseed') | ||||
template = ui.config(b'notify_obsoleted', b'template') | ||||
Martin von Zweigbergk
|
r45828 | spec = formatter.literal_templatespec(template) | ||
Joerg Sonnenberger
|
r44897 | templater = logcmdutil.changesettemplater(ui, repo, spec) | ||
ui.pushbuffer() | ||||
n = notify.notifier(ui, repo, b'incoming') | ||||
subs = set() | ||||
for sub, spec in n.subs: | ||||
if spec is None: | ||||
subs.add(sub) | ||||
continue | ||||
revs = repo.revs(b'%r and %d:', spec, ctx.rev()) | ||||
if len(revs): | ||||
subs.add(sub) | ||||
continue | ||||
if len(subs) == 0: | ||||
ui.debug( | ||||
b'notify_obsoleted: no subscribers to selected repo and revset\n' | ||||
) | ||||
return | ||||
templater.show( | ||||
ctx, | ||||
changes=ctx.changeset(), | ||||
baseurl=ui.config(b'web', b'baseurl'), | ||||
root=repo.root, | ||||
webroot=n.root, | ||||
) | ||||
data = ui.popbuffer() | ||||
try: | ||||
msg = mail.parsebytes(data) | ||||
except emailerrors.MessageParseError as inst: | ||||
raise error.Abort(inst) | ||||
msg['In-reply-to'] = notify.messageid(ctx, domain, messageidseed) | ||||
msg['Message-Id'] = notify.messageid( | ||||
ctx, domain, messageidseed + b'-obsoleted' | ||||
) | ||||
msg['Date'] = encoding.strfromlocal( | ||||
dateutil.datestr(format=b"%a, %d %b %Y %H:%M:%S %1%2") | ||||
) | ||||
if not msg['From']: | ||||
sender = ui.config(b'email', b'from') or ui.username() | ||||
if b'@' not in sender or b'@localhost' in sender: | ||||
sender = n.fixmail(sender) | ||||
msg['From'] = mail.addressencode(ui, sender, n.charsets, n.test) | ||||
msg['To'] = ', '.join(sorted(subs)) | ||||
msgtext = msg.as_bytes() if pycompat.ispy3 else msg.as_string() | ||||
if ui.configbool(b'notify', b'test'): | ||||
ui.write(msgtext) | ||||
if not msgtext.endswith(b'\n'): | ||||
ui.write(b'\n') | ||||
else: | ||||
ui.status(_(b'notify_obsoleted: sending mail for %d\n') % ctx.rev()) | ||||
mail.sendmail( | ||||
ui, emailutils.parseaddr(msg['From'])[1], subs, msgtext, mbox=n.mbox | ||||
) | ||||
Joerg Sonnenberger
|
r45635 | def has_successor(repo, rev): | ||
return any( | ||||
r for r in obsutil.allsuccessors(repo.obsstore, [rev]) if r != rev | ||||
) | ||||
Joerg Sonnenberger
|
r44897 | def hook(ui, repo, hooktype, node=None, **kwargs): | ||
Joerg Sonnenberger
|
r45635 | if hooktype != b"txnclose": | ||
Joerg Sonnenberger
|
r44897 | raise error.Abort( | ||
_(b'Unsupported hook type %r') % pycompat.bytestr(hooktype) | ||||
) | ||||
Joerg Sonnenberger
|
r45635 | for rev in obsutil.getobsoleted(repo, changes=kwargs['changes']): | ||
ctx = repo.unfiltered()[rev] | ||||
if not has_successor(repo, ctx.node()): | ||||
_report_commit(ui, repo, ctx) | ||||