@@ -9,6 +9,9 @@ import argparse
import logging
import b4
import sys
+import subprocess
+import os
+import tempfile
logger = b4.logger
@@ -93,6 +96,74 @@ def cmd_send(cmdargs):
b4.ez.cmd_send(cmdargs)
+def cmd_flow(cmdargs):
+ import b4.ez
+ def run_b4_recursively(command_args: str):
+ current_path = os.path.dirname(os.path.abspath(__file__))
+ command_args_run = [os.path.join(current_path, 'b4.sh')] + command_args.split(" ")
+ command_args_bak = [os.path.join(current_path, '..', '..', 'b4.sh')] + command_args.split(" ")
+ # TODO: can we deterministically see where the shell script executed from?
+ try:
+ out = subprocess.run(command_args_run, check=True)
+ except:
+ try:
+ out = subprocess.run(command_args_bak, check=True)
+ except:
+ return False
+ return True
+
+ def question_user(prompt: str, command: str) -> bool:
+ # We want to default this as a Y so that users can use the `yes` command
+ user_input = input(prompt + ' [Y/n]: ')
+ if user_input.strip().lower() not in ['y', '']:
+ return False
+ run_b4_recursively(command)
+ return True
+
+ # Colors
+ WARNING = '\033[93m'
+ ENDC = '\033[0m'
+
+ print("Starting up an interactive session to submit kernel patches upstream. Make your changes prior to running this. Press Ctrl+C to exit at any point.")
+ print(WARNING)
+ print("If you changed a commit message, trailers will NOT be applied for that patch. Please manually apply them.")
+ print(ENDC)
+
+ question_user("Retrieve trailers?", "trailers -u")
+ question_user("Edit cover letter?", "prep --edit-cover")
+ with tempfile.TemporaryDirectory() as tmpdir:
+ if question_user("View patches?", f'send -o {tmpdir}'):
+ for file in os.listdir(tmpdir):
+ print(f"viewing: {file}")
+ file_path = os.path.join(tmpdir, file)
+ with open(file_path, 'r') as f:
+ content = f.read().encode()
+ b4.edit_in_editor(content, filehint='Review Patches')
+ # needs to run `b4 send -o {tmpdir}` just in case user says no in the previous question
+ if question_user("Run checkpatch.pl?", f'send -o {tmpdir}'):
+ for file in os.listdir(tmpdir):
+ if file.startswith('0000-'):
+ continue
+
+ print(file)
+ file_path = os.path.join(tmpdir, file)
+ subprocess.run(["./scripts/checkpatch.pl", file_path], check=True)
+
+ cover, tracking = b4.ez.load_cover(strip_comments=True)
+ revision = tracking['series'].get('revision')
+ if revision > 1:
+ question_user("Compare with previous version?", f"prep --compare-to v{revision - 1}")
+
+ question_user("Auto add emails?", "prep --auto-to-cc")
+ question_user("Check with b4?", "prep --check")
+ question_user("Reflect patches?", "send --reflect")
+
+ print(WARNING)
+ print("You are about to send your patches upstream.")
+ print(ENDC)
+ question_user("Send?", "send")
+
+
def cmd_am(cmdargs):
import b4.mbox
b4.mbox.main(cmdargs)
@@ -389,6 +460,10 @@ def setup_parser() -> argparse.ArgumentParser:
help='Submit the token received via verification email')
sp_send.set_defaults(func=cmd_send)
+ # b4 flow
+ sp_flow = subparsers.add_parser('flow', help='Interactive session to submit patches upstream')
+ sp_flow.set_defaults(func=cmd_flow)
+
return parser
`b4 flow` is an interactive session designed to handhold the user into submitting patches upsteam. As b4 grows in complexity, there will be multiple commands to remember. `b4 flow` covers the majority of cases and shows the proper way to use the program. Signed-off-by: Rudraksha Gupta <guptarud@gmail.com> --- `b4 flow` is an interactive session designed to handhold the user into submitting patches upsteam. As b4 grows in complexity, there will be multiple commands to remember. `b4 flow` covers the majority of cases and shows the proper way to use the program. --- src/b4/command.py | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) --- base-commit: 42535902a457332560d860608b770fca9eea1382 change-id: 20250302-flow-954294542408 Best regards,