notify: add option for deterministic message-id generation
authorJoerg Sonnenberger <joerg@bec.de>
Sat, 07 Sep 2019 12:49:33 +0200
changeset 42904 d26a6706b070
parent 42903 66048f6b5d0d
child 42905 3df3b139a43d
notify: add option for deterministic message-id generation Copied from email by durin42: > Pierre-Yves asked offline why I asked a new option for this and why it > is not the default. Message-Id is supposed to be unique world-wide and > while it is desirable to have a deterministic mechanism for them for > creating follow-up emails, it needs organisational control for ensuring > the uniqueness. Differential Revision: https://phab.mercurial-scm.org/D6824
hgext/notify.py
tests/test-notify.t
--- a/hgext/notify.py	Sat Sep 07 23:20:11 2019 -0400
+++ b/hgext/notify.py	Sat Sep 07 12:49:33 2019 +0200
@@ -82,6 +82,12 @@
 
 notify.domain
   Default email domain for sender or recipients with no explicit domain.
+  It is also used for the domain part of the ``Message-Id`` when using
+  ``notify.messageidseed``.
+
+notify.messageidseed
+  Create deterministic ``Message-Id`` headers for the mails based on the seed
+  and the revision identifier of the first commit in the changeset.
 
 notify.style
   Style file to use when formatting emails.
@@ -144,6 +150,7 @@
 import email.errors as emailerrors
 import email.parser as emailparser
 import fnmatch
+import hashlib
 import socket
 import time
 
@@ -183,6 +190,9 @@
 configitem('notify', 'domain',
     default=None,
 )
+configitem('notify', 'messageidseed',
+    default=None,
+)
 configitem('notify', 'fromauthor',
     default=None,
 )
@@ -268,6 +278,7 @@
         self.subs = self.subscribers()
         self.merge = self.ui.configbool('notify', 'merge')
         self.showfunc = self.ui.configbool('notify', 'showfunc')
+        self.messageidseed = self.ui.config('notify', 'messageidseed')
         if self.showfunc is None:
             self.showfunc = self.ui.configbool('diff', 'showfunc')
 
@@ -412,10 +423,7 @@
 
         msg[r'X-Hg-Notification'] = r'changeset %s' % ctx
         if not msg[r'Message-Id']:
-            msg[r'Message-Id'] = encoding.strfromlocal(
-                '<hg.%s.%d.%d@%s>' % (ctx, int(time.time()),
-                                      hash(self.repo.root),
-                                      encoding.strtolocal(socket.getfqdn())))
+            msg[r'Message-Id'] = messageid(ctx, self.domain, self.messageidseed)
         msg[r'To'] = encoding.strfromlocal(', '.join(sorted(subs)))
 
         msgtext = encoding.strtolocal(msg.as_string())
@@ -517,3 +525,16 @@
 
     if count:
         n.send(ctx, count, data)
+
+def messageid(ctx, domain, messageidseed):
+    if domain and messageidseed:
+        host = domain
+    else:
+        host = encoding.strtolocal(socket.getfqdn())
+    if messageidseed:
+        messagehash = hashlib.sha512(ctx.hex() + messageidseed)
+        messageid = '<hg.%s@%s>' % (messagehash.hexdigest()[:64], host)
+    else:
+        messageid = '<hg.%s.%d.%d@%s>' % (ctx, int(time.time()),
+                                          hash(ctx.repo().root), host)
+    return encoding.strfromlocal(messageid)
--- a/tests/test-notify.t	Sat Sep 07 23:20:11 2019 -0400
+++ b/tests/test-notify.t	Sat Sep 07 12:49:33 2019 +0200
@@ -99,7 +99,13 @@
     "/long/path/repository" into "repository". Default: 0.
   
   notify.domain
-    Default email domain for sender or recipients with no explicit domain.
+    Default email domain for sender or recipients with no explicit domain. It is
+    also used for the domain part of the "Message-Id" when using
+    "notify.messageidseed".
+  
+  notify.messageidseed
+    Create deterministic "Message-Id" headers for the mails based on the seed
+    and the revision identifier of the first commit in the changeset.
   
   notify.style
     Style file to use when formatting emails.
@@ -190,7 +196,7 @@
 of the very long subject line
 pull (minimal config)
 
-  $ hg --traceback --cwd b pull ../a | "$PYTHON" $TESTTMP/filter.py
+  $ hg --traceback --cwd b --config notify.domain=example.com --config notify.messageidseed=example pull ../a | "$PYTHON" $TESTTMP/filter.py
   pulling from ../a
   searching for changes
   adding changesets
@@ -203,10 +209,10 @@
   Content-Transfer-Encoding: 7bit
   Date: * (glob)
   Subject: changeset in $TESTTMP/b: b
-  From: test
+  From: test@example.com
   X-Hg-Notification: changeset 00a13f371396
-  Message-Id: <*> (glob)
-  To: baz, foo@bar
+  Message-Id: <hg.ba3098a36bd4c297288d16788623a841f81f618ea961a0f0fd65de7eb1191b66@example.com>
+  To: baz@example.com, foo@bar
   
   changeset 00a13f371396 in $TESTTMP/b
   details: $TESTTMP/b?cmd=changeset;node=00a13f371396