[PATCH b4] review: allow a custom Message-Id command for TUI replies
Christian Brauner <[email protected]> Fri, 22 May 2026 12:57:56 +0200
| Newsgroups | org.kernel.linux.tools |
|---|---|
| Message-ID | <[email protected]> |
The review TUI composes replies and sends them directly over SMTP via send_mail(), bypassing git-send-email and therefore any sendemail-validate hook that would normally rewrite the Message-Id. Review replies thus always got a built-in <...@b4> id. Add an opt-in b4.review-msgid-cmd option: when set, run it and use its stdout as the Message-Id (angle brackets optional), falling back to the built-in id when unset or on failure. Wire it into both the review-reply builder and LoreMessage.make_reply() so follow-up replies are covered too. Signed-off-by: Christian Brauner (Amutable) <[email protected]> --- I love b4 review and I'm in the process of making it my default review tool. 2026 and kernel review is officially better than Github. Didn't expect to see that day tbh. All my reviews come with specific message ids and I would like to be able to continue using them so I would appreciate the ability to define a message-id generation command in the [b4] .gitconfig section. I've written the patch for this. Thanks! Christian --- src/b4/__init__.py | 28 +++++++++++++++++++++++++++- src/b4/review/_review.py | 2 +- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/b4/__init__.py b/src/b4/__init__.py index 0530fec..ff056e2 100644 --- a/src/b4/__init__.py +++ b/src/b4/__init__.py @@ -177,6 +177,8 @@ DEFAULT_CONFIG: ConfigDictT = { 'review-target-branch': None, # Do not patatt-sign outgoing review emails 'review-no-patatt-sign': None, + # Command whose stdout is used as the Message-Id for review-TUI replies + 'review-msgid-cmd': None, } # This is where we store actual config @@ -2531,7 +2533,7 @@ class LoreMessage: references = LoreMessage.clean_header(self.msg.get('References', '') or '') msg['References'] = f'{references} <{self.msgid}>'.strip() msg['Date'] = email.utils.formatdate(localtime=True) - msg['Message-Id'] = make_msgid(idstring='b4-reply') + msg['Message-Id'] = make_msgid_with_cmd(idstring='b4-reply') return msg @staticmethod @@ -5853,6 +5855,30 @@ def make_msgid(idstring: Optional[str] = None, domain: str = 'b4') -> str: return email.utils.make_msgid(idstring=idstring, domain=domain) +def make_msgid_with_cmd(idstring: Optional[str] = None, domain: str = 'b4') -> str: + """Like make_msgid(), but use b4.review-msgid-cmd's output when set.""" + config = get_main_config() + cmdstr = config.get('review-msgid-cmd') + if isinstance(cmdstr, (list, tuple)): + cmdstr = cmdstr[0] if cmdstr else None + if cmdstr: + sp = shlex.shlex(str(cmdstr), posix=True) + sp.whitespace_split = True + args = [os.path.expanduser(os.path.expandvars(a)) for a in sp] + try: + out = subprocess.check_output(args, text=True).strip() + if out: + if not out.startswith('<'): + out = f'<{out}>' + return out + logger.warning('review-msgid-cmd %s produced no output', args) + except (OSError, subprocess.SubprocessError) as ex: + logger.warning( + 'review-msgid-cmd failed (%s); using built-in message-id', ex + ) + return make_msgid(idstring=idstring, domain=domain) + + def is_maildir(dest: str) -> bool: return ( os.path.isdir(os.path.join(dest, 'new')) diff --git a/src/b4/review/_review.py b/src/b4/review/_review.py index 47098c1..f874bb3 100644 --- a/src/b4/review/_review.py +++ b/src/b4/review/_review.py @@ -2521,7 +2521,7 @@ def _build_review_email( else: msg['References'] = f'<{header_info["msgid"]}>' msg['Date'] = email.utils.formatdate(localtime=True) - msg['Message-Id'] = b4.make_msgid(idstring='b4-review') + msg['Message-Id'] = b4.make_msgid_with_cmd(idstring='b4-review') return msg --- base-commit: d5d981426ead3f490713ef5d2fd1aa3d0f13b005 change-id: 20260522-review-msgid-cmd-49924d64541a