[PATCH b4 12/12] Add local CI review check

Tamir Duberstein <[email protected]>
Newsgroups org.kernel.linux.tools
Message-ID <[email protected]>
Configure b4's review TUI to run the project's local checks as a
per-series check command. The helper emits the JSON shape expected by b4
and records the input message headers so results show which series was
checked.

Signed-off-by: Tamir Duberstein <[email protected]>
---
 .b4-config    |  1 +
 misc/b4-ci.py | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 58 insertions(+)

diff --git a/.b4-config b/.b4-config
index ea95e4a..9fcebab 100644
--- a/.b4-config
+++ b/.b4-config
@@ -5,3 +5,4 @@
   send-series-cc = Konstantin Ryabitsev <[email protected]>
   send-endpoint-web = https://lkml.kernel.org/_b4_submit
   send-prefixes = b4
+  review-series-check-cmd = uv run --all-extras completion --all-groups python misc/b4-ci.py
diff --git a/misc/b4-ci.py b/misc/b4-ci.py
new file mode 100644
index 0000000..c65863c
--- /dev/null
+++ b/misc/b4-ci.py
@@ -0,0 +1,57 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0-or-later
+"""Run b4's local CI checks for the review TUI."""
+
+import json
+import subprocess
+import sys
+from email.parser import BytesParser
+from email.policy import default
+from typing import Dict, List
+
+CHECKS = [
+    ('ruff', ['ruff', 'check', '.']),
+    ('mypy', ['mypy', '.']),
+    ('pyright', ['pyright', '.']),
+    ('ty', ['ty', 'check', '.']),
+    ('pytest', ['pytest', '--durations=20']),
+]
+
+
+def read_input_details() -> List[str]:
+    msg = BytesParser(policy=default).parse(sys.stdin.buffer, headersonly=True)
+    details: List[str] = []
+    if subject := msg.get('Subject'):
+        details.append(f'Input-Subject: {subject}')
+    if msgid := msg.get('Message-ID'):
+        details.append(f'Input-Message-ID: {msgid}')
+    return details
+
+
+def run_check(tool: str, cmd: List[str], input_details: List[str]) -> Dict[str, str]:
+    proc = subprocess.run(cmd, capture_output=True, text=True, check=False)
+    details = [f'$ {" ".join(cmd)}']
+    if input_details:
+        details.extend(['', *input_details])
+    if proc.stdout:
+        details.extend(['', proc.stdout.rstrip()])
+    if proc.stderr:
+        details.extend(['', proc.stderr.rstrip()])
+
+    return {
+        'tool': tool,
+        'status': 'pass' if proc.returncode == 0 else 'fail',
+        'summary': 'passed' if proc.returncode == 0 else f'failed with exit code {proc.returncode}',
+        'details': '\n'.join(details),
+    }
+
+
+def main() -> None:
+    input_details = read_input_details()
+    results = [run_check(tool, cmd, input_details) for tool, cmd in CHECKS]
+    json.dump(results, sys.stdout, indent=2)
+    sys.stdout.write('\n')
+
+
+if __name__ == '__main__':
+    main()

-- 
2.53.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.