From patchwork Sun Mar 29 15:01:26 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2075 Return-Path: X-Original-To: u-boot-concept@u-boot.org Delivered-To: u-boot-concept@u-boot.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1774796520; bh=To45w+1AETczLa39yFpi7+ngu84VXNlW7R5xbeieEjA=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=Xtglq+Co3hfbdzF46vAxqPMjQN5aqsAuhwQa4tfW7dNMbb0FSvm9HaTzkcehPZjhc mbRIFrJqyfVntO/KmKOV6+vXe1hBX8tKYfan7HBOJLYC6BM1Q1sYLs0cK7jC3OnE0q y5uSCDzImpadmQU5BTLbyUMabLHTr2Yo+r8hrXaejQ+pp5gSQgLZK5LDrwaFSFaiJb C+ckbVDhAHpiDC76pBP4vTW1BCEF7q/P2LrKHI4dXxXSAa9OeiRU8nu14y69DqI3PL p4pmf1AeND8tyLypNWZki+DlK1YvyY0FnziSpIqhWPtCpYQeyMwnY7F9ej9AsH0MCo umMnT9JbdFVRA== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id ED65F6A2D3 for ; Sun, 29 Mar 2026 09:02:00 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10024) with ESMTP id UPUKAvW3bCbG for ; Sun, 29 Mar 2026 09:02:00 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1774796520; bh=To45w+1AETczLa39yFpi7+ngu84VXNlW7R5xbeieEjA=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=Xtglq+Co3hfbdzF46vAxqPMjQN5aqsAuhwQa4tfW7dNMbb0FSvm9HaTzkcehPZjhc mbRIFrJqyfVntO/KmKOV6+vXe1hBX8tKYfan7HBOJLYC6BM1Q1sYLs0cK7jC3OnE0q y5uSCDzImpadmQU5BTLbyUMabLHTr2Yo+r8hrXaejQ+pp5gSQgLZK5LDrwaFSFaiJb C+ckbVDhAHpiDC76pBP4vTW1BCEF7q/P2LrKHI4dXxXSAa9OeiRU8nu14y69DqI3PL p4pmf1AeND8tyLypNWZki+DlK1YvyY0FnziSpIqhWPtCpYQeyMwnY7F9ej9AsH0MCo umMnT9JbdFVRA== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id AE10D6A2AB for ; Sun, 29 Mar 2026 09:02:00 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1774796518; bh=GSMRS6M2FTolFF6qEWopzdDsvhgBR5Z5nDikQw7ODzY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=pZSv40u2Toi0Jn7AdjQe7Es+JM9oURyIx1LTdLSt6z8AZZmBfiFQMjCNZZhBHu791 reomrrf3mZNrFMrPioEPodI87rnv/lzWc6QwzTlef7N7X2Zn9FqxTt/C4WYIgxCnz8 ruQjo8Dv1FamTpMVv1QugY8rxYVZnYG9qI1qhhqY3OFbvY2EfAwhRWE54Meht+4ZVS ZazWlBG8hyvMrWebJusLv5A3EWN932pfClSpcne0s5QCww/8tM1wjHKeThBCnFc1vG YN5t2D6nD+FLhqlAMxbBUgX/5w/K7b7rYXDMIM+3qNLyAq4mRmIJ1WSKohi05k/AlC TJhohgh4XhU6w== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 917DE6A2D0; Sun, 29 Mar 2026 09:01:58 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10026) with ESMTP id oWeCbX7KCZ9u; Sun, 29 Mar 2026 09:01:58 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1774796516; bh=KC6epDvCNXv5lzgtCw+7SpVNGCLzlVxAQGAvXvrgeNU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=r8/ryCvHm1zXXCoIEY8dFxrk1EE8WN9/vjq5YEUQfZbfQ88b4nZumWpSW/1BMWK4J Li2OY8XQ40KY3TWX/oqyk/CuKwq6Jwv9k4vWtFDSkayf0XiW0JOKid4PlVDcYB5kI4 O/8+rnrkFpkWPH2NfcIkDq7LdSBBJd8P9PZkq0tU3u6FFnao0yuSdwZG4AIy5geA4D 2yh5zAIXBU0XLBv80EImkGqY8MA5VJdRNQzGeJLMB1ErDUa84B8cyl73ouix6vcEhy 1ZG36COUMfMM+UisMoJ0J5lSc8+uTopA2a/JT7+Q4UN6O4tYY36Ezndn2bKT3ClENg tIRvhjJ9FqIyA== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 2E7896A2D3; Sun, 29 Mar 2026 09:01:56 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Sun, 29 Mar 2026 09:01:26 -0600 Message-ID: <20260329150140.4095446-2-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260329150140.4095446-1-sjg@u-boot.org> References: <20260329150140.4095446-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: PLGHYQGARNXIMBSPI32X3QFQVRIREK6C X-Message-ID-Hash: PLGHYQGARNXIMBSPI32X3QFQVRIREK6C X-MailFrom: sjg@u-boot.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 01/11] u_boot_pylib: Use PIPE_PTY when an output function is given List-Id: Discussion and patches related to U-Boot Concept Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Simon Glass When an output_func is provided to run_pipe(), use PIPE_PTY instead of PIPE for stdout. This ensures the subprocess sees a tty, so interactive prompts and editors work correctly while still allowing the output to be captured and processed in real time. Signed-off-by: Simon Glass --- tools/u_boot_pylib/command.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tools/u_boot_pylib/command.py b/tools/u_boot_pylib/command.py index 370713bdf41..54a7ee8e672 100644 --- a/tools/u_boot_pylib/command.py +++ b/tools/u_boot_pylib/command.py @@ -117,7 +117,10 @@ def run_pipe(pipe_list, infile=None, outfile=None, capture=False, elif stdin_data: kwargs['stdin'] = cros_subprocess.PIPE if pipeline or capture: - kwargs['stdout'] = cros_subprocess.PIPE + if output_func: + kwargs['stdout'] = cros_subprocess.PIPE_PTY + else: + kwargs['stdout'] = cros_subprocess.PIPE elif outfile: kwargs['stdout'] = open(outfile, 'wb') if capture_stderr: From patchwork Sun Mar 29 15:01:27 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2076 Return-Path: X-Original-To: u-boot-concept@u-boot.org Delivered-To: u-boot-concept@u-boot.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1774796521; bh=bhoRALAIbxb1k8+aBA4Py3iYdLP1aryG8dG/DstmZL8=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=UgWStCLILHlvtXEt5eZvmAqMxKU+/9pNUON+ymjHhiBjZDmHoMzsTUtZ4f7HmvDKN O5MW6W7wjPBpR5S1cHQHt3oDJWN2K6UyNaFOGD1RFBlt6euDmFqQPwL10ntJIswtZJ 8yusv2EG+piIOhQuW3bZczpe75nfAzP5W9OVuojvQnV7FSsmwH2RE6fQYhVaRpbsSo /qi+4JfN2g+q9L1pe5TPifRK7VGMXTxYMZgmsq6QhuNTs9f9je6PgAB8K6ut7Sb4k5 /3sUAXo1o33fE+bXxFoYLPkT5m5hRU93gU7lvOI1DRiL4K/eDqyuPkxSPWaqgSzEoK DCSLeDhKKwyhA== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id AB6B26A2AB for ; Sun, 29 Mar 2026 09:02:01 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10024) with ESMTP id 88ZPQkkrrXiJ for ; Sun, 29 Mar 2026 09:02:01 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1774796521; bh=bhoRALAIbxb1k8+aBA4Py3iYdLP1aryG8dG/DstmZL8=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=UgWStCLILHlvtXEt5eZvmAqMxKU+/9pNUON+ymjHhiBjZDmHoMzsTUtZ4f7HmvDKN O5MW6W7wjPBpR5S1cHQHt3oDJWN2K6UyNaFOGD1RFBlt6euDmFqQPwL10ntJIswtZJ 8yusv2EG+piIOhQuW3bZczpe75nfAzP5W9OVuojvQnV7FSsmwH2RE6fQYhVaRpbsSo /qi+4JfN2g+q9L1pe5TPifRK7VGMXTxYMZgmsq6QhuNTs9f9je6PgAB8K6ut7Sb4k5 /3sUAXo1o33fE+bXxFoYLPkT5m5hRU93gU7lvOI1DRiL4K/eDqyuPkxSPWaqgSzEoK DCSLeDhKKwyhA== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 665176A2DD for ; Sun, 29 Mar 2026 09:02:01 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1774796518; bh=bdYU4nDQtCsSpa+cLHyO+dfv5R1g96AE7bOqRFV5Ibg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=QwmnAs+cn4bo06eoZWaelf37dtXdePpHgS4T/SixjQmwyCTWx2UL9JJme+pdntMf1 GOw/AH8y36L4U0UIe0PO4cuf+qxq/aNagiBVfTwPyAHvap3J3PLBeZK2oMHKyCu8SB E3f0i2eCbwdLx53u1h1l6hs/hSNVjwrtePeJyL4eB833hjvMvViRUn4UoPzwmgu8dv abKWl9qKVmJXC12ZXxIO/ctpoay+Alt6fUZ5kJSsJv0BkeKYYzMQtjh/3vGHtzDmAG 0JFw0PfUdzNpbdItaQJoWLpBk1+UzCwtLfuH6yt9Aa1jh0BCGF3a8b3KCgPtwpGOrX 9R+7gO71msmdA== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id A8A5E6A2D1; Sun, 29 Mar 2026 09:01:58 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10026) with ESMTP id BpU9F3qLhCcA; Sun, 29 Mar 2026 09:01:58 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1774796517; bh=0eBQISj7CjGwDSU9CagRdVSrJFCPiwZ5tv8RWJ2n0Yw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=GuGpdt4dH9izB34j6IN90fxKAXkrcZ4mZMQlPgjb48jFTOnIlySGV0BBBbuJPtw/Q DmT9gFI69jA1eXq1XVOKoDXgos8xnEvobgupZ59EDnOmzU6+r5Mf8jkiHGBHyWszwH KyGI+bYHDkQGRm7XyWLWS9D0oEq5vvCwA66pzZmeyqBfIYGw3ufb09LEUiXMoKsUcx GuCjienz6+zxOI5H3vPmZpoDSxANSY3e7YBV+JvwuTTPJa84gCFc3TycP6kHdA7bwI l45BqI8+QgNLaX1FVMIdS+fRMfoBBAdV2CfdrOiCkPAghR5PcxkZhkcRgdl66y3w+3 wGcWWO61W6l6w== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id E98696A02E; Sun, 29 Mar 2026 09:01:56 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Sun, 29 Mar 2026 09:01:27 -0600 Message-ID: <20260329150140.4095446-3-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260329150140.4095446-1-sjg@u-boot.org> References: <20260329150140.4095446-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: TRPZN2UXXU2NF5B42U4T5CSRGMFCCXQX X-Message-ID-Hash: TRPZN2UXXU2NF5B42U4T5CSRGMFCCXQX X-MailFrom: sjg@u-boot.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 02/11] u_boot_pylib: Capture send-output to count sent patches List-Id: Discussion and patches related to U-Boot Concept Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Simon Glass Capture the git send-email output using an echo output function that writes to the terminal in real time while also buffering the output. After the command finishes, count "Result: " lines in the SMTP log to determine how many patches were actually sent. Change the return value to a (cmd_string, num_sent) tuple so callers can distinguish between a successful send and a user abort. Signed-off-by: Simon Glass --- tools/u_boot_pylib/gitutil.py | 37 +++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/tools/u_boot_pylib/gitutil.py b/tools/u_boot_pylib/gitutil.py index c96cd251776..22414d250a9 100644 --- a/tools/u_boot_pylib/gitutil.py +++ b/tools/u_boot_pylib/gitutil.py @@ -483,7 +483,9 @@ def email_patches(series, cover_fname, args, dry_run, warn_on_error, cc_fname, cwd (str): Path to use for patch files (None to use current dir) Returns: - Git command that was/would be run + tuple: + str: Git command that was/would be run + int: Number of patches successfully sent (0 for dry run) # For the duration of this doctest pretend that we ran patman with ./patman >>> _old_argv0 = sys.argv[0] @@ -501,22 +503,22 @@ def email_patches(series, cover_fname, args, dry_run, warn_on_error, cc_fname, >>> series['cc'] = ['mary'] >>> email_patches(series, 'cover', ['p1', 'p2'], True, True, 'cc-fname', \ False, alias) - 'git send-email --annotate --to "f.bloggs@napier.co.nz" --cc \ -"m.poppins@cloud.net" --cc-cmd "./patman send --cc-cmd cc-fname" cover p1 p2' + ('git send-email --annotate --to "f.bloggs@napier.co.nz" --cc \ +"m.poppins@cloud.net" --cc-cmd "./patman send --cc-cmd cc-fname" cover p1 p2', 0) >>> email_patches(series, None, ['p1'], True, True, 'cc-fname', False, \ alias) - 'git send-email --annotate --to "f.bloggs@napier.co.nz" --cc \ -"m.poppins@cloud.net" --cc-cmd "./patman send --cc-cmd cc-fname" p1' + ('git send-email --annotate --to "f.bloggs@napier.co.nz" --cc \ +"m.poppins@cloud.net" --cc-cmd "./patman send --cc-cmd cc-fname" p1', 0) >>> series['cc'] = ['all'] >>> email_patches(series, 'cover', ['p1', 'p2'], True, True, 'cc-fname', \ True, alias) - 'git send-email --annotate --to "this-is-me@me.com" --cc-cmd "./patman \ -send --cc-cmd cc-fname" cover p1 p2' + ('git send-email --annotate --to "this-is-me@me.com" --cc-cmd "./patman \ +send --cc-cmd cc-fname" cover p1 p2', 0) >>> email_patches(series, 'cover', ['p1', 'p2'], True, True, 'cc-fname', \ False, alias) - 'git send-email --annotate --to "f.bloggs@napier.co.nz" --cc \ + ('git send-email --annotate --to "f.bloggs@napier.co.nz" --cc \ "f.bloggs@napier.co.nz" --cc "j.bloggs@napier.co.nz" --cc \ -"m.poppins@cloud.net" --cc-cmd "./patman send --cc-cmd cc-fname" cover p1 p2' +"m.poppins@cloud.net" --cc-cmd "./patman send --cc-cmd cc-fname" cover p1 p2', 0) # Restore argv[0] since we clobbered it. >>> sys.argv[0] = _old_argv0 @@ -530,7 +532,7 @@ send --cc-cmd cc-fname" cover p1 p2' "Series-to: Fred Bloggs \n" "Or do something like this\n" "git config sendemail.to u-boot@lists.denx.de") - return None + return None, 0 cc = build_email_list(list(set(series.get('cc')) - set(series.get('to'))), alias, '--cc', warn_on_error) if self_only: @@ -553,10 +555,19 @@ send --cc-cmd cc-fname" cover p1 p2' if cover_fname: cmd.append(cover_fname) cmd += args + num_sent = 0 if not dry_run: - command.run(*cmd, capture=False, capture_stderr=False, cwd=cwd) - return' '.join([f'"{x}"' if ' ' in x and '"' not in x else x - for x in cmd]) + def echo_output(_stream, data): + os.write(sys.stdout.fileno(), data) + return False + + result = command.run_pipe( + [cmd], capture=True, output_func=echo_output, + raise_on_error=False, cwd=cwd) + num_sent = result.stdout.count('Result: ') + cmd_str = ' '.join([f'"{x}"' if ' ' in x and '"' not in x else x + for x in cmd]) + return cmd_str, num_sent def lookup_email(lookup_name, alias, warn_on_error=True, level=0): From patchwork Sun Mar 29 15:01:28 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2077 Return-Path: X-Original-To: u-boot-concept@u-boot.org Delivered-To: u-boot-concept@u-boot.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1774796522; bh=rnxTSHIpcNJV9cX05QyAaz5vtsgcx9I+cSLlSvNCjFw=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=LM0rCIeLzKZIea7M00pryrZelwTRLWJPnnT7rGjdHmDxuKjHyqNUb31nPfemlV1pV q1SNm+xj28vzXkwcHyJV81E+/+KyifRLLZDJhNkSKb3Vo9o+n7vq8HuPYQYBQ9gszS GjWAAFW77fqRaDTH3+8HQxTKYY3V1scsw1E6PXCGC50A02kfQjYajqnmqzaySqE+q2 Dqi0TkBs+Ql7bNNNhw2SHsbSFkoljovhLUsh6Zx2Ay81pKeEfmypguPJQ6mFirkKUK wfCqkFpSZre8rn98opyKQNqfNQuBHGoSuCsQJMK0YE/E5Zy/PhGMey5Gt3e72gEFvf Gi9QQqOM9F7ZQ== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 6C1416A2DB for ; Sun, 29 Mar 2026 09:02:02 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10024) with ESMTP id YrX3mXwQsRhx for ; Sun, 29 Mar 2026 09:02:02 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1774796522; bh=rnxTSHIpcNJV9cX05QyAaz5vtsgcx9I+cSLlSvNCjFw=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=LM0rCIeLzKZIea7M00pryrZelwTRLWJPnnT7rGjdHmDxuKjHyqNUb31nPfemlV1pV q1SNm+xj28vzXkwcHyJV81E+/+KyifRLLZDJhNkSKb3Vo9o+n7vq8HuPYQYBQ9gszS GjWAAFW77fqRaDTH3+8HQxTKYY3V1scsw1E6PXCGC50A02kfQjYajqnmqzaySqE+q2 Dqi0TkBs+Ql7bNNNhw2SHsbSFkoljovhLUsh6Zx2Ay81pKeEfmypguPJQ6mFirkKUK wfCqkFpSZre8rn98opyKQNqfNQuBHGoSuCsQJMK0YE/E5Zy/PhGMey5Gt3e72gEFvf Gi9QQqOM9F7ZQ== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 2412E6A2DE for ; Sun, 29 Mar 2026 09:02:02 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1774796518; bh=SLfLIQdyD93AT39rOsl+tdhde9e3mbSbIdGiuutsJjc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=P7ITREUYr3mbvUtOWq85j9c2VZpL6A/Y+DUo6O2zc7DshMDwehMuQNxepiqW6p8IV +8Ejjd70odm711syk32iZ2FHp3TbqMnGXBXpSWbKUS1nHXFfga8pieXzPN8OeokB0Q 7IUx12KjZEkugdHh9bY/5RfflKcDmVapoYKp5kn/i0MYVXhQSuETZGgcrcvhUBvFuZ f1Hze1hZLvgLin/X1bcYAylt2SSsSzpX2znjWz0UCag4faLvFm2ocnOxtcWIpXVdTU 5H13CdpzVyF1EQwEeiyGAd7lEC/Niy1ePesrnjmPyHjRpm4pb9d7NSVIK2j+VT8njv 3g2kBt/1leV+Q== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id C34C96A02E; Sun, 29 Mar 2026 09:01:58 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10026) with ESMTP id oRPQdVxH9msJ; Sun, 29 Mar 2026 09:01:58 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1774796518; bh=edY5INgVaEfncscn+sJtPuoCLZgx29klkW9r++Hhz78=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=DOQZIwRPvr/UAH2byqwvQZ8PsZwihElr0tRsRPZhdO/ipERnniwKyGusJtV52zqql X1gpdsirsRzd8Y7H9eSZwU/2pyNeT0/5LEsY46q2aE6jm+9t8pZ4xuF5XHs7XBsYlI VZRdQyd2MSw3anrMgrZ6OPvkLluqEUWkTVCSQ+rU0saO+GqZgYuwUdiOcKobBQafSj kjC1R6wKZ94pTdLaBVmRzd0px18bNFCBsdUOmGn1LwDoxzKcXPv2IjOqtih7XV2S7F DSyJ/K0j05mGgPj0QDD6DS8mExFxfBmLyWX3q7LOiEJW2cavZhMp8lzmqYNM5mRExG 2RtCYIJH92HgA== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id C94996A2AB; Sun, 29 Mar 2026 09:01:57 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Sun, 29 Mar 2026 09:01:28 -0600 Message-ID: <20260329150140.4095446-4-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260329150140.4095446-1-sjg@u-boot.org> References: <20260329150140.4095446-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: NHYBWYCTAR45C4K3P5F6CJNTSAR7U5KN X-Message-ID-Hash: NHYBWYCTAR45C4K3P5F6CJNTSAR7U5KN X-MailFrom: sjg@u-boot.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 03/11] patman: Strip version suffix in get_series_upstream() List-Id: Discussion and patches related to U-Boot Concept Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Simon Glass When get_series_upstream() auto-detects the series from the current branch, it uses the raw branch name (e.g. 'bootma-us2') without stripping the version suffix. This causes the database lookup to fail, so the per-upstream patchwork URL is not found and patman falls back to the wrong server. Call split_name_version() to strip the version number, matching what _parse_series() already does. Also pass self.gitdir to get_branch() Signed-off-by: Simon Glass --- tools/patman/cser_helper.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/patman/cser_helper.py b/tools/patman/cser_helper.py index ec3956db4d5..0b9f670ca25 100644 --- a/tools/patman/cser_helper.py +++ b/tools/patman/cser_helper.py @@ -435,7 +435,8 @@ class CseriesHelper: str or None: Upstream name, or None if not found """ if not name: - name = gitutil.get_branch() + name = gitutil.get_branch(self.gitdir) + name, _ = patchstream.split_name_version(name) ser = self.get_series_by_name(name) if ser: return ser.upstream From patchwork Sun Mar 29 15:01:29 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2078 Return-Path: X-Original-To: u-boot-concept@u-boot.org Delivered-To: u-boot-concept@u-boot.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1774796527; bh=LqPQZynybYrbSqmtBFgOQhdyBUzdQMPlJDu1/u8XYl0=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=QAF7ajJ+pCg9OeaZ7teCTpzhBx3XahCtkOYeMFESDCgnnYQzD+76RNwUukm+wFw62 vGjR/JvbOcAtfzJ4rQDy+RXlNqmjS3tRMLSBUv24/n59e7BYtYfU7lM43yOvfZ7QwV wRh43QtNEJBbN5KXzJHgL+tOYQoWxDEgwKiyZ2waK4LQJScPevw5lTgKBkSBj/72H+ dbXL10xZtbS4BihgcRnGvKbxI1sEN9nz6CTnWENngTpQvWNEnEpSksLM6gBqZxyIXA 9bTI98cIYd9hWHHzyCFJ+FmdCvBiHHDaKv48d7ne3NW4lE5BR7mHnXJROioBx2Rght tqKdkAmd5Isiw== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 598526A2D4 for ; Sun, 29 Mar 2026 09:02:07 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10024) with ESMTP id 1C5FaETEvhgD for ; Sun, 29 Mar 2026 09:02:07 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1774796527; bh=LqPQZynybYrbSqmtBFgOQhdyBUzdQMPlJDu1/u8XYl0=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=QAF7ajJ+pCg9OeaZ7teCTpzhBx3XahCtkOYeMFESDCgnnYQzD+76RNwUukm+wFw62 vGjR/JvbOcAtfzJ4rQDy+RXlNqmjS3tRMLSBUv24/n59e7BYtYfU7lM43yOvfZ7QwV wRh43QtNEJBbN5KXzJHgL+tOYQoWxDEgwKiyZ2waK4LQJScPevw5lTgKBkSBj/72H+ dbXL10xZtbS4BihgcRnGvKbxI1sEN9nz6CTnWENngTpQvWNEnEpSksLM6gBqZxyIXA 9bTI98cIYd9hWHHzyCFJ+FmdCvBiHHDaKv48d7ne3NW4lE5BR7mHnXJROioBx2Rght tqKdkAmd5Isiw== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 0F0426A2AB for ; Sun, 29 Mar 2026 09:02:07 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1774796524; bh=pnFlT/hBZn4JH30NmuQoadxsquO6yrf2egFw0ZeNAqs=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=k3+cuE/G3+LYC97GG+MEV/DGNxzzx3Vu4zPGjtdpZ6UrX1/asNG00dQ60FF9ZJz5+ ZEXWbNoNX8zrqwxsH/QtLDLlhrgM5V/5z4jifqJs9Z3p13vMXSuwPJ9JC59NadYLoX OzY9PrR/i5OVD+5R2ExAdkBBBr5lXOdeC4kRkP9m3d5I9993TvpkW/0BtH7Vdh67p/ iechQycbpfoyNUW4vi3iCg7me9w1qpK5/YWREBaxDGer4NzrLa3Pkt5ifmsCnCd9T4 TWCsn8UFSD+whUCC8SZN/NHcRjLOBj0IyxDqYKwm68S6d5yFKdDOh3Y3sZNyct/V5x Lb6kb8HveAeBQ== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id EBA966A2AB; Sun, 29 Mar 2026 09:02:04 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10026) with ESMTP id ziRJAn3XrHgC; Sun, 29 Mar 2026 09:02:04 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1774796518; bh=9yu24i8p8UbkkwSHdpp6UhEtszf5rd3MveD4PWcMtqQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Pta69JmPgFqtoHQPqkMl4q8oT0k96fgvOmuACr9UHQHq53pc6RiP3QgkugFkjlUH8 DEQhuGQoPaJMN5+di2rD9UmFn0ovCxeAxO85Uv23jaAoUo0LbM4EyJdaZArPNORb0Y osCvzUQMFjAaUDwRAaQB5YqS18F1fdL1q7gt8raEvNzeobOGTUG2aiKw61g3rx2ROK 84pHS7jvDy5jforjbaeg/3rx+V8ShvVcD7++hEbiNobtRACDrpzOlj7iGLEAyGb/BM upnqkvqeQ7UtFd1Xn1tQTD3BztJb0rKnTH1IxoLH7U7hJnkvYOsMnF531TVGcaSasW 4I6VMiTfCHiBg== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 871A46A2CB; Sun, 29 Mar 2026 09:01:58 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Sun, 29 Mar 2026 09:01:29 -0600 Message-ID: <20260329150140.4095446-5-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260329150140.4095446-1-sjg@u-boot.org> References: <20260329150140.4095446-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: KGFU2L2JGLZ5T64GWGC6X7MGMDVKQD5I X-Message-ID-Hash: KGFU2L2JGLZ5T64GWGC6X7MGMDVKQD5I X-MailFrom: sjg@u-boot.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 04/11] patman: Fix output ordering when running git send-email List-Id: Discussion and patches related to U-Boot Concept Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Simon Glass When patman runs git send-email, stdout goes through a PTY filter while stderr goes directly to the terminal. Since git send-email writes prompts to stderr and email details to stdout, the prompts appear before the context they relate to (e.g. "Send this email?" appears before the email headers). Add a merge_stderr parameter to run_pipe() and use it for the send-email invocation. This redirects stderr to stdout so both streams go through the same PTY, preserving the correct output order. Signed-off-by: Simon Glass --- tools/u_boot_pylib/command.py | 10 ++++++++-- tools/u_boot_pylib/gitutil.py | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/tools/u_boot_pylib/command.py b/tools/u_boot_pylib/command.py index 54a7ee8e672..c44bed6acc0 100644 --- a/tools/u_boot_pylib/command.py +++ b/tools/u_boot_pylib/command.py @@ -66,7 +66,8 @@ class CommandResult: def run_pipe(pipe_list, infile=None, outfile=None, capture=False, capture_stderr=False, oneline=False, raise_on_error=True, cwd=None, - binary=False, output_func=None, stdin_data=None, **kwargs): + binary=False, output_func=None, stdin_data=None, + merge_stderr=False, **kwargs): """ Perform a command pipeline, with optional input/output filenames. @@ -87,6 +88,9 @@ def run_pipe(pipe_list, infile=None, outfile=None, capture=False, output_func (function): Output function to call with each output fragment (if it returns True the function terminates) stdin_data (str or None): Data to send to the first command's stdin + merge_stderr (bool): True to redirect stderr to stdout so that both + streams are interleaved in order. Use this for interactive + processes where output ordering matters. **kwargs: Additional keyword arguments to cros_subprocess.Popen() Returns: CommandResult object @@ -123,7 +127,9 @@ def run_pipe(pipe_list, infile=None, outfile=None, capture=False, kwargs['stdout'] = cros_subprocess.PIPE elif outfile: kwargs['stdout'] = open(outfile, 'wb') - if capture_stderr: + if merge_stderr: + kwargs['stderr'] = cros_subprocess.STDOUT + elif capture_stderr: kwargs['stderr'] = cros_subprocess.PIPE try: diff --git a/tools/u_boot_pylib/gitutil.py b/tools/u_boot_pylib/gitutil.py index 22414d250a9..8e45727b47a 100644 --- a/tools/u_boot_pylib/gitutil.py +++ b/tools/u_boot_pylib/gitutil.py @@ -563,7 +563,7 @@ send --cc-cmd cc-fname" cover p1 p2', 0) result = command.run_pipe( [cmd], capture=True, output_func=echo_output, - raise_on_error=False, cwd=cwd) + raise_on_error=False, cwd=cwd, merge_stderr=True) num_sent = result.stdout.count('Result: ') cmd_str = ' '.join([f'"{x}"' if ' ' in x and '"' not in x else x for x in cmd]) From patchwork Sun Mar 29 15:01:30 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2079 Return-Path: X-Original-To: u-boot-concept@u-boot.org Delivered-To: u-boot-concept@u-boot.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1774796529; bh=KsaqcAijLCdfgi+KGePcL0X07RnZ+rqQd2ifS+zGYmc=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=ClI6YDxauYew/lXu826yr0HuUXhAfxgwLsOpa+2hridgj6APXJHDZZWrsZweuVAYQ soGUwjzfyHeDoKgUDCOP5r03X/TVSEGYB2apmQgDry6Jd1TrupMhxLYrT76nwcBn5v Ves5ZFRGIE3KI2cZIHHet2TxOLStCxlXhgx7jqfiq5e4QX6ydVx2doaIpEXLFl0WGd UoYb9DmNRngMH1A5dZYhzsyMyc44QLGo715eRBe8K4iP6rW+ITX1zyU3t7HpKINTSG MwlQ/hPpV2HpNWpUrM2I2sPrpCeMaYK9ksNRth5oammx4eObmPVXES8fLKl2gSBuv5 eBzLEtwB5rI6w== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id DE82D6A2AB for ; Sun, 29 Mar 2026 09:02:09 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10024) with ESMTP id K8MY6o8znnDe for ; Sun, 29 Mar 2026 09:02:09 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1774796528; bh=KsaqcAijLCdfgi+KGePcL0X07RnZ+rqQd2ifS+zGYmc=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=YvbCFnbv8cf7I+k6rGokVTotONCKSxREXnZ2zI1jw1jp8IUEZVFfn+vNXADTzD3bw Hs+bjTCTlNiH8S/IyNw0N/RWH3sE6zqpvdg4HEY122FK1RHcZ9f4QdAVASaPkwi/U2 ZAtz46R7nk+Go7fiAtCvsluzw0VVrhq4/nsflcKdWC9Bb2vOgIUrocHfYEqQcfAorr VuIWaTgONwemQRM7W+UEkcq9UHycsYI5iQ0w88MeIlACFLOWxLywairxycjcOkmB+7 AY8Gye6IaXQAkpTSgcn8GpPgIDUtf/jq6OWDHga0CfahoDAXCi/Es27lWWsr0AIUqz Vl+n2OBu4ahpA== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 95CE26A2D1 for ; Sun, 29 Mar 2026 09:02:08 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1774796527; bh=ADtTjEqkXjzDIsPRjer6Z0UzOWG/MDLmKRm2nkpds90=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=H7+ORbY8hqYoHtSIFk9fP9IaYm+RTjAUQXhnORuscn3osMygLZxAnypRJm8QipgyN vbsCYXVzvcdNDVxZZM43GMIW4RrCYK76aboeaolDJJ1OynabsEle2D45vCWmdSgviW qV8BAvcXPnDWdQF4PO/Inuk1QH6IQfFpxHBZRr1AGoJT0ocxA1XVspEFCA/mUECwmn aqlTciT1bwHe4zgt04lFp8okz7li/PO5galkpY1gGs25rWt8d9v2NpHBCw2kC1B5Bp JSjtuCYuCXrjGu9iQ/3uhRE5o70FmWQHz4b4TzncGS7ze/o0yNtVfQQan1SOt+NRLg Ev3XGVDyIvQaQ== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 42E136A2D2; Sun, 29 Mar 2026 09:02:07 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10026) with ESMTP id 2iWENlrVWTt7; Sun, 29 Mar 2026 09:02:07 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1774796523; bh=uxTTI+9yHFVRMJJLyb/t6RdHjZ2r9FrqrgKDPID3l6Y=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=sTYNzGkQIkBFx1V2r1N9xfmBYe0hnHUMbHvW6bBKsR2sUmVXbvo0twpszkuaq0NnO Ast9MZZRROSPeetza1ePZPPQCPzzAWHxeVh89cEJFuOJVnQSf+CsOLcC10H/VflIkL zd4nY+Md4C530aqGT2jVvMBb4A1IQdP30fWmjfYow5Gli5tunS1R7OfL9//8kcU/rT T707nBYYJpis10lC+HyqfREMkMDcsjkJVGJhPDoc9GwN9LS/5hoBHK3Rzm2dnaOgtj bP+5cGs1C8aiFVDxCCq798ZhTRBwSSrfXEryQlgDObIWU2YAW8tHwsAImxDVpxQTmy Zsn9zrYGs4KTg== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id D5EEB6A2D4; Sun, 29 Mar 2026 09:02:02 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Sun, 29 Mar 2026 09:01:30 -0600 Message-ID: <20260329150140.4095446-6-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260329150140.4095446-1-sjg@u-boot.org> References: <20260329150140.4095446-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: VA4XZUEAJTLC6RZWTFRQGEGDINKATY2Y X-Message-ID-Hash: VA4XZUEAJTLC6RZWTFRQGEGDINKATY2Y X-MailFrom: sjg@u-boot.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 05/11] patman: Use actual send count to determine likely_sent List-Id: Discussion and patches related to U-Boot Concept Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Simon Glass Update send.py and func_test.py to handle the new (cmd, num_sent) tuple returned by gitutil.email_patches(). Use num_sent > 0 instead of the old heuristic, so workflow entries are only created when patches were actually sent. Signed-off-by: Simon Glass --- tools/patman/func_test.py | 2 +- tools/patman/send.py | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/tools/patman/func_test.py b/tools/patman/func_test.py index d029181765c..b1f37577643 100644 --- a/tools/patman/func_test.py +++ b/tools/patman/func_test.py @@ -230,7 +230,7 @@ class TestFunctional(unittest.TestCase, TestCommon): cc_file = series.MakeCcFile(process_tags, cover_fname, not ignore_bad_tags, add_maintainers, None, get_maintainer_script, alias) - cmd = gitutil.email_patches( + cmd, _ = gitutil.email_patches( series, cover_fname, args, dry_run, not ignore_bad_tags, cc_file, alias, in_reply_to=in_reply_to, thread=None) series.ShowActions(args, cmd, process_tags, alias) diff --git a/tools/patman/send.py b/tools/patman/send.py index eb9a8e0da2e..db7d1ae6e4e 100644 --- a/tools/patman/send.py +++ b/tools/patman/send.py @@ -99,8 +99,9 @@ def email_patches(col, series, cover_fname, patch_files, process_tags, its_a_go, # Email the patches out (giving the user time to check / cancel) cmd = '' + num_sent = 0 if its_a_go: - cmd = gitutil.email_patches( + cmd, num_sent = gitutil.email_patches( series, cover_fname, patch_files, dry_run, not ignore_bad_tags, cc_file, alias=settings.alias, in_reply_to=in_reply_to, thread=thread, smtp_server=smtp_server, identity=identity, @@ -115,7 +116,7 @@ def email_patches(col, series, cover_fname, patch_files, process_tags, its_a_go, print(col.build(col.RED, "Email would not be sent")) os.remove(cc_file) - return cmd + return cmd, num_sent def prepare_patches(col, branch, count, start, end, ignore_binary, signoff, @@ -206,11 +207,11 @@ def send(args, git_dir=None, cwd=None): print(f"Using sendemail identity '{identity}'") its_a_go = ok or args.ignore_errors - cmd = email_patches( + cmd, num_sent = email_patches( col, series, cover_fname, patch_files, args.process_tags, its_a_go, args.ignore_bad_tags, args.add_maintainers, args.get_maintainer_script, args.limit, args.dry_run, args.in_reply_to, args.thread, args.smtp_server, identity=identity, cwd=cwd) - return cmd and its_a_go and not args.dry_run + return num_sent > 0 From patchwork Sun Mar 29 15:01:31 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2080 Return-Path: X-Original-To: u-boot-concept@u-boot.org Delivered-To: u-boot-concept@u-boot.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1774796532; bh=hn9xGORctKuykAIBW2bYoZwdE0xdc6cu/VoIGaKkVpE=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=B5I60tk5q2wY+dlVzMP+Gz4zaKGxTI2z0L5q17n5ciObc5xwrGy7VI4Sv6tE1hOts 4OrQFcDGz3RhCuVct4tgxVDQKXtfjMdmnPQ/pw/QkORr2b5JWgfmjW5ajqEKQmTdXq +6nkqxj8I0fturUuaoZfET6R5jeTVO/VbYuc24om94xbxbfY1t3IpX1XUly6hfhDnr 3dxeLwkei/cZEQKg1tsRicvOrfSBXx/mFgF/NWK6ur/9ay8hXzwAwEW+6axZ+wrIik GQi93OQYNLB+lmQSfxpSQtRg5ok2RqjGCuCs2f+hZD3oglt0yTHIi6z0ZkpyE3hCDH Nnppu+vlFnNqg== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 32EA86A2DB for ; Sun, 29 Mar 2026 09:02:12 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10024) with ESMTP id uOAJOrS2RzcE for ; Sun, 29 Mar 2026 09:02:12 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1774796531; bh=hn9xGORctKuykAIBW2bYoZwdE0xdc6cu/VoIGaKkVpE=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=caeX85FOjEMpnEF8GAIB5G7BIpSm8Bd9QIV9b4LzvOxZy9KCfCFDnLYAz4riXz7FE eeWRD1jXoC6bBeSdrL3QeqlkK452nDbSq1lOrYWq0jbR+xN6o97Gk2g7dSyhX28oU+ GawBWefCipcs7QmKqviLOurYBU3sqKrNO+j7qM+V7p2B1zvWbB82tHhaS6KER7W8l0 Dx7fjo6fp19fSRf3YCYGjs1vUeEFXcLCljRGxh6a6LywB0M/U+mXGRFiOL2X57BnHZ cqjXeBe90l2zLp3MwEAE9OJSBRHjhB1ComnjAINYnjowVoemuwvyVkS/O/0V7BjQ6g hpIeW5MI9L+lw== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 7FB796A2CB for ; Sun, 29 Mar 2026 09:02:11 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1774796529; bh=nsXcvKAqXEHKCPb2RKsI1AZnkjjmjykpCPjldIHIq3g=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=vXSU0EX8zBPTzmNiHDs7UmRf+EdlfZjGyyda34iB03R+k+hlJ2hymgMz2bXYpudnv eb8LEmAF4dmd342eU6i8dMVFK/ceyPVIzTiu9uEPFSINjHH0RE6buuRHZHAhjWJxUn ILEwTQxqJ4rAoYwfII3wAfvF9luzS+uuU5143JTz25UPqRlWlNwPi/Hn2BY0kGEFRE 9eefarfbxcjQ2ICb9ThrYvujHPez5FWamLwgsqi2deZ+oHX0wnrSVEMPLbxmufAUAV sym2UIDwZ1blY68xUkn/YkIS17OP+sMGk/NJ7/3PB5qFEIfoXR2wz2QUh5HAJvEHVd cIHcUGmrQmE3A== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id BE4E36A2E2; Sun, 29 Mar 2026 09:02:09 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10026) with ESMTP id uLIroXcTPMVP; Sun, 29 Mar 2026 09:02:09 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1774796523; bh=bgwMxGXUxZHYZuNgyHV9yv54xYV407XXUjcy/DODHq0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=iZIH7rY3Ieza1VLU0bBjL1ZSUAlYPI7FjJ4f++1OHo0rw+I7tPXEVO/6WmCjFHZgr XYx+XeXO3i4wM6qdjPIGr6H7hLKT0h6bQwnlyadHc3j9snA/nNpalyRGfl3KyZuUvw ijuFjoBi+nUSMYOMpbC2SebHkyfJDBsajxBZ3FEv2Cyh/Emctn+m01OBU+RRIivDIM AqdIhdX3zTA7IhBogByYabvYxNw/Eu+tHrc2C4t0cII2uIZ4guMcQ2zY4pnJVrasNb GxeGABuuDKfE5rfgumbJ1xROFCbnHOpTnnfG4akWJmDsf0tyzCwDpETxXoN6l/gdSw cZUbN/zuL5NnA== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id AF26F6A2D3; Sun, 29 Mar 2026 09:02:03 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Sun, 29 Mar 2026 09:01:31 -0600 Message-ID: <20260329150140.4095446-7-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260329150140.4095446-1-sjg@u-boot.org> References: <20260329150140.4095446-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: 3WEJFSKGOA5S3WXZARNJPOTGEOBM3VTP X-Message-ID-Hash: 3WEJFSKGOA5S3WXZARNJPOTGEOBM3VTP X-MailFrom: sjg@u-boot.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 06/11] patman: Add workflow table to the database List-Id: Discussion and patches related to U-Boot Concept Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Simon Glass Add an extensible workflow table for tracking series-related tasks. Each entry has a type, series reference, timestamp and an archived flag for preserving history. The first type is 'todo' for marking series that need attention after a certain date. Add database methods for adding, archiving and querying workflow entries, with filtering by type and due date. Archived entries are kept for history but excluded from active queries. Signed-off-by: Simon Glass --- tools/patman/database.py | 91 +++++++++++++++++++++++++++++++++++- tools/patman/test_cseries.py | 48 ++++++++++++++++++- tools/patman/workflow.py | 13 ++++++ 3 files changed, 150 insertions(+), 2 deletions(-) create mode 100644 tools/patman/workflow.py diff --git a/tools/patman/database.py b/tools/patman/database.py index e1ec0dc00e2..edb7d116c33 100644 --- a/tools/patman/database.py +++ b/tools/patman/database.py @@ -19,7 +19,7 @@ from u_boot_pylib import tout from patman.series import Series # Schema version (version 0 means there is no database yet) -LATEST = 5 +LATEST = 6 # Information about a series/version record SerVer = namedtuple( @@ -198,6 +198,21 @@ class Database: # pylint:disable=R0904 self.cur.execute('ALTER TABLE upstream ADD COLUMN no_tags BIT') self.cur.execute('ALTER TABLE ser_ver ADD COLUMN desc') + def _migrate_to_v6(self): + """Add workflow table for tracking todos and other workflow items + + Fields: + id: Auto-increment primary key + type: Workflow-entry type, e.g. 'todo' or 'sent' + series_id: Foreign key referencing series.id + timestamp: Due/event time as 'YYYY-MM-DD HH:MM:SS' + archived: 0 for active entries, 1 for archived (soft-delete) + """ + self.cur.execute( + 'CREATE TABLE workflow (id INTEGER PRIMARY KEY AUTOINCREMENT,' + 'type, series_id INTEGER, timestamp, archived BIT,' + 'FOREIGN KEY (series_id) REFERENCES series (id))') + def migrate_to(self, dest_version): """Migrate the database to the selected version @@ -226,6 +241,8 @@ class Database: # pylint:disable=R0904 self._migrate_to_v4() elif version == 5: self._migrate_to_v5() + elif version == 6: + self._migrate_to_v6() # Save the new version if we have a schema_version table if version > 1: @@ -1041,3 +1058,75 @@ class Database: # pylint:disable=R0904 if not recs: return None return recs[0] + + # workflow functions + + def workflow_add(self, wtype, series_id, timestamp): + """Add a workflow entry + + Args: + wtype (str): Workflow type, e.g. 'todo' + series_id (int): ID of the series + timestamp (str): Timestamp string, e.g. '2025-01-15 10:30:00' + """ + self.execute( + 'INSERT INTO workflow (type, series_id, timestamp, archived) ' + 'VALUES (?, ?, ?, 0)', (wtype, series_id, timestamp)) + + def workflow_archive(self, wtype, series_id): + """Archive active workflow entries for a given type and series + + Args: + wtype (str): Workflow type, e.g. 'todo' + series_id (int): ID of the series + """ + self.execute( + 'UPDATE workflow SET archived = 1 ' + 'WHERE type = ? AND series_id = ? AND archived = 0', + (wtype, series_id)) + + def workflow_get(self, wtype, series_id): + """Get the active workflow entry for a given type and series + + Args: + wtype (str): Workflow type, e.g. 'todo' + series_id (int): ID of the series + + Return: + str or None: Timestamp string if found, else None + """ + res = self.execute( + 'SELECT timestamp FROM workflow ' + 'WHERE type = ? AND series_id = ? AND archived = 0', + (wtype, series_id)) + rec = res.fetchone() + if rec: + return rec[0] + return None + + def workflow_get_by_type(self, wtype, before=None): + """Get active workflow entries for a given type, joined with series + + Args: + wtype (str): Workflow type, e.g. 'todo' + before (str or None): If set, only return entries where + timestamp <= this value + + Return: + list of tuple: + int: series ID + str: series name + str: series description + str: timestamp + """ + query = ('SELECT s.id, s.name, s.desc, w.timestamp ' + 'FROM workflow w ' + 'JOIN series s ON w.series_id = s.id ' + 'WHERE w.type = ? AND w.archived = 0 AND s.archived = 0') + params = [wtype] + if before is not None: + query += ' AND w.timestamp <= ?' + params.append(before) + query += ' ORDER BY w.timestamp' + res = self.execute(query, params) + return res.fetchall() diff --git a/tools/patman/test_cseries.py b/tools/patman/test_cseries.py index c0beb128265..798673e09cb 100644 --- a/tools/patman/test_cseries.py +++ b/tools/patman/test_cseries.py @@ -26,6 +26,7 @@ from patman import database from patman import patchstream from patman.patchwork import Patchwork from patman.test_common import TestCommon +from patman import workflow as wf HASH_RE = r'[0-9a-f]+' #pylint: disable=protected-access @@ -3556,7 +3557,7 @@ Date: .* self.assertEqual(f'Update database to v{version}', out.getvalue().strip()) self.assertEqual(version, db.get_schema_version()) - self.assertEqual(5, database.LATEST) + self.assertEqual(6, database.LATEST) def test_migrate_future_version(self): """Test that a database newer than patman is rejected""" @@ -4130,3 +4131,48 @@ Date: .* self.run_args('series', '-s', 'first', 'version-change', '--new-version', '3', pwork=True) method.assert_called_once_with('first', None, 3, dry_run=False) + + def test_workflow_db_methods(self): + """Test workflow database methods""" + cser = self.get_cser() + with terminal.capture(): + cser.add('first', 'my description', allow_unmarked=True) + + ser = cser.get_series_by_name('first') + + # Initially there is no workflow entry + self.assertIsNone(cser.db.workflow_get('todo', ser.idnum)) + + # Add a todo entry + cser.db.workflow_add('todo', ser.idnum, '2025-03-15 10:00:00') + cser.commit() + + # Should be able to read it back + ts = cser.db.workflow_get('todo', ser.idnum) + self.assertEqual('2025-03-15 10:00:00', ts) + + # Get by type should return it + entries = cser.db.workflow_get_by_type('todo') + self.assertEqual(1, len(entries)) + entry = entries[0] + self.assertEqual(ser.idnum, entry[0]) + self.assertEqual('first', entry[1]) + self.assertEqual('my description', entry[2]) + self.assertEqual('2025-03-15 10:00:00', entry[3]) + + # Get by type with before filter + entries = cser.db.workflow_get_by_type( + 'todo', before='2025-03-14 00:00:00') + self.assertEqual(0, len(entries)) + entries = cser.db.workflow_get_by_type( + 'todo', before='2025-03-16 00:00:00') + self.assertEqual(1, len(entries)) + + # Archive it - should no longer be active, but still in the table + cser.db.workflow_archive('todo', ser.idnum) + cser.commit() + self.assertIsNone(cser.db.workflow_get('todo', ser.idnum)) + res = cser.db.execute( + 'SELECT archived FROM workflow WHERE series_id = ?', + (ser.idnum,)) + self.assertEqual(1, res.fetchone()[0]) diff --git a/tools/patman/workflow.py b/tools/patman/workflow.py new file mode 100644 index 00000000000..37644a5de88 --- /dev/null +++ b/tools/patman/workflow.py @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright 2025 Google LLC +# + +"""Workflow types and operations for patman series management""" + +import enum + + +class Wtype(enum.StrEnum): + """Types of workflow entry""" + TODO = 'todo' From patchwork Sun Mar 29 15:01:32 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2081 Return-Path: X-Original-To: u-boot-concept@u-boot.org Delivered-To: u-boot-concept@u-boot.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1774796536; bh=YQdic/hRB1EqzEQO7jDcOhHy3J4wJqcx6huuYJ3a6og=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=A3AjbWWfFN51SrBsY+yRlA0yRE/Vmhe1AX+3KKliFX9IkWcdKpSHp3EYPjvWFuUMX 5dIgOJd9KvF34kF/nE4+trTM8+rPQEMOOyI0LjDlA3+v4+Ujond4BoRkMSg18IBXg6 N+q776c4LpPvZsPvPOFTjeYaozgth+c4Mb6pj75rExECZxofPhXs1fDHAsnkzOPJNd EzW4PaGitvtNS5fSh2ORlZ7KlsvVs1d8tvYMsRSZUDLcnoWgunvYXoo/Qo7NxDKhSS 2liKwfHuARyNxXL9t16Xp02qtL8zYS0gRDX7dcOyfFEITIL5M5aKch3iWQ0Bx82geO 0bT9h82xh4byQ== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 1A03B6A02E for ; Sun, 29 Mar 2026 09:02:16 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10024) with ESMTP id kDEU39b0P1bY for ; Sun, 29 Mar 2026 09:02:16 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1774796536; bh=YQdic/hRB1EqzEQO7jDcOhHy3J4wJqcx6huuYJ3a6og=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=A3AjbWWfFN51SrBsY+yRlA0yRE/Vmhe1AX+3KKliFX9IkWcdKpSHp3EYPjvWFuUMX 5dIgOJd9KvF34kF/nE4+trTM8+rPQEMOOyI0LjDlA3+v4+Ujond4BoRkMSg18IBXg6 N+q776c4LpPvZsPvPOFTjeYaozgth+c4Mb6pj75rExECZxofPhXs1fDHAsnkzOPJNd EzW4PaGitvtNS5fSh2ORlZ7KlsvVs1d8tvYMsRSZUDLcnoWgunvYXoo/Qo7NxDKhSS 2liKwfHuARyNxXL9t16Xp02qtL8zYS0gRDX7dcOyfFEITIL5M5aKch3iWQ0Bx82geO 0bT9h82xh4byQ== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 057026A2AB for ; Sun, 29 Mar 2026 09:02:16 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1774796534; bh=apTv5mD/Ad4Nel6jSxR7AMs/zATLCzdWjxM2iV/Zdt0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=SUi9mt75WNiTLI3cWvXJA6ZhmXSFgqUokYBXAl8uOmilP0Gz6aQxwpipZGTGcx4UO eBDplqmtqReNbRHn4LOI7BS4UJg7ligYHIhegcWymxSOoC6du0A5/hf0jMNXbwUy5j HFj5kCuANT0NC0hZ/6SL0y2LRZ/dRQ7xsd3VcduSrreyRsNtREAglWBk/botEBnRoQ L/zXE56mPw0peYWrj86tVaX9RCFUq3AlTOFSRbTewFIWgdMSVYOq/WCt2A5kcSjqqc HY3sgA+UqdrHn57xuvBnJMwmaHIZp/LiqNS/luniv1WWr9lINB95yP72zFiJck9IsP E1YQQ8HEF3Gtg== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 853786A2AB; Sun, 29 Mar 2026 09:02:14 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10026) with ESMTP id mzPdx3un_h1B; Sun, 29 Mar 2026 09:02:14 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1774796528; bh=PgYH/AR3CcX2VbbHpS0DvsMS/ufEJBIc5nVRcb+eWJw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=JsWxoSgM5D2uE9Nr/vDJIwMkcAirBKZrO1/pBeUSpDPBZ2xX8D5D2KFcvj4xYBsHl covrwkulQt9fUHqwOQW7BloSHhncvEeQTvwpXto2yS1GdjGoWu1WyMNtvDXDPvXfPQ 6kW4BNlIHG9beINZ/dQzrsKouI5AHNwG+uoZAig0Gcl9+3mj6J0l2bAboho+hLSDro ktlA2oIfXSL/rO+56JmM9LHnDsHIXoy2vGfi6gFM87QlWsAoLixFtCilFHSg24OoJ1 VeYP/d/lXPFp1CiyXzuSSIS74xqE9iDc9xsN+qzXY/Tr0hXJaji3vsjpVpSW8CQfvz kv4jV/T46ofhA== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id CB8056A02E; Sun, 29 Mar 2026 09:02:07 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Sun, 29 Mar 2026 09:01:32 -0600 Message-ID: <20260329150140.4095446-8-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260329150140.4095446-1-sjg@u-boot.org> References: <20260329150140.4095446-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: 6QUR7CFFFH6RQ4EVGYHKHYNH4TDLIKAR X-Message-ID-Hash: 6QUR7CFFFH6RQ4EVGYHKHYNH4TDLIKAR X-MailFrom: sjg@u-boot.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 07/11] patman: Add series-todo workflow feature List-Id: Discussion and patches related to U-Boot Concept Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Simon Glass Add functions to mark a series for attention after a given number of days, clear the marker, and list which series are due. This is useful for tracking when to follow up on sent patch series. The todo list shows relative due dates (e.g. '3d overdue', 'in 7d', 'today') and a [todo] marker appears in series summary output when a series is due. Signed-off-by: Simon Glass --- tools/patman/cser_helper.py | 7 ++- tools/patman/cseries.py | 1 - tools/patman/test_cseries.py | 97 ++++++++++++++++++++++++++++++++++++ tools/patman/workflow.py | 63 +++++++++++++++++++++++ 4 files changed, 166 insertions(+), 2 deletions(-) diff --git a/tools/patman/cser_helper.py b/tools/patman/cser_helper.py index 0b9f670ca25..7979deda6dc 100644 --- a/tools/patman/cser_helper.py +++ b/tools/patman/cser_helper.py @@ -28,6 +28,7 @@ from patman.database import Database, Pcommit, SerVer from patman import patchwork from patman.series import Series from patman import status +from patman.workflow import Wtype # Tag to use for Change IDs @@ -1552,7 +1553,11 @@ class CseriesHelper: if val in states: state = val state_str, pad = self._build_col(state, base_str=name) - print(f"{state_str}{pad} {stats.rjust(6)} {desc}") + marker = '' + ts = self.db.workflow_get(Wtype.TODO, ser.idnum) + if ts and ts <= self.get_now().strftime('%Y-%m-%d %H:%M:%S'): + marker = ' [todo]' + print(f"{state_str}{pad} {stats.rjust(6)} {desc}{marker}") def _series_max_version(self, idnum): """Find the latest version of a series diff --git a/tools/patman/cseries.py b/tools/patman/cseries.py index 716d3c7aa88..8f159a8308f 100644 --- a/tools/patman/cseries.py +++ b/tools/patman/cseries.py @@ -7,7 +7,6 @@ import asyncio from collections import OrderedDict, defaultdict - import pygit2 from u_boot_pylib import cros_subprocess diff --git a/tools/patman/test_cseries.py b/tools/patman/test_cseries.py index 798673e09cb..b4dce4cb853 100644 --- a/tools/patman/test_cseries.py +++ b/tools/patman/test_cseries.py @@ -4176,3 +4176,100 @@ Date: .* 'SELECT archived FROM workflow WHERE series_id = ?', (ser.idnum,)) self.assertEqual(1, res.fetchone()[0]) + + def test_workflow_todo(self): + """Test setting and clearing a todo""" + cser = self.get_cser() + with terminal.capture(): + cser.add('first', 'my description', allow_unmarked=True) + + cser.fake_now = datetime(2025, 3, 1, 12, 0, 0) + ser = cser.get_series_by_name('first') + + # Set a todo for 7 days + with terminal.capture() as (out, _): + wf.todo(cser,'first', 7) + self.assertIn('2025-03-08 12:00:00', out.getvalue()) + + # Check the DB entry + ts = cser.db.workflow_get('todo', ser.idnum) + self.assertEqual('2025-03-08 12:00:00', ts) + + # Replacing the todo should work + with terminal.capture() as (out, _): + wf.todo(cser,'first', 14) + self.assertIn('2025-03-15 12:00:00', out.getvalue()) + ts = cser.db.workflow_get('todo', ser.idnum) + self.assertEqual('2025-03-15 12:00:00', ts) + + # Clear it + with terminal.capture() as (out, _): + wf.todo_clear(cser,'first') + self.assertIn('Todo cleared', out.getvalue()) + self.assertIsNone(cser.db.workflow_get('todo', ser.idnum)) + + def test_workflow_todo_list(self): + """Test listing todos""" + cser = self.get_cser() + with terminal.capture(): + cser.add('first', 'my description', allow_unmarked=True) + cser.add('second', 'board stuff', allow_unmarked=True) + + cser.fake_now = datetime(2025, 3, 10, 12, 0, 0) + + # Set todos: first is due, second is in the future + with terminal.capture(): + wf.todo(cser,'first', 0) + wf.todo(cser,'second', 7) + + # Default list shows only due entries + with terminal.capture() as (out, _): + wf.todo_list(cser,show_all=False) + lines = out.getvalue().splitlines() + self.assertEqual(3, len(lines)) + self.assertIn('first', lines[2]) + self.assertIn('today', lines[2]) + + # --all shows all entries + with terminal.capture() as (out, _): + wf.todo_list(cser,show_all=True) + lines = out.getvalue().splitlines() + self.assertEqual(4, len(lines)) + self.assertIn('first', lines[2]) + self.assertIn('today', lines[2]) + self.assertIn('second', lines[3]) + self.assertIn('in 7d', lines[3]) + + # No todos + with terminal.capture(): + wf.todo_clear(cser,'first') + wf.todo_clear(cser,'second') + with terminal.capture() as (out, _): + wf.todo_list(cser,show_all=False) + self.assertIn('No todos due', out.getvalue()) + + def test_workflow_summary_marker(self): + """Test that [todo] shows in series summary""" + cser = self.get_cser() + with terminal.capture(): + cser.add('first', 'my description', allow_unmarked=True) + + cser.fake_now = datetime(2025, 3, 10, 12, 0, 0) + + # Set a todo that is already due + with terminal.capture(): + wf.todo(cser,'first', 0) + + # Summary should show [todo] + with terminal.capture() as (out, _): + cser.summary(None) + self.assertIn('[todo]', out.getvalue()) + + # Set a todo in the future + with terminal.capture(): + wf.todo(cser,'first', 14) + + # Summary should NOT show [todo] + with terminal.capture() as (out, _): + cser.summary(None) + self.assertNotIn('[todo]', out.getvalue()) diff --git a/tools/patman/workflow.py b/tools/patman/workflow.py index 37644a5de88..029d63cf6a4 100644 --- a/tools/patman/workflow.py +++ b/tools/patman/workflow.py @@ -5,9 +5,72 @@ """Workflow types and operations for patman series management""" +from datetime import datetime, timedelta import enum class Wtype(enum.StrEnum): """Types of workflow entry""" TODO = 'todo' + + +def todo(cser, series, days): + """Mark a series as a todo item after a number of days + + Args: + cser (CseriesHelper): Series helper with open database + series (str): Name of series to use, or None for current branch + days (int): Number of days from now to mark as due + """ + ser = cser._parse_series(series) + cser.db.workflow_archive(Wtype.TODO, ser.idnum) + when = cser.get_now() + timedelta(days=days) + ts = when.strftime('%Y-%m-%d %H:%M:%S') + cser.db.workflow_add(Wtype.TODO, ser.idnum, ts) + cser.commit() + print(f"Series '{ser.name}' marked for todo on {ts}") + + +def todo_clear(cser, series): + """Clear the todo marker for a series + + Args: + cser (CseriesHelper): Series helper with open database + series (str): Name of series to use, or None for current branch + """ + ser = cser._parse_series(series) + cser.db.workflow_archive(Wtype.TODO, ser.idnum) + cser.commit() + print(f"Todo cleared for series '{ser.name}'") + + +def todo_list(cser, show_all): + """List series that are due (or scheduled) for attention + + Args: + cser (CseriesHelper): Series helper with open database + show_all (bool): True to show all scheduled todos, not just + those that are due + """ + now = cser.get_now().strftime('%Y-%m-%d %H:%M:%S') + before = None if show_all else now + entries = cser.db.workflow_get_by_type(Wtype.TODO, before=before) + if not entries: + if show_all: + print('No todos scheduled') + else: + print('No todos due') + return + print(f"{'Series':17} {'Due':>14} Description") + print(f"{'-' * 17} {'-' * 14} {'-' * 30}") + for _sid, name, desc, ts in entries: + when = datetime.strptime(ts, '%Y-%m-%d %H:%M:%S') + delta = when - cser.get_now() + days = delta.days + if days < 0: + due = f'{-days}d overdue' + elif days == 0: + due = 'today' + else: + due = f'in {days}d' + print(f"{name:17} {due:>14} {desc}") From patchwork Sun Mar 29 15:01:33 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2082 Return-Path: X-Original-To: u-boot-concept@u-boot.org Delivered-To: u-boot-concept@u-boot.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1774796540; bh=uY2Xq2CJUGKLs3XuF7zQMp4J6xoAP6WiTwE+8WgrQPg=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=cc/yF6uHOfh73FFJzDyk+RuGIA5RnOkzFM7AD06lJjrZ1O++AKmY3dccEmd0PGpSB ajVavWdyd1MI+2D0G/h6bEr2I+Pe4y/9l3Xtr4Nk7BRVcwvZD0UIe24OUw8YDpDNhC NTQ24eBZKzFBS2cWv8n5+vHg8Hvq6EwVDhi0Qh551n117JBTgr2PEvorGjBCl+iRnq aLzn9leqrHx4pQjSZEu46qw3Px6TRPgP/eHTl1X9RurHphed/fGRVaxJmCvnB7SwDn MWp6GnDfaJQr0FpzNe9J6kgSG7B7D+o8DRnlMyaRmoNgqeW0y5Zd9UjXCBlz/NczwA fxFggWfOK71vA== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id AF6F56A2D1 for ; Sun, 29 Mar 2026 09:02:20 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10024) with ESMTP id nvnQDgnh0Idl for ; Sun, 29 Mar 2026 09:02:20 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1774796540; bh=uY2Xq2CJUGKLs3XuF7zQMp4J6xoAP6WiTwE+8WgrQPg=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=cc/yF6uHOfh73FFJzDyk+RuGIA5RnOkzFM7AD06lJjrZ1O++AKmY3dccEmd0PGpSB ajVavWdyd1MI+2D0G/h6bEr2I+Pe4y/9l3Xtr4Nk7BRVcwvZD0UIe24OUw8YDpDNhC NTQ24eBZKzFBS2cWv8n5+vHg8Hvq6EwVDhi0Qh551n117JBTgr2PEvorGjBCl+iRnq aLzn9leqrHx4pQjSZEu46qw3Px6TRPgP/eHTl1X9RurHphed/fGRVaxJmCvnB7SwDn MWp6GnDfaJQr0FpzNe9J6kgSG7B7D+o8DRnlMyaRmoNgqeW0y5Zd9UjXCBlz/NczwA fxFggWfOK71vA== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 9EDF46A2AB for ; Sun, 29 Mar 2026 09:02:20 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1774796538; bh=YM5luIkKe4ctAkGcKjSAe+36+ulFh6WFgWZxB4c5a7Y=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Uan8tJ2zi975haNucV8MpwGiFWRhqyGb95UHfwrcqHVE3dIYyLEfoB2k8YQtu+nc0 Tvfh+jNttteMUpZlwHYxbPrWQD8goWDCbKqtKp0i2UZaGZpoBxuXbunTehIub9ml/i BZC5sTIL5YK9FuCi+uf7N/k4b+9O4zODmPdobsOQ34oodDQAKBk8cDszWPHTFtQUcm T7xkUy1JzjkusruejBWCTxfZJzcJ9frhE2wOgS5ewNlXI9DtavEUS5lIBMwCuXPTyp 81npoXY5wmt2x/HTXTExlQlQFAgbQ3BXXaiJ0JyX/x+8R7obhhl8fHmd6GJlLGsWOz pllv4BH9wM1eA== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id A9DCD6A02E; Sun, 29 Mar 2026 09:02:18 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10026) with ESMTP id zaBCiQtyWYXb; Sun, 29 Mar 2026 09:02:18 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1774796532; bh=rQCMEigTSz96J/D8r1PJw78SjG/ply5+dGyKMi4pZCM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ontNVA29+NXtyKGG1nURaauY4ojPqST8WktH7PYwopkk+S6LDwhSTOQePjOac5K2C LHCf2HJ5JXv582UW9yDXhpiDqleXcV3KKOQ8eKkHHwRxptVUCfoNwIWYQNmv9OGbPC v96X5QVeRsvrOMXPumuYp71Wvx26joqayfbOnIIcNW1Nano8jAj1HGjcc1RpSJCjZU oLxr+pNvQJm5My7ZvFdbI/2IfJGfuHkkpozQRlu96KALyyHNGoFio9DqO8Sk2secx9 C/VbEBvPEK7u7GihVjSEdyYE11nEYqeB0wOEqRWFGi6nIHx9UEYuH+HSM1Q7pvMX28 7PXA+LCSqwgHA== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 8B2E56A2D3; Sun, 29 Mar 2026 09:02:12 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Sun, 29 Mar 2026 09:01:33 -0600 Message-ID: <20260329150140.4095446-9-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260329150140.4095446-1-sjg@u-boot.org> References: <20260329150140.4095446-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: ZZBITPOTIGFPHUD6PFAJE6EBTFMCCD6F X-Message-ID-Hash: ZZBITPOTIGFPHUD6PFAJE6EBTFMCCD6F X-MailFrom: sjg@u-boot.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 08/11] patman: Add 'workflow' command with todo subcommands List-Id: Discussion and patches related to U-Boot Concept Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Simon Glass Wire the workflow todo feature into the command line with a new 'workflow' (alias 'wf') top-level command. Subcommands: patman wf todo [-s SERIES] [DAYS] - mark series for todo patman wf todo [-s SERIES] --clear - clear the todo marker patman wf todo-list [--all] - list due (or all) todos Signed-off-by: Simon Glass --- tools/patman/cmdline.py | 32 +++++++++++++++++++++++++++++++- tools/patman/control.py | 29 +++++++++++++++++++++++++++++ tools/patman/test_cseries.py | 20 ++++++++++++++++++++ 3 files changed, 80 insertions(+), 1 deletion(-) diff --git a/tools/patman/cmdline.py b/tools/patman/cmdline.py index 5bf917e4712..3628c12459c 100644 --- a/tools/patman/cmdline.py +++ b/tools/patman/cmdline.py @@ -26,6 +26,7 @@ ALIASES = { 'status': ['st'], 'patchwork': ['pw'], 'upstream': ['us'], + 'workflow': ['wf'], # Subcommand aliases 'archive': ['ar'], @@ -35,6 +36,7 @@ ALIASES = { 'open': ['o'], 'progress': ['p', 'pr', 'prog'], 'rm-version': ['rmv'], + 'todo-list': ['tl'], 'unarchive': ['unar'], } @@ -476,6 +478,32 @@ def add_upstream_subparser(subparsers): return upstream +def add_workflow_subparser(subparsers): + """Add the 'workflow' subparser + + Args: + subparsers (argparse action): Subparser parent + + Return: + ArgumentParser: workflow subparser + """ + workflow = subparsers.add_parser('workflow', aliases=ALIASES['workflow'], + help='Manage workflow items') + workflow_subparsers = workflow.add_subparsers(dest='subcmd') + todo = workflow_subparsers.add_parser('todo') + todo.add_argument('-s', '--series', help='Name of series') + todo.add_argument('days', nargs='?', type=int, default=14, + help='Number of days until due (default: 14)') + todo.add_argument('--clear', action='store_true', + help='Clear the todo marker instead of setting it') + + tlist = workflow_subparsers.add_parser('todo-list', + aliases=ALIASES['todo-list']) + tlist.add_argument('--all', action='store_true', dest='show_all', + help='Show all scheduled todos, not just due ones') + return workflow + + def setup_parser(): """Set up command-line parser @@ -518,6 +546,7 @@ def setup_parser(): series = add_series_subparser(subparsers) add_status_subparser(subparsers) upstream = add_upstream_subparser(subparsers) + workflow = add_workflow_subparser(subparsers) # Only add the 'test' action if the test data files are available. if HAS_TESTS: @@ -530,6 +559,7 @@ def setup_parser(): 'series': series, 'patchwork': patchwork, 'upstream': upstream, + 'workflow': workflow, } return parsers @@ -578,7 +608,7 @@ def parse_args(argv=None, config_fname=None, parsers=None): args.cmd = full if 'subcmd' in args and args.subcmd in aliases: args.subcmd = full - if args.cmd in ['series', 'upstream', 'patchwork'] and not args.subcmd: + if args.cmd in ['series', 'upstream', 'patchwork', 'workflow'] and not args.subcmd: parser.parse_args([args.cmd, '--help']) return args diff --git a/tools/patman/control.py b/tools/patman/control.py index cabd2138bb3..fe012f1a21b 100644 --- a/tools/patman/control.py +++ b/tools/patman/control.py @@ -344,6 +344,33 @@ def patchwork(args, test_db=None, pwork=None): finally: cser.close_database() +def do_workflow(args, test_db=None): + """Process a 'workflow' subcommand + + Args: + args (Namespace): Arguments to process + test_db (str or None): Directory containing the test database, None to + use the normal one + """ + from patman import cseries + from patman import workflow + + cser = cseries.Cseries(test_db) + try: + cser.open_database() + if args.subcmd == 'todo': + if args.clear: + workflow.todo_clear(cser, args.series) + else: + workflow.todo(cser, args.series, args.days) + elif args.subcmd == 'todo-list': + workflow.todo_list(cser, args.show_all) + else: + raise ValueError(f"Unknown workflow subcommand '{args.subcmd}'") + finally: + cser.close_database() + + def do_patman(args, test_db=None, pwork=None, cser=None): """Process a patman command @@ -392,6 +419,8 @@ def do_patman(args, test_db=None, pwork=None, cser=None): upstream(args, test_db) elif args.cmd == 'patchwork': patchwork(args, test_db, pwork) + elif args.cmd == 'workflow': + do_workflow(args, test_db) except Exception as exc: terminal.tprint(f'patman: {type(exc).__name__}: {exc}', colour=terminal.Color.RED) diff --git a/tools/patman/test_cseries.py b/tools/patman/test_cseries.py index b4dce4cb853..8fcdbd340fb 100644 --- a/tools/patman/test_cseries.py +++ b/tools/patman/test_cseries.py @@ -4273,3 +4273,23 @@ Date: .* with terminal.capture() as (out, _): cser.summary(None) self.assertNotIn('[todo]', out.getvalue()) + + def test_workflow_todo_cmdline(self): + """Test todo via the command line""" + cser = self.get_cser() + with terminal.capture(): + cser.add('first', 'my description', allow_unmarked=True) + + # Test via command line + self.db_close() + with terminal.capture() as (out, _): + self.run_args('workflow', 'todo', '-s', 'first', '7') + self.assertIn('marked for todo', out.getvalue()) + + with terminal.capture() as (out, _): + self.run_args('workflow', 'todo', '-s', 'first', '--clear') + self.assertIn('Todo cleared', out.getvalue()) + + with terminal.capture() as (out, _): + self.run_args('wf', 'todo-list') + self.assertIn('No todos due', out.getvalue()) From patchwork Sun Mar 29 15:01:34 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2083 Return-Path: X-Original-To: u-boot-concept@u-boot.org Delivered-To: u-boot-concept@u-boot.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1774796545; bh=gDi3/0qox4MDbiZZ1tE8Ry119Ko95KezgGUe4sl9anA=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=CMeh3J+OkLrcd8r76Bol8utAa+yO2HTXaCv7hV07g2Ii3Bv7/NUWjCNK50f9BsfmW JNy9wZG30ZVx0Zk5UN/KXBD/SIHrt4yb6oF5tShLKYLUBwEGA1cjDzVR4ekCV+vvNI yR1c7GH9MogAfxf6bO16CGM2z9ilgED6mpryAiVqR0t8txuVs6UJ8vPQlzTASq8vOD kdqFj38I9zFDbee6A1BxW12ZVO6y9QXPsHtepKCqssfmpfu5VaK6n32mIM9rwxuBas W9Y0hts0G7Bmc+cj1ITsH28mC2YcaCj27NcFB+hazurs1c6PHyAMfuORCPsJ4d5/UH G7NuI/8/fL9iw== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 3D0076A02E for ; Sun, 29 Mar 2026 09:02:25 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10024) with ESMTP id UrExjIap-qIi for ; Sun, 29 Mar 2026 09:02:25 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1774796545; bh=gDi3/0qox4MDbiZZ1tE8Ry119Ko95KezgGUe4sl9anA=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=CMeh3J+OkLrcd8r76Bol8utAa+yO2HTXaCv7hV07g2Ii3Bv7/NUWjCNK50f9BsfmW JNy9wZG30ZVx0Zk5UN/KXBD/SIHrt4yb6oF5tShLKYLUBwEGA1cjDzVR4ekCV+vvNI yR1c7GH9MogAfxf6bO16CGM2z9ilgED6mpryAiVqR0t8txuVs6UJ8vPQlzTASq8vOD kdqFj38I9zFDbee6A1BxW12ZVO6y9QXPsHtepKCqssfmpfu5VaK6n32mIM9rwxuBas W9Y0hts0G7Bmc+cj1ITsH28mC2YcaCj27NcFB+hazurs1c6PHyAMfuORCPsJ4d5/UH G7NuI/8/fL9iw== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 2CF9B6A2AB for ; Sun, 29 Mar 2026 09:02:25 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1774796543; bh=HT99tAx9QIPfrE5rxQiTPGkPGYsjpF9McedZBFCyYeU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=aibVm9bykwMGGjQ+e97kx9eJLbnGNh8Tpl4OoXRe4aHoXLIUp3NpGsdAM9ww8tkmy JRAtib0IHB++2CkIfS3bbbr6p+qPmXTYmRe9F4txs+xfPqDvtpbmfdtGGB4JluZHzw JNl8noDCGX3pxiYdTMFFXvHoDJlYnrsrRLGcknZ/woOe8pgZcE9lp+k/dFOT6iqaie H1etNNbKBONWs9DRyHwqpRwpqQpcqpvmtvYe7RLuHOPgnfuXvvKiqI8DJP6bGYkHIN z1Ax4lExL/Tl3jO+2kMGWKmFsRtsZD2yXB2VMjzQ3buC87BKvoA0uqhHnXxraMx+rG pP2S5FV+ssLPg== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 366456A02E; Sun, 29 Mar 2026 09:02:23 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10026) with ESMTP id 3zVW4ern5Ham; Sun, 29 Mar 2026 09:02:23 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1774796537; bh=ZALzZ56kmzIQIH7QtkTwbzI9D0R1te3Tf4of0zBqWc0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=nfBNsfuiEqu/xPs3yL3jRqEtQWYR2HB2E3cVNDnH6/SBaFblSNr3kLNWDFnor2MoY bdmoUuPmRJbpFQY+zxbSK0R7TJTKJdrEWlld6nPnsBDV44HDyeQNMpwNCzbLCmGRFb LVO2fAeugxa8mwZA/1EhUNzCbhzLlXU1+FtaZ+YfXNxWms4vWGb4CRLGDJr2TJ8Xrg q6gUW6k8f+9TA9l8GkcQivzRebvV8lgv/RQorHUdLdA1334USg69Uxvav+klW20zeE 4ovwNSu0kR+zvmMJQp/9zySKa6pwjge7y+7d665TkMXuref6MRePj4o+QNAxruizAm Do9tOAU8wo/xg== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id E65E86A2CB; Sun, 29 Mar 2026 09:02:16 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Sun, 29 Mar 2026 09:01:34 -0600 Message-ID: <20260329150140.4095446-10-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260329150140.4095446-1-sjg@u-boot.org> References: <20260329150140.4095446-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: HRUQQ777QQW36AF7K2YXE7ZPZQLLNXQS X-Message-ID-Hash: HRUQQ777QQW36AF7K2YXE7ZPZQLLNXQS X-MailFrom: sjg@u-boot.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 09/11] patman: Record series send in the workflow table List-Id: Discussion and patches related to U-Boot Concept Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Simon Glass When a series is sent, record the event as a SENT workflow entry and automatically create a TODO entry 7 days out so the developer is reminded to follow up on reviews. Sending again archives the previous entries and creates fresh ones. Signed-off-by: Simon Glass --- tools/patman/cseries.py | 4 ++++ tools/patman/test_cseries.py | 30 ++++++++++++++++++++++++++++++ tools/patman/workflow.py | 18 ++++++++++++++++++ 3 files changed, 52 insertions(+) diff --git a/tools/patman/cseries.py b/tools/patman/cseries.py index 8f159a8308f..4dddc70b679 100644 --- a/tools/patman/cseries.py +++ b/tools/patman/cseries.py @@ -20,6 +20,7 @@ from patman import cser_helper from patman.cser_helper import AUTOLINK, oid from patman import send from patman import status +from patman import workflow class Cseries(cser_helper.CseriesHelper): @@ -988,6 +989,9 @@ class Cseries(cser_helper.CseriesHelper): args.branch = self._get_branch_name(ser.name, version) likely_sent = send.send(args, git_dir=self.gitdir, cwd=self.topdir) + if likely_sent: + workflow.sent(self, ser.idnum) + if likely_sent and autolink: tout.notice(f'Autolinking with Patchwork ({autolink_wait} seconds)') self.link_auto(pwork, name, version, True, wait_s=autolink_wait) diff --git a/tools/patman/test_cseries.py b/tools/patman/test_cseries.py index 8fcdbd340fb..01f4f4a133d 100644 --- a/tools/patman/test_cseries.py +++ b/tools/patman/test_cseries.py @@ -4293,3 +4293,33 @@ Date: .* with terminal.capture() as (out, _): self.run_args('wf', 'todo-list') self.assertIn('No todos due', out.getvalue()) + + def test_workflow_sent(self): + """Test that sending a series creates SENT and TODO entries""" + cser = self.get_cser() + with terminal.capture(): + cser.add('first', 'my description', allow_unmarked=True) + + cser.fake_now = datetime(2025, 3, 1, 12, 0, 0) + ser = cser.get_series_by_name('first') + + # Record a send + wf.sent(cser, ser.idnum) + + # Should have a SENT entry with current time + ts = cser.db.workflow_get('sent', ser.idnum) + self.assertEqual('2025-03-01 12:00:00', ts) + + # Should have a TODO entry 7 days out + ts = cser.db.workflow_get('todo', ser.idnum) + self.assertEqual('2025-03-08 12:00:00', ts) + + # Sending again should archive old entries and create new ones + cser.fake_now = datetime(2025, 3, 5, 12, 0, 0) + wf.sent(cser, ser.idnum) + + ts = cser.db.workflow_get('sent', ser.idnum) + self.assertEqual('2025-03-05 12:00:00', ts) + + ts = cser.db.workflow_get('todo', ser.idnum) + self.assertEqual('2025-03-12 12:00:00', ts) diff --git a/tools/patman/workflow.py b/tools/patman/workflow.py index 029d63cf6a4..42e38db56f6 100644 --- a/tools/patman/workflow.py +++ b/tools/patman/workflow.py @@ -11,9 +11,27 @@ import enum class Wtype(enum.StrEnum): """Types of workflow entry""" + SENT = 'sent' TODO = 'todo' +def sent(cser, series_id): + """Record that a series was sent and create a follow-up todo + + Args: + cser (CseriesHelper): Series helper with open database + series_id (int): ID of the series that was sent + """ + ts = cser.get_now().strftime('%Y-%m-%d %H:%M:%S') + cser.db.workflow_archive(Wtype.SENT, series_id) + cser.db.workflow_add(Wtype.SENT, series_id, ts) + when = cser.get_now() + timedelta(days=7) + todo_ts = when.strftime('%Y-%m-%d %H:%M:%S') + cser.db.workflow_archive(Wtype.TODO, series_id) + cser.db.workflow_add(Wtype.TODO, series_id, todo_ts) + cser.commit() + + def todo(cser, series, days): """Mark a series as a todo item after a number of days From patchwork Sun Mar 29 15:01:35 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2084 Return-Path: X-Original-To: u-boot-concept@u-boot.org Delivered-To: u-boot-concept@u-boot.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1774796548; bh=n17GeSJJWdQCcKarjtpFen8+jHDDqOCzaw67jDHS9rA=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=Gmmks4jtH/fZgCxrGgfFr7hB+me7nrcmJDt62+imGBWiLW83hF+ZProrHg1VdmCqJ w5kZviXPUDHpjKa56qHn/KfEfD0G2OyXG19wb6Mus3EQI4oxLs7Voc5vqTuItNBr6H llPc4x6xWNqjD3NFIjjAzmmMEAR6mQq/I0by6PfEdQ5q40DoQvQHiDrkYd4Fdr4tFC dZ2nnxwno4Qt4qTyqvyGMynjOMdiQ71H8wNcuWo1YMzOLjkEQwkL8Mpi9KUEy41HrP ra9XQJpPZWMvVOO+yajaV4MbqZDdRjE0TmM9eVBtiL4eYm0HIovRBR9pequGbyB4Nm iVH8FU+Faus2Q== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 1411D6A2E4 for ; Sun, 29 Mar 2026 09:02:28 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10024) with ESMTP id aOoEhuawnsL1 for ; Sun, 29 Mar 2026 09:02:28 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1774796547; bh=n17GeSJJWdQCcKarjtpFen8+jHDDqOCzaw67jDHS9rA=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=H5gBZqDeSTuObMxx6O/Eodn0EQ2/CKsNq9z73HvgReUISRuOgInl0+bXjBl754nsI sQa1EsYk3et2bnXODLOE6dUva0noyfY9+ZQp16gZAp3Bnqn5aAN789cEeh8arIESSu HNS2r/QUknXM10Khx1b0QCwYzfuMsHp5KfAOBhfi16/RTE6IWS8hjMk/9fx33FX9s9 lU3qXOxJiSHLwZ8VVtNPtD6q7nkr7a5uJwY/WL01G4YPsN2ns5Dm3YwBE/hWkvtjTV zvF9MgzVHNT7gfumWJLH+2uem6IToUfdb6WepQBOFMFPSdadJzc2QQKvrPh0gh+fbO 2O4of/GpoQb4A== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 0D28F6A2AB for ; Sun, 29 Mar 2026 09:02:27 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1774796545; bh=jsXxjrNavvy2PBgJQgu04+nrZG7J6KfJOWHYNBnw0I0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=JR/aIm3Uqm5xA0jY8Oyc5BdE0CAQK/6ey0VC7MVNTtREiE769/cQlsN1fBkUal10/ tGyFjyW47Wib+mjuUwVumSex60/3ismqEJkD/wy0TKxsnCJ7GeLjfJ64tXohMJ5Mcg rniwHRldpcICv+CSTdlZhOXQUc1Tc4Ti0hEC3oMCZI7YSFNI3NCg+D/uuV+nt09m1N BfPB5MWbW3/605jLmqOAczHQQojxgM4oVk32CasO18aEzE2Fba99vK8+RK3YDVUzdu pBpN1wjDQvQi0F3hZkWjpSYca/ZZYihE1C6HgKXd4BoY7gpCgqA6LYyr/57vCu8l4m k40Fh7lycQh7Q== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id BC6E26A2D3; Sun, 29 Mar 2026 09:02:25 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10026) with ESMTP id JaRS5m_4Hr26; Sun, 29 Mar 2026 09:02:25 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1774796541; bh=LQe7tZ/l8zQ5BNf3unZTw/DdSeBZ6MqfgtpFjeCsTeM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=L1CSI2mtVjnFQXEfzkm/UOAICCRbE7fY3DozbfVzaYPLlJHY2+8bvLsLJNtCZ1z8k TWJg8j8DyUuexVRE7qmZDo1U0pDVdyU9ydja2IKkFX/pvfhmhkYMJx9yr+466hAspP yxwxWJAEVEOCJIavxbtpfbgsbFkPPLmrHSO87eVYx1dhDE+glOBsilqnnzKmve6oc2 uPUrG0KPd1pVLFrH4XD7YWoAN6VYCXv/wfpxE19DQae7YMWwfIbXcczcXjGH1nkU9w D0NvCz/mUHPQD+oP62EglCttqQLXf+FvCnCezdaBqpmOcMJglt5l2iRBfeQLjo0xvN ERkrTvvjNTXYA== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 39A1F6A2D0; Sun, 29 Mar 2026 09:02:21 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Sun, 29 Mar 2026 09:01:35 -0600 Message-ID: <20260329150140.4095446-11-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260329150140.4095446-1-sjg@u-boot.org> References: <20260329150140.4095446-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: FHWKAA3IVEDZJHBZTMM4J7WG5BE6O7JA X-Message-ID-Hash: FHWKAA3IVEDZJHBZTMM4J7WG5BE6O7JA X-MailFrom: sjg@u-boot.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 10/11] patman: Add 'workflow list' command to show workflow history List-Id: Discussion and patches related to U-Boot Concept Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Simon Glass Add a 'list' subcommand (aliases: 'ls', 'wl') to the workflow command that shows all workflow entries ordered by timestamp. Use -a/--all to include archived entries, which are marked with '*' in an extra column. Timestamps are shown in a friendly format: time only for today, day and time for the past week, 'Nd ago' up to two weeks, and 'Nw ago' beyond that. Future dates use the same pattern. Signed-off-by: Simon Glass --- tools/patman/cmdline.py | 7 ++++ tools/patman/control.py | 2 + tools/patman/database.py | 23 ++++++++++++ tools/patman/test_cseries.py | 72 ++++++++++++++++++++++++++++++++++++ tools/patman/workflow.py | 59 +++++++++++++++++++++++++++++ 5 files changed, 163 insertions(+) diff --git a/tools/patman/cmdline.py b/tools/patman/cmdline.py index 3628c12459c..26626f5b441 100644 --- a/tools/patman/cmdline.py +++ b/tools/patman/cmdline.py @@ -37,6 +37,7 @@ ALIASES = { 'progress': ['p', 'pr', 'prog'], 'rm-version': ['rmv'], 'todo-list': ['tl'], + 'workflow-list': ['wl'], 'unarchive': ['unar'], } @@ -501,6 +502,12 @@ def add_workflow_subparser(subparsers): aliases=ALIASES['todo-list']) tlist.add_argument('--all', action='store_true', dest='show_all', help='Show all scheduled todos, not just due ones') + + wlist = workflow_subparsers.add_parser('list', + aliases=[*ALIASES['workflow-list'], + 'ls']) + wlist.add_argument('-a', '--all', action='store_true', dest='show_all', + help='Include archived entries') return workflow diff --git a/tools/patman/control.py b/tools/patman/control.py index fe012f1a21b..0c3a3097967 100644 --- a/tools/patman/control.py +++ b/tools/patman/control.py @@ -365,6 +365,8 @@ def do_workflow(args, test_db=None): workflow.todo(cser, args.series, args.days) elif args.subcmd == 'todo-list': workflow.todo_list(cser, args.show_all) + elif args.subcmd in ['list', 'wl', 'ls']: + workflow.list_entries(cser, args.show_all) else: raise ValueError(f"Unknown workflow subcommand '{args.subcmd}'") finally: diff --git a/tools/patman/database.py b/tools/patman/database.py index edb7d116c33..3133a320694 100644 --- a/tools/patman/database.py +++ b/tools/patman/database.py @@ -1130,3 +1130,26 @@ class Database: # pylint:disable=R0904 query += ' ORDER BY w.timestamp' res = self.execute(query, params) return res.fetchall() + + def workflow_list(self, include_archived=False): + """Get workflow entries joined with series info + + Args: + include_archived (bool): True to include archived entries + + Return: + list of tuple: + str: workflow type + str: series name + str: series description + str: timestamp + int: archived flag (0 or 1) + """ + query = ('SELECT w.type, s.name, s.desc, w.timestamp, w.archived ' + 'FROM workflow w ' + 'JOIN series s ON w.series_id = s.id') + if not include_archived: + query += ' WHERE w.archived = 0' + query += ' ORDER BY w.timestamp' + res = self.execute(query) + return res.fetchall() diff --git a/tools/patman/test_cseries.py b/tools/patman/test_cseries.py index 01f4f4a133d..4eea5922c84 100644 --- a/tools/patman/test_cseries.py +++ b/tools/patman/test_cseries.py @@ -4323,3 +4323,75 @@ Date: .* ts = cser.db.workflow_get('todo', ser.idnum) self.assertEqual('2025-03-12 12:00:00', ts) + + def test_workflow_list(self): + """Test listing all workflow entries""" + cser = self.get_cser() + with terminal.capture(): + cser.add('first', 'my description', allow_unmarked=True) + + cser.fake_now = datetime(2025, 3, 1, 12, 0, 0) + + # Record a send (creates SENT + TODO) + ser = cser.get_series_by_name('first') + wf.sent(cser, ser.idnum) + + # Default list shows only active entries + with terminal.capture() as (out, _): + wf.list_entries(cser, show_all=False) + lines = out.getvalue().splitlines() + self.assertEqual(4, len(lines)) + self.assertIn('sent', lines[2]) + self.assertIn('todo', lines[3]) + + # Archive the todo + wf.todo_clear(cser, 'first') + + # Without --all, only SENT is active; no 'A' column + with terminal.capture() as (out, _): + wf.list_entries(cser, show_all=False) + lines = out.getvalue().splitlines() + self.assertEqual(3, len(lines)) + self.assertIn('sent', lines[2]) + self.assertNotIn('A', lines[0]) + + # With --all, archived entries appear with '*' marker + with terminal.capture() as (out, _): + wf.list_entries(cser, show_all=True) + lines = out.getvalue().splitlines() + self.assertGreater(len(lines), 3) + self.assertIn(' A ', lines[0]) + has_archived = any('*' in line for line in lines[2:]) + self.assertTrue(has_archived) + + def test_friendly_time(self): + """Test friendly timestamp formatting""" + now = datetime(2025, 3, 10, 15, 0, 0) # Monday + + # Same day + when = datetime(2025, 3, 10, 9, 30, 0) + self.assertEqual('09:30', wf.friendly_time(now, when)) + + # Earlier this week (3 days ago = Friday) + when = datetime(2025, 3, 7, 14, 20, 0) + self.assertEqual('Fri 14:20', wf.friendly_time(now, when)) + + # 10 days ago + when = datetime(2025, 2, 28, 10, 0, 0) + self.assertEqual('10d ago', wf.friendly_time(now, when)) + + # 3 weeks ago + when = datetime(2025, 2, 17, 10, 0, 0) + self.assertEqual('3w ago', wf.friendly_time(now, when)) + + # Future within a week (3 days from now = Thursday) + when = datetime(2025, 3, 13, 16, 0, 0) + self.assertEqual('Thu 16:00', wf.friendly_time(now, when)) + + # Future 10 days + when = datetime(2025, 3, 20, 10, 0, 0) + self.assertEqual('in 10d', wf.friendly_time(now, when)) + + # Future 3 weeks + when = datetime(2025, 3, 31, 10, 0, 0) + self.assertEqual('in 3w', wf.friendly_time(now, when)) diff --git a/tools/patman/workflow.py b/tools/patman/workflow.py index 42e38db56f6..d123e3068a2 100644 --- a/tools/patman/workflow.py +++ b/tools/patman/workflow.py @@ -15,6 +15,34 @@ class Wtype(enum.StrEnum): TODO = 'todo' +def friendly_time(now, when): + """Format a timestamp in a human-friendly way + + Args: + now (datetime): Current time + when (datetime): Timestamp to format + + Return: + str: Friendly string, e.g. 'Tue 15:34', '3d ago', '2w ago' + """ + delta = now - when + days = delta.days + if days < 0: + days = -days + if days >= 14: + return f'in {days // 7}w' + if days >= 7: + return f'in {days}d' + return when.strftime('%a %H:%M') + if days == 0: + return when.strftime('%H:%M') + if days < 7: + return when.strftime('%a %H:%M') + if days < 14: + return f'{days}d ago' + return f'{days // 7}w ago' + + def sent(cser, series_id): """Record that a series was sent and create a follow-up todo @@ -92,3 +120,34 @@ def todo_list(cser, show_all): else: due = f'in {days}d' print(f"{name:17} {due:>14} {desc}") + + +def list_entries(cser, show_all): + """List all workflow entries + + Args: + cser (CseriesHelper): Series helper with open database + show_all (bool): True to include archived entries + """ + entries = cser.db.workflow_list(include_archived=show_all) + if not entries: + print('No workflow entries') + return + hdr = f"{'Type':6} {'Series':17} {'When':>10}" + div = f"{'-' * 6} {'-' * 17} {'-' * 10}" + if show_all: + hdr += ' A' + div += ' -' + hdr += ' Description' + div += ' ' + '-' * 30 + print(hdr) + print(div) + now = cser.get_now() + for wtype, name, desc, ts, archived in entries: + when = datetime.strptime(ts, '%Y-%m-%d %H:%M:%S') + friendly = friendly_time(now, when) + line = f"{wtype:6} {name:17} {friendly:>10}" + if show_all: + line += f" {'*' if archived else ' '}" + line += f" {desc}" + print(line) From patchwork Sun Mar 29 15:01:36 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2085 Return-Path: X-Original-To: u-boot-concept@u-boot.org Delivered-To: u-boot-concept@u-boot.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1774796552; bh=4lw7Az7i2fhFLEe0Qb32OB6scGatkdo77VM3TPYKKHA=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=WTb+ekqSOjGfn4eI1jdOZ0yj0GdJeVteJKvroKQwU+G5V57xM2iYJNNs2RfVhBdfl 1V39zhLqoVOdhMc9u7AtuQKlffv7dU0rKK/kXPdGxq46bRUEIHcrg0gOb4q2g4qUzB yR/7GrQYGWK62Hp+jqQ0cp/O7/Gn1rDMOSyufQ2V7wYrG19FkY+I/C0LN9IXg7HuxP vGGr0U37JFlbqFpH5EK7eCc8CByHKzRaMp9/9D3nHCEHMmG4geezdcnitA0Xi5Qg5I ArasaFbVbmWz9y0TGpBe6y1r3qhdW/UA8WExv4tQ/rFCx0qK5nnYZQThpn0cMq7sfn 0jaWV9KzLVsyA== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 9D88F6A2D0 for ; Sun, 29 Mar 2026 09:02:32 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10024) with ESMTP id EDx2qMPa2dSX for ; Sun, 29 Mar 2026 09:02:32 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1774796552; bh=4lw7Az7i2fhFLEe0Qb32OB6scGatkdo77VM3TPYKKHA=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=WTb+ekqSOjGfn4eI1jdOZ0yj0GdJeVteJKvroKQwU+G5V57xM2iYJNNs2RfVhBdfl 1V39zhLqoVOdhMc9u7AtuQKlffv7dU0rKK/kXPdGxq46bRUEIHcrg0gOb4q2g4qUzB yR/7GrQYGWK62Hp+jqQ0cp/O7/Gn1rDMOSyufQ2V7wYrG19FkY+I/C0LN9IXg7HuxP vGGr0U37JFlbqFpH5EK7eCc8CByHKzRaMp9/9D3nHCEHMmG4geezdcnitA0Xi5Qg5I ArasaFbVbmWz9y0TGpBe6y1r3qhdW/UA8WExv4tQ/rFCx0qK5nnYZQThpn0cMq7sfn 0jaWV9KzLVsyA== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 8C7926A2AB for ; Sun, 29 Mar 2026 09:02:32 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1774796550; bh=qvcOmPNp8p4lc4bCpZ7TypjI9FvZxN+oK7hDIPXvgUw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=nn7UzaV6CXUcvLZvuwTIRfOxPl+Ux3GohnIW+v/lNsd/5Ihvmmvq6W1YXP9+TbOqn QXTkBnb75upmgxPCIieinOlRxRng2PI28J4OfwSfLl7iw4UMlyXG1ZHfdeIgsI5i3T 3NVLCPuFqSruaiVR+a0UeDT+Q5EJafr8ZmXnSmUTGWqYWDrezxWW55vCFVUDBAtTAo unPDsvb2S7x1qYcxRCpxN+iZNgJD34purTQdPtRKd041k53bQZIPNL4vN07gHvPOYN dzkztKzUqKsts8lesP0zCTS+idblXADhHmAgD8HsP3f63yhtcUVtFKcQWHOv56kwsK nDnipxhV+PYGQ== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 643F46A02E; Sun, 29 Mar 2026 09:02:30 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10026) with ESMTP id HRbkut-vbW0M; Sun, 29 Mar 2026 09:02:30 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1774796546; bh=53E4m6P1btAt9r9gv7gbvkrBkFrB2ictqZvQJJgBv3c=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=rVibq7RB3zmCQgxQ1fSK8qGrdWCIER2JwayNWTvYB1Mx7jlIdzeQ//r9R6ce8hSmO 8cCkiyTfkOYEb3TokC2xMNQIyG3t7az3Q97TO1iOQ1IbmeUskepOabPLuVQ8NmBQEE 0swsLAd0c+E3MTP7Dluq4XhOh3nLA3h7BoD117nBMB6Cs4BTRNXypEU3rklxzClAGX M0bhOwjp17CiaZtIL70wIfFAt8pl1OjqPQiSzMGj5R8doAIfgTFLu1cZpGbPVjqhDX lwmB6DneFdJJF0Dg8AAS7bM9sEtqo+sKj+itlC1TkJ4dqFjEwiYaGCKC3QcV3Bivp/ ulJ9YxAH1mFoA== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id EFBEE6A2DB; Sun, 29 Mar 2026 09:02:25 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Sun, 29 Mar 2026 09:01:36 -0600 Message-ID: <20260329150140.4095446-12-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260329150140.4095446-1-sjg@u-boot.org> References: <20260329150140.4095446-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: 7RAGEKH3ZOPXGXOX7N65JPVHXYRZCYPP X-Message-ID-Hash: 7RAGEKH3ZOPXGXOX7N65JPVHXYRZCYPP X-MailFrom: sjg@u-boot.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 11/11] patman: Record ser_ver in workflow sent entries List-Id: Discussion and patches related to U-Boot Concept Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Simon Glass Add a ser_ver_id column to the workflow table (schema v7) so that SENT entries record which version of a series was sent. This is a foreign key referencing ser_ver.id, or NULL for entries like TODO that are not tied to a specific version. Show the version in the 'workflow list' output. Signed-off-by: Simon Glass --- tools/patman/cseries.py | 3 ++- tools/patman/database.py | 30 ++++++++++++++++++++++++------ tools/patman/test_cseries.py | 19 ++++++++++++++----- tools/patman/workflow.py | 14 ++++++++------ 4 files changed, 48 insertions(+), 18 deletions(-) diff --git a/tools/patman/cseries.py b/tools/patman/cseries.py index 4dddc70b679..0643d44cc01 100644 --- a/tools/patman/cseries.py +++ b/tools/patman/cseries.py @@ -990,7 +990,8 @@ class Cseries(cser_helper.CseriesHelper): likely_sent = send.send(args, git_dir=self.gitdir, cwd=self.topdir) if likely_sent: - workflow.sent(self, ser.idnum) + svid = self.get_series_svid(ser.idnum, version) + workflow.sent(self, ser.idnum, ser_ver_id=svid) if likely_sent and autolink: tout.notice(f'Autolinking with Patchwork ({autolink_wait} seconds)') diff --git a/tools/patman/database.py b/tools/patman/database.py index 3133a320694..ec1daa1c073 100644 --- a/tools/patman/database.py +++ b/tools/patman/database.py @@ -19,7 +19,7 @@ from u_boot_pylib import tout from patman.series import Series # Schema version (version 0 means there is no database yet) -LATEST = 6 +LATEST = 7 # Information about a series/version record SerVer = namedtuple( @@ -213,6 +213,16 @@ class Database: # pylint:disable=R0904 'type, series_id INTEGER, timestamp, archived BIT,' 'FOREIGN KEY (series_id) REFERENCES series (id))') + def _migrate_to_v7(self): + """Add ser_ver_id to workflow table for tracking which version was sent + + Fields: + ser_ver_id: Foreign key referencing ser_ver.id, or NULL for + entries not tied to a specific version (e.g. todo) + """ + self.cur.execute( + 'ALTER TABLE workflow ADD COLUMN ser_ver_id INTEGER') + def migrate_to(self, dest_version): """Migrate the database to the selected version @@ -243,6 +253,8 @@ class Database: # pylint:disable=R0904 self._migrate_to_v5() elif version == 6: self._migrate_to_v6() + elif version == 7: + self._migrate_to_v7() # Save the new version if we have a schema_version table if version > 1: @@ -1061,17 +1073,20 @@ class Database: # pylint:disable=R0904 # workflow functions - def workflow_add(self, wtype, series_id, timestamp): + def workflow_add(self, wtype, series_id, timestamp, ser_ver_id=None): """Add a workflow entry Args: wtype (str): Workflow type, e.g. 'todo' series_id (int): ID of the series timestamp (str): Timestamp string, e.g. '2025-01-15 10:30:00' + ser_ver_id (int or None): ID of the ser_ver record, if applicable """ self.execute( - 'INSERT INTO workflow (type, series_id, timestamp, archived) ' - 'VALUES (?, ?, ?, 0)', (wtype, series_id, timestamp)) + 'INSERT INTO workflow ' + '(type, series_id, timestamp, archived, ser_ver_id) ' + 'VALUES (?, ?, ?, 0, ?)', + (wtype, series_id, timestamp, ser_ver_id)) def workflow_archive(self, wtype, series_id): """Archive active workflow entries for a given type and series @@ -1144,10 +1159,13 @@ class Database: # pylint:disable=R0904 str: series description str: timestamp int: archived flag (0 or 1) + int or None: version number from ser_ver, if applicable """ - query = ('SELECT w.type, s.name, s.desc, w.timestamp, w.archived ' + query = ('SELECT w.type, s.name, s.desc, w.timestamp, w.archived,' + 'sv.version ' 'FROM workflow w ' - 'JOIN series s ON w.series_id = s.id') + 'JOIN series s ON w.series_id = s.id ' + 'LEFT JOIN ser_ver sv ON w.ser_ver_id = sv.id') if not include_archived: query += ' WHERE w.archived = 0' query += ' ORDER BY w.timestamp' diff --git a/tools/patman/test_cseries.py b/tools/patman/test_cseries.py index 4eea5922c84..7a5eede9f82 100644 --- a/tools/patman/test_cseries.py +++ b/tools/patman/test_cseries.py @@ -3557,7 +3557,7 @@ Date: .* self.assertEqual(f'Update database to v{version}', out.getvalue().strip()) self.assertEqual(version, db.get_schema_version()) - self.assertEqual(6, database.LATEST) + self.assertEqual(7, database.LATEST) def test_migrate_future_version(self): """Test that a database newer than patman is rejected""" @@ -4303,20 +4303,29 @@ Date: .* cser.fake_now = datetime(2025, 3, 1, 12, 0, 0) ser = cser.get_series_by_name('first') - # Record a send - wf.sent(cser, ser.idnum) + svid = cser.get_series_svid(ser.idnum, 1) + + # Record a send with ser_ver_id + wf.sent(cser, ser.idnum, ser_ver_id=svid) # Should have a SENT entry with current time ts = cser.db.workflow_get('sent', ser.idnum) self.assertEqual('2025-03-01 12:00:00', ts) - # Should have a TODO entry 7 days out + # The SENT entry should have the ser_ver_id + res = cser.db.execute( + 'SELECT ser_ver_id FROM workflow ' + 'WHERE type = ? AND series_id = ? AND archived = 0', + ('sent', ser.idnum)) + self.assertEqual(svid, res.fetchone()[0]) + + # Should have a TODO entry 7 days out (no ser_ver_id) ts = cser.db.workflow_get('todo', ser.idnum) self.assertEqual('2025-03-08 12:00:00', ts) # Sending again should archive old entries and create new ones cser.fake_now = datetime(2025, 3, 5, 12, 0, 0) - wf.sent(cser, ser.idnum) + wf.sent(cser, ser.idnum, ser_ver_id=svid) ts = cser.db.workflow_get('sent', ser.idnum) self.assertEqual('2025-03-05 12:00:00', ts) diff --git a/tools/patman/workflow.py b/tools/patman/workflow.py index d123e3068a2..85b38bd3f58 100644 --- a/tools/patman/workflow.py +++ b/tools/patman/workflow.py @@ -43,16 +43,17 @@ def friendly_time(now, when): return f'{days // 7}w ago' -def sent(cser, series_id): +def sent(cser, series_id, ser_ver_id=None): """Record that a series was sent and create a follow-up todo Args: cser (CseriesHelper): Series helper with open database series_id (int): ID of the series that was sent + ser_ver_id (int or None): ID of the ser_ver record that was sent """ ts = cser.get_now().strftime('%Y-%m-%d %H:%M:%S') cser.db.workflow_archive(Wtype.SENT, series_id) - cser.db.workflow_add(Wtype.SENT, series_id, ts) + cser.db.workflow_add(Wtype.SENT, series_id, ts, ser_ver_id=ser_ver_id) when = cser.get_now() + timedelta(days=7) todo_ts = when.strftime('%Y-%m-%d %H:%M:%S') cser.db.workflow_archive(Wtype.TODO, series_id) @@ -133,8 +134,8 @@ def list_entries(cser, show_all): if not entries: print('No workflow entries') return - hdr = f"{'Type':6} {'Series':17} {'When':>10}" - div = f"{'-' * 6} {'-' * 17} {'-' * 10}" + hdr = f"{'Type':6} {'Series':17} {'Ver':>3} {'When':>10}" + div = f"{'-' * 6} {'-' * 17} {'-' * 3} {'-' * 10}" if show_all: hdr += ' A' div += ' -' @@ -143,10 +144,11 @@ def list_entries(cser, show_all): print(hdr) print(div) now = cser.get_now() - for wtype, name, desc, ts, archived in entries: + for wtype, name, desc, ts, archived, version in entries: when = datetime.strptime(ts, '%Y-%m-%d %H:%M:%S') friendly = friendly_time(now, when) - line = f"{wtype:6} {name:17} {friendly:>10}" + ver = f'v{version}' if version else '' + line = f"{wtype:6} {name:17} {ver:>3} {friendly:>10}" if show_all: line += f" {'*' if archived else ' '}" line += f" {desc}"