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