|
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_obsoleted is a hook to send a mail when an |
|
6 existing draft changeset is obsoleted by an obsmarker without successor. |
|
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 pretxnclose.changeset_obsoleted = \ |
|
17 python:hgext.hooklib.changeset_obsoleted.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 obsutil, |
|
32 pycompat, |
|
33 registrar, |
|
34 ) |
|
35 from mercurial.utils import dateutil |
|
36 from .. import notify |
|
37 |
|
38 configtable = {} |
|
39 configitem = registrar.configitem(configtable) |
|
40 |
|
41 configitem( |
|
42 b'notify_obsoleted', b'domain', default=None, |
|
43 ) |
|
44 configitem( |
|
45 b'notify_obsoleted', b'messageidseed', default=None, |
|
46 ) |
|
47 configitem( |
|
48 b'notify_obsoleted', |
|
49 b'template', |
|
50 default=b'''Subject: changeset abandoned |
|
51 |
|
52 This changeset has been abandoned. |
|
53 ''', |
|
54 ) |
|
55 |
|
56 |
|
57 def _report_commit(ui, repo, ctx): |
|
58 domain = ui.config(b'notify_obsoleted', b'domain') or ui.config( |
|
59 b'notify', b'domain' |
|
60 ) |
|
61 messageidseed = ui.config( |
|
62 b'notify_obsoleted', b'messageidseed' |
|
63 ) or ui.config(b'notify', b'messageidseed') |
|
64 template = ui.config(b'notify_obsoleted', b'template') |
|
65 spec = logcmdutil.templatespec(template, None) |
|
66 templater = logcmdutil.changesettemplater(ui, repo, spec) |
|
67 ui.pushbuffer() |
|
68 n = notify.notifier(ui, repo, b'incoming') |
|
69 |
|
70 subs = set() |
|
71 for sub, spec in n.subs: |
|
72 if spec is None: |
|
73 subs.add(sub) |
|
74 continue |
|
75 revs = repo.revs(b'%r and %d:', spec, ctx.rev()) |
|
76 if len(revs): |
|
77 subs.add(sub) |
|
78 continue |
|
79 if len(subs) == 0: |
|
80 ui.debug( |
|
81 b'notify_obsoleted: no subscribers to selected repo and revset\n' |
|
82 ) |
|
83 return |
|
84 |
|
85 templater.show( |
|
86 ctx, |
|
87 changes=ctx.changeset(), |
|
88 baseurl=ui.config(b'web', b'baseurl'), |
|
89 root=repo.root, |
|
90 webroot=n.root, |
|
91 ) |
|
92 data = ui.popbuffer() |
|
93 |
|
94 try: |
|
95 msg = mail.parsebytes(data) |
|
96 except emailerrors.MessageParseError as inst: |
|
97 raise error.Abort(inst) |
|
98 |
|
99 msg['In-reply-to'] = notify.messageid(ctx, domain, messageidseed) |
|
100 msg['Message-Id'] = notify.messageid( |
|
101 ctx, domain, messageidseed + b'-obsoleted' |
|
102 ) |
|
103 msg['Date'] = encoding.strfromlocal( |
|
104 dateutil.datestr(format=b"%a, %d %b %Y %H:%M:%S %1%2") |
|
105 ) |
|
106 if not msg['From']: |
|
107 sender = ui.config(b'email', b'from') or ui.username() |
|
108 if b'@' not in sender or b'@localhost' in sender: |
|
109 sender = n.fixmail(sender) |
|
110 msg['From'] = mail.addressencode(ui, sender, n.charsets, n.test) |
|
111 msg['To'] = ', '.join(sorted(subs)) |
|
112 |
|
113 msgtext = msg.as_bytes() if pycompat.ispy3 else msg.as_string() |
|
114 if ui.configbool(b'notify', b'test'): |
|
115 ui.write(msgtext) |
|
116 if not msgtext.endswith(b'\n'): |
|
117 ui.write(b'\n') |
|
118 else: |
|
119 ui.status(_(b'notify_obsoleted: sending mail for %d\n') % ctx.rev()) |
|
120 mail.sendmail( |
|
121 ui, emailutils.parseaddr(msg['From'])[1], subs, msgtext, mbox=n.mbox |
|
122 ) |
|
123 |
|
124 |
|
125 def hook(ui, repo, hooktype, node=None, **kwargs): |
|
126 if hooktype != b"pretxnclose": |
|
127 raise error.Abort( |
|
128 _(b'Unsupported hook type %r') % pycompat.bytestr(hooktype) |
|
129 ) |
|
130 for rev in obsutil.getobsoleted(repo, repo.currenttransaction()): |
|
131 _report_commit(ui, repo, repo.unfiltered()[rev]) |