public inbox for tools@linux.kernel.org
 help / color / mirror / Atom feed
* [PATCH] b4: Implement --bcc and send-series-bcc config option
@ 2026-03-07  1:27 Dmitry Torokhov
  2026-03-07  4:49 ` Konstantin Ryabitsev
  0 siblings, 1 reply; 2+ messages in thread
From: Dmitry Torokhov @ 2026-03-07  1:27 UTC (permalink / raw)
  To: tools

Allow using blind carbon copy when sending entire series (no per-patch
Bcc).

Handling is similar to "To" and "Cc" handling except that it ignores
"not-me-too" and other exclusion lists as the intent to send to the
address(es) specified in bcc is explicit.

Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
---
 docs/config.rst           |  9 +++++++++
 docs/contributor/send.rst | 11 ++++++++++-
 src/b4/__init__.py        | 19 +++++++++++++------
 src/b4/command.py         |  1 +
 src/b4/ez.py              | 23 ++++++++++++++++++++++-
 5 files changed, 55 insertions(+), 8 deletions(-)

diff --git a/docs/config.rst b/docs/config.rst
index 8ffbf5e..9f508cf 100644
--- a/docs/config.rst
+++ b/docs/config.rst
@@ -591,6 +591,15 @@ Contributor-oriented settings
      .. versionchanged:: v0.15
         Added ``shallow`` config value.
 
+   :term:`b4.send-series-bcc`
+     A comma-separated list of addresses to always add to the "Bcc:" list.
+     Recipients in this list will always receive the series regardless of
+     any exclusion filters (such as the ``--not-me-too`` flag or the
+     :term:`b4.email-exclude` configuration setting).
+     See :ref:`prep_recipients`.
+
+     Default: ``None``
+
    :term:`b4.send-series-cc`
      A comma-separated list of addresses to always add to the "Cc:" header.
      See :ref:`prep_recipients`.
diff --git a/docs/contributor/send.rst b/docs/contributor/send.rst
index ba18ef5..1314d48 100644
--- a/docs/contributor/send.rst
+++ b/docs/contributor/send.rst
@@ -320,8 +320,17 @@ Command line flags
   configuration file using the :term:`b4.send-series-cc` option (see
   :ref:`contributor_settings`).
 
+``--bcc``
+  Additional email addresses to include into the Bcc: list. Recipients
+  in this list will always receive the series regardless of any
+  exclusion filters (such as the ``--not-me-too`` flag or the
+  :term:`b4.email-exclude` configuration setting). Separate multiple
+  entries with a comma. You can also set this in the configuration
+  file using the :term:`b4.send-series-bcc` option (see
+  :ref:`contributor_settings`).
+
 ``--not-me-too``
-  Removes your own email address from the recipients.
+  Removes your own email address from the To: or Cc: recipients.
 
 ``--no-sign``
   Don't sign your patches with your configured attestation mechanism.
diff --git a/src/b4/__init__.py b/src/b4/__init__.py
index 9a5d25b..9ad68a4 100644
--- a/src/b4/__init__.py
+++ b/src/b4/__init__.py
@@ -4413,6 +4413,14 @@ def send_mail(smtp: Union[smtplib.SMTP, smtplib.SMTP_SSL, List[str], None], msgs
         dryrun = True
 
     for msg in msgs:
+        if not destaddrs:
+            alldests = email.utils.getaddresses([str(x) for x in msg.get_all('to', [])])
+            alldests += email.utils.getaddresses([str(x) for x in msg.get_all('cc', [])])
+            alldests += email.utils.getaddresses([str(x) for x in msg.get_all('bcc', [])])
+            myaddrs = {x[1] for x in alldests}
+        else:
+            myaddrs = set(destaddrs)
+
         if not msg.get('X-Mailer'):
             msg.add_header('X-Mailer', f'b4 {__VERSION__}')
         msg.set_charset('utf-8')
@@ -4422,6 +4430,11 @@ def send_mail(smtp: Union[smtplib.SMTP, smtplib.SMTP_SSL, List[str], None], msgs
         else:
             nl = '\r\n'
 
+        # If we have a Bcc header, we must strip it from the message body
+        # before sending it out.
+        if msg['Bcc']:
+            del msg['Bcc']
+
         bdata = LoreMessage.get_msg_as_bytes(msg, nl=nl, headers='encode')
 
         subject = msg.get('Subject', '')
@@ -4450,12 +4463,6 @@ def send_mail(smtp: Union[smtplib.SMTP, smtplib.SMTP_SSL, List[str], None], msgs
             logger.info('    | ' + bdata.decode().rstrip().replace('\n', '\n    | '))
             logger.info('    --- DRYRUN: message ends ---')
             continue
-        if not destaddrs:
-            alldests = email.utils.getaddresses([str(x) for x in msg.get_all('to', [])])
-            alldests += email.utils.getaddresses([str(x) for x in msg.get_all('cc', [])])
-            myaddrs = {x[1] for x in alldests}
-        else:
-            myaddrs = set(destaddrs)
 
         tosend.append((myaddrs, bdata, ls))
 
diff --git a/src/b4/command.py b/src/b4/command.py
index a49a8bc..d115765 100644
--- a/src/b4/command.py
+++ b/src/b4/command.py
@@ -425,6 +425,7 @@ def setup_parser() -> argparse.ArgumentParser:
                          help='Do not add any addresses found in the cover or patch trailers to To: or Cc:')
     sp_send.add_argument('--to', nargs='+', metavar='ADDR', help='Addresses to add to the To: list')
     sp_send.add_argument('--cc', nargs='+', metavar='ADDR', help='Addresses to add to the Cc: list')
+    sp_send.add_argument('--bcc', nargs='+', metavar='ADDR', help='Addresses to add to the Bcc: list')
     sp_send.add_argument('--not-me-too', action='store_true', default=False,
                          help='Remove yourself from the To: or Cc: list')
     sp_send.add_argument('--resend', metavar='vN', nargs='?', const='latest',
diff --git a/src/b4/ez.py b/src/b4/ez.py
index 52eb239..378d201 100644
--- a/src/b4/ez.py
+++ b/src/b4/ez.py
@@ -1968,6 +1968,8 @@ def cmd_send(cmdargs: argparse.Namespace) -> None:
     excludes: Set[str] = set()
     pccs: Dict[str, List[Tuple[str, str]]] = dict()
 
+    bccdests = list()
+
     if cmdargs.preview_to or cmdargs.no_trailer_to_cc:
         todests = list()
         ccdests = list()
@@ -2006,6 +2008,7 @@ def cmd_send(cmdargs: argparse.Namespace) -> None:
 
     tos = set()
     ccs = set()
+    bccs = set()
     if cmdargs.preview_to:
         tos.update(cmdargs.preview_to)
     else:
@@ -2017,6 +2020,13 @@ def cmd_send(cmdargs: argparse.Namespace) -> None:
             ccs.update(cmdargs.cc)
         if config.get('send-series-cc'):
             ccs.add(config.get('send-series-cc'))
+        if cmdargs.bcc:
+            bccs.update(cmdargs.bcc)
+        if config.get('send-series-bcc'):
+            bccs.add(config.get('send-series-bcc'))
+    if bccs:
+        for pair in email.utils.getaddresses(list(bccs)):
+            bccdests.append(pair)
     if ccs:
         for pair in email.utils.getaddresses(list(ccs)):
             if pair[1] in seen:
@@ -2032,6 +2042,7 @@ def cmd_send(cmdargs: argparse.Namespace) -> None:
 
     allto = list()
     allcc = list()
+    allbcc = list()
     alldests = set()
 
     if todests:
@@ -2042,6 +2053,10 @@ def cmd_send(cmdargs: argparse.Namespace) -> None:
         allcc = b4.cleanup_email_addrs(ccdests, excludes, None)
         logger.debug('allcc: %s', allcc)
         alldests.update(set([x[1] for x in allcc]))
+    if bccdests:
+        allbcc = b4.cleanup_email_addrs(bccdests, set(), None)
+        logger.debug('allbcc: %s', allbcc)
+        alldests.update(set([x[1] for x in allbcc]))
 
     logger.debug('alldests: %s', alldests)
 
@@ -2076,7 +2091,7 @@ def cmd_send(cmdargs: argparse.Namespace) -> None:
 
     # Give the user the last opportunity to bail out
     if not cmdargs.dryrun:
-        if not len(alldests):
+        if not len(allto):
             logger.critical('CRITICAL: Could not find any destination addresses')
             logger.critical('          try b4 prep --auto-to-cc or b4 send --to addr')
             sys.exit(1)
@@ -2135,6 +2150,7 @@ def cmd_send(cmdargs: argparse.Namespace) -> None:
         logger.info('---')
         b4.print_pretty_addrs(allto, 'To')
         b4.print_pretty_addrs(allcc, 'Cc')
+        b4.print_pretty_addrs(allbcc, 'Bcc')
         logger.info('---')
         for commit, msg in patches:
             if not msg:
@@ -2256,6 +2272,11 @@ def cmd_send(cmdargs: argparse.Namespace) -> None:
                 msg.replace_header('Cc', b4.format_addrs(pcc))
             else:
                 msg.add_header('Cc', b4.format_addrs(pcc))
+        if allbcc:
+            if msg['Bcc']:
+                msg.replace_header('Bcc', b4.format_addrs(allbcc))
+            else:
+                msg.add_header('Bcc', b4.format_addrs(allbcc))
 
         send_msgs.append(msg)
 
-- 
2.53.0.473.g4a7958ca14-goog


^ permalink raw reply related	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-03-07  4:49 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-07  1:27 [PATCH] b4: Implement --bcc and send-series-bcc config option Dmitry Torokhov
2026-03-07  4:49 ` Konstantin Ryabitsev

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox