From: Brian Masney <masneyb@kernel.org>
To: "Kernel.org Tools" <tools@kernel.org>
Cc: Konstantin Ryabitsev <konstantin@linuxfoundation.org>,
Jerome Brunet <jbrunet@baylibre.com>,
Brian Masney <masneyb@kernel.org>
Subject: [PATCH b4 2/3] review: introduce support for b4.pw-key-command
Date: Thu, 15 Oct 2026 10:08:42 -0400 [thread overview]
Message-ID: <20261015-pw-key-command-v1-2-6daa9b8cee2a@kernel.org> (raw)
In-Reply-To: <20261015-pw-key-command-v1-0-6daa9b8cee2a@kernel.org>
Add support for b4.pw-key-command which is a command to run to obtain
the patchwork API key, so it can be kept in a password manager instead
of being stored in plain text in your git config. The first line of the
command's output is used as the key. The command is not run through a
shell, and it runs at most once per b4 invocation. When set, it takes
precedence over b4.pw-key.
Assisted-by: LLM
Signed-off-by: Brian Masney <masneyb@kernel.org>
---
docs/config.rst | 16 ++++++++++++++++
src/b4/__init__.py | 45 ++++++++++++++++++++++++++++++++++++++++++---
2 files changed, 58 insertions(+), 3 deletions(-)
diff --git a/docs/config.rst b/docs/config.rst
index 593129e8ae70..29f82a76f014 100644
--- a/docs/config.rst
+++ b/docs/config.rst
@@ -990,6 +990,22 @@ integrate your b4 workflow with patchwork.
Default: ``None``
+ :term:`b4.pw-key-command`
+ A command to run to obtain the patchwork API key, so it can be kept
+ in a password manager instead of being stored in plain text in your
+ git config. The first line of the command's output is used as the
+ key. The command is not run through a shell, and it runs at most
+ once per b4 invocation. When set, it takes precedence over
+ :term:`b4.pw-key`. For example::
+
+ pw-key-command = pass show patchwork/api-key
+
+ This setting is ignored in the in-tree ``.b4-config`` file.
+
+ Default: ``None``
+
+ .. versionadded:: v0.17
+
:term:`b4.pw-project`
The name of the patchwork project, exactly as seen in the URL
sub-path. For example::
diff --git a/src/b4/__init__.py b/src/b4/__init__.py
index 16b7208f0b09..1014815d4d33 100644
--- a/src/b4/__init__.py
+++ b/src/b4/__init__.py
@@ -392,6 +392,8 @@ LORENODE: Optional[liblore.LoreNode] = None
URL_SERVERS: Dict[str, Tuple[str, bool]] = dict()
# Indicates that we've cleaned cache already
_CACHE_CLEANED = False
+# Patchwork API keys returned by pw-key-command, keyed by the command
+_PW_KEY_CACHE: Dict[str, str] = dict()
# DNS answers for the life of the process (see _setup_dns_resolver)
_DNS_CACHE = dns.resolver.LRUCache()
# Used to track send-email alias replacements
@@ -5000,6 +5002,11 @@ def _setup_main_config(
'trailer*',
'pw-*',
]
+ # Never take these from an in-tree .b4-config: they run commands
+ # outside of any check flow, so a cloned tree could run anything.
+ wtdeny = [
+ 'pw-key-command',
+ ]
multivals = [
'keyringsrc',
'am-perpatch-check-cmd',
@@ -5015,6 +5022,9 @@ def _setup_main_config(
wtconfig = get_config_from_git(r'b4\..*', multivals=multivals, source=wtcfg)
logger.debug('wtcfg=%s', wtconfig)
for key, val in wtconfig.items():
+ if key in wtdeny:
+ logger.debug('wtcfg: ignoring %s', key)
+ continue
if key in multivals:
val = [_val_to_path(topdir, x) for x in val]
else:
@@ -6403,13 +6413,42 @@ def get_smtp(
def get_pw_key(config: Optional[ConfigDictT] = None) -> str:
- """Return the Patchwork API key from b4.pw-key.
+ """Return the Patchwork API key.
- Returns an empty string when no key is configured.
+ If b4.pw-key-command is set, run it and use the first line of its
+ output, so the key can live in a password manager instead of the git
+ config. The result is cached for the life of the process, so the
+ command runs at most once. Otherwise, fall back to b4.pw-key.
+ Returns an empty string when no key is configured or the command fails.
"""
if config is None:
config = get_main_config()
- return str(config.get('pw-key', '') or '')
+ cmdstr = config.get('pw-key-command')
+ if not cmdstr:
+ return str(config.get('pw-key', '') or '')
+
+ cmdstr = str(cmdstr)
+ if cmdstr in _PW_KEY_CACHE:
+ return _PW_KEY_CACHE[cmdstr]
+
+ pwkey = ''
+ try:
+ cmdargs = shlex.split(cmdstr)
+ logger.debug('Running pw-key-command: %s', cmdargs[0] if cmdargs else '')
+ # Leave stdin and stderr alone so password managers can prompt
+ sp = subprocess.run(cmdargs, stdout=subprocess.PIPE, check=False)
+ if sp.returncode != 0:
+ logger.critical('pw-key-command exited with status %s', sp.returncode)
+ else:
+ lines = sp.stdout.decode(errors='replace').splitlines()
+ pwkey = lines[0].strip() if lines else ''
+ if not pwkey:
+ logger.critical('pw-key-command returned no output')
+ except (OSError, ValueError) as ex:
+ logger.critical('Could not run pw-key-command: %s', ex)
+
+ _PW_KEY_CACHE[cmdstr] = pwkey
+ return pwkey
def get_patchwork_session(pwkey: str, pwurl: str) -> Tuple[requests.Session, str]:
--
2.55.0
next prev parent reply other threads:[~2026-10-15 14:08 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-15 14:08 [PATCH b4 0/3] review: add support for b4.pw-key-command Brian Masney
2026-10-15 14:08 ` [PATCH b4 1/3] review: centralize lookup of patchwork key in one place Brian Masney
2026-10-15 14:08 ` Brian Masney [this message]
2026-10-15 14:08 ` [PATCH b4 3/3] review: tests: add tests for get_pw_key() and b4.pw-key-command Brian Masney
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261015-pw-key-command-v1-2-6daa9b8cee2a@kernel.org \
--to=masneyb@kernel.org \
--cc=jbrunet@baylibre.com \
--cc=konstantin@linuxfoundation.org \
--cc=tools@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox