hgext/hooklib/changeset_published.py
changeset 44413 4cabeea6d214
child 45268 3c2fae87bd5a
equal deleted inserted replaced
44412:edc8504bc26b 44413:4cabeea6d214
       
     1 # Copyright 2020 Joerg Sonnenberger <joerg@bec.de>
       
     2 #
       
     3 # This software may be used and distributed according to the terms of the
       
     4 # GNU General Public License version 2 or any later version.
       
     5 """changeset_published is a hook to send a mail when an
       
     6 existing draft changeset is moved to the public phase.
       
     7 
       
     8 Correct message threading requires the same messageidseed to be used for both
       
     9 the original notification and the new mail.
       
    10 
       
    11 Usage:
       
    12   [notify]
       
    13   messageidseed = myseed
       
    14 
       
    15   [hooks]
       
    16   txnclose-phase.changeset_published = \
       
    17     python:hgext.hooklib.changeset_published.hook
       
    18 """
       
    19 
       
    20 from __future__ import absolute_import
       
    21 
       
    22 import email.errors as emailerrors
       
    23 import email.utils as emailutils
       
    24 
       
    25 from mercurial.i18n import _
       
    26 from mercurial import (
       
    27     encoding,
       
    28     error,
       
    29     logcmdutil,
       
    30     mail,
       
    31     pycompat,
       
    32     registrar,
       
    33 )
       
    34 from mercurial.utils import dateutil
       
    35 from .. import notify
       
    36 
       
    37 configtable = {}
       
    38 configitem = registrar.configitem(configtable)
       
    39 
       
    40 configitem(
       
    41     b'notify_published', b'domain', default=None,
       
    42 )
       
    43 configitem(
       
    44     b'notify_published', b'messageidseed', default=None,
       
    45 )
       
    46 configitem(
       
    47     b'notify_published',
       
    48     b'template',
       
    49     default=b'''Subject: changeset published
       
    50 
       
    51 This changeset has been published.
       
    52 ''',
       
    53 )
       
    54 
       
    55 
       
    56 def _report_commit(ui, repo, ctx):
       
    57     domain = ui.config(b'notify_published', b'domain') or ui.config(
       
    58         b'notify', b'domain'
       
    59     )
       
    60     messageidseed = ui.config(
       
    61         b'notify_published', b'messageidseed'
       
    62     ) or ui.config(b'notify', b'messageidseed')
       
    63     template = ui.config(b'notify_published', b'template')
       
    64     spec = logcmdutil.templatespec(template, None)
       
    65     templater = logcmdutil.changesettemplater(ui, repo, spec)
       
    66     ui.pushbuffer()
       
    67     n = notify.notifier(ui, repo, b'incoming')
       
    68 
       
    69     subs = set()
       
    70     for sub, spec in n.subs:
       
    71         if spec is None:
       
    72             subs.add(sub)
       
    73             continue
       
    74         revs = repo.revs(b'%r and %d:', spec, ctx.rev())
       
    75         if len(revs):
       
    76             subs.add(sub)
       
    77             continue
       
    78     if len(subs) == 0:
       
    79         ui.debug(
       
    80             b'notify_published: no subscribers to selected repo and revset\n'
       
    81         )
       
    82         return
       
    83 
       
    84     templater.show(
       
    85         ctx,
       
    86         changes=ctx.changeset(),
       
    87         baseurl=ui.config(b'web', b'baseurl'),
       
    88         root=repo.root,
       
    89         webroot=n.root,
       
    90     )
       
    91     data = ui.popbuffer()
       
    92 
       
    93     try:
       
    94         msg = mail.parsebytes(data)
       
    95     except emailerrors.MessageParseError as inst:
       
    96         raise error.Abort(inst)
       
    97 
       
    98     msg['In-reply-to'] = notify.messageid(ctx, domain, messageidseed)
       
    99     msg['Message-Id'] = notify.messageid(
       
   100         ctx, domain, messageidseed + b'-published'
       
   101     )
       
   102     msg['Date'] = encoding.strfromlocal(
       
   103         dateutil.datestr(format=b"%a, %d %b %Y %H:%M:%S %1%2")
       
   104     )
       
   105     if not msg['From']:
       
   106         sender = ui.config(b'email', b'from') or ui.username()
       
   107         if b'@' not in sender or b'@localhost' in sender:
       
   108             sender = n.fixmail(sender)
       
   109         msg['From'] = mail.addressencode(ui, sender, n.charsets, n.test)
       
   110     msg['To'] = ', '.join(sorted(subs))
       
   111 
       
   112     msgtext = msg.as_bytes() if pycompat.ispy3 else msg.as_string()
       
   113     if ui.configbool(b'notify', b'test'):
       
   114         ui.write(msgtext)
       
   115         if not msgtext.endswith(b'\n'):
       
   116             ui.write(b'\n')
       
   117     else:
       
   118         ui.status(_(b'notify_published: sending mail for %d\n') % ctx.rev())
       
   119         mail.sendmail(
       
   120             ui, emailutils.parseaddr(msg['From'])[1], subs, msgtext, mbox=n.mbox
       
   121         )
       
   122 
       
   123 
       
   124 def hook(ui, repo, hooktype, node=None, **kwargs):
       
   125     if hooktype != b"txnclose-phase":
       
   126         raise error.Abort(
       
   127             _(b'Unsupported hook type %r') % pycompat.bytestr(hooktype)
       
   128         )
       
   129     ctx = repo.unfiltered()[node]
       
   130     if kwargs['oldphase'] == b'draft' and kwargs['phase'] == b'public':
       
   131         _report_commit(ui, repo, ctx)