From patchwork Sun Feb 22 15:42:52 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 1933 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=1771775067; bh=rHVCoCvyRhbwxdAEvCBIpQO64th3Dxh3WZVLxidd/1k=; 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=szhG7P9+ET97V44z+5rpSWbyyOF0N56Rqiiz+FrX/lF6IxPFkbNV39UO1ivkwy48r b3utc/hmjIjtImp5COrq7EZmY8qF30N13SSBI3pdy+rF5hQVwci9lsyDFvBGpZVKYm p6It/mkr7AM5ha17OgkOkmOLTIwcO/5TtAY+CKhNgbXwOu8nqa4I5rFK/8t5hNcgVe +Q+vcnA0OFKuT7q8KF8UkbFhL47JaEUqzk1u2LkfyLdBtRDpNfNmAhwN4VRVHswUy4 g7HER114eVaGpntJVlnXDaNlx7w3Ayb5km59/Ks61vYBEAz7depExGAG1nYk/NVF2x KD+i1tmlEcPSA== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 4BACB69D3F for ; Sun, 22 Feb 2026 08:44:27 -0700 (MST) 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 DB7hupU2R3Gf for ; Sun, 22 Feb 2026 08:44:27 -0700 (MST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1771775067; bh=rHVCoCvyRhbwxdAEvCBIpQO64th3Dxh3WZVLxidd/1k=; 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=szhG7P9+ET97V44z+5rpSWbyyOF0N56Rqiiz+FrX/lF6IxPFkbNV39UO1ivkwy48r b3utc/hmjIjtImp5COrq7EZmY8qF30N13SSBI3pdy+rF5hQVwci9lsyDFvBGpZVKYm p6It/mkr7AM5ha17OgkOkmOLTIwcO/5TtAY+CKhNgbXwOu8nqa4I5rFK/8t5hNcgVe +Q+vcnA0OFKuT7q8KF8UkbFhL47JaEUqzk1u2LkfyLdBtRDpNfNmAhwN4VRVHswUy4 g7HER114eVaGpntJVlnXDaNlx7w3Ayb5km59/Ks61vYBEAz7depExGAG1nYk/NVF2x KD+i1tmlEcPSA== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 3982C69D48 for ; Sun, 22 Feb 2026 08:44:27 -0700 (MST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1771775065; bh=n/SvtcIz7K/pSCvSl6SL9dkkYYeSnXP6010kMl+MdUw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=iwkL5CKOqB6pNVR+nzoXpFTRLvnH+PJ03lGTShqAC83BUyGyreJlpZmo/ddYcRiTJ eyRlQ67gYbMRegHUjBqcobS0t7yCL/TxApD5/6vohC/A7VCTCzOkOPIn9G+PQ8pw1R sZNnqnHOUvPPTsjFZEvr+fRjoJBJRnLNT1nNMAUqqnUHnhjLErclUKIpxIZen+/SF1 EKY2Xbr/PtoddcNRDtFaiNkj2MqB3WKX17Q9BHZNrPQGbHb2rb1A+EoWVDWWkYwH4y 3S01dWby8eQKwtQb8k5Yv2I5hEoTEgu9wtrciDbVpzoJnFvw4FB6cxJ0uF7HjDA5ZO JlMD+C0nekkUQ== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 77A6A69D3F; Sun, 22 Feb 2026 08:44:25 -0700 (MST) 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 Hlb2-bMD65vD; Sun, 22 Feb 2026 08:44:25 -0700 (MST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1771775061; bh=LygppgSnkrwDKLRu6x0bBENcZ3cLDBFH/REvEXM63Xg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=lx4ikFfsazgm4v0LihjAvvp+14YH/L6U++q0q/Mvqf7Yoma9Akk4y4A0tPHgr8HMC GDJSIcGvwpjZtsFGeEdVPu3UiJV+WPz3O++0/N/A6xh3NQ+eOXtK6i6NXwQ2meBcou uo9qzTFPWAWSWhlJZAkDfW2iemlP+6/N6cal5sO7q09A/DiTV5IibzRCUwzM7cuxBC /ah/oe5/WUOOG8apVLD0VPhTCAyNkNeihpm0zp1070EAfPcB+Vh6WoQIn3seZ47+J3 MBa5tcIqzft+rRQZ4YMUceXC73tf1JaCsjSorEX1ktevRRH/4tyQ7KDocjCme1/c8u sm+8OKVBB2etw== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id EEC8169C5C; Sun, 22 Feb 2026 08:44:20 -0700 (MST) From: Simon Glass To: U-Boot Concept Date: Sun, 22 Feb 2026 08:42:52 -0700 Message-ID: <20260222154303.2851319-13-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260222154303.2851319-1-sjg@u-boot.org> References: <20260222154303.2851319-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: KJJIVHK4HIGT2T3BSAOWITXPXXOX674H X-Message-ID-Hash: KJJIVHK4HIGT2T3BSAOWITXPXXOX674H 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 , "Claude Opus 4 . 6" X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 12/16] pickman: Add a database table to track pipeline fixes 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 pipeline_fix table (schema v4) for tracking pipeline-fix attempts per MR. Each row records the MR IID, pipeline ID, attempt number, status and timestamp. A UNIQUE constraint on (mr_iid, pipeline_id) ensures each pipeline is only processed once. Add pfix_count(), pfix_add() and pfix_has() accessors. Co-developed-by: Claude Opus 4.6 Signed-off-by: Simon Glass --- tools/pickman/database.py | 65 +++++++++++++++++++++++++++++- tools/pickman/ftest.py | 84 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 148 insertions(+), 1 deletion(-) diff --git a/tools/pickman/database.py b/tools/pickman/database.py index 92bff7a5702..317668a979d 100644 --- a/tools/pickman/database.py +++ b/tools/pickman/database.py @@ -19,7 +19,7 @@ from u_boot_pylib import tools from u_boot_pylib import tout # Schema version (version 0 means there is no database yet) -LATEST = 3 +LATEST = 4 # Default database filename DB_FNAME = '.pickman.db' @@ -141,6 +141,19 @@ class Database: # pylint: disable=too-many-public-methods 'processed_at TEXT, ' 'UNIQUE(mr_iid, comment_id))') + def _create_v4(self): + """Migrate database to v4 schema - add pipeline_fix table""" + # Table for tracking pipeline fix attempts per MR + self.cur.execute( + 'CREATE TABLE pipeline_fix (' + 'id INTEGER PRIMARY KEY AUTOINCREMENT, ' + 'mr_iid INTEGER, ' + 'pipeline_id INTEGER, ' + 'attempt INTEGER, ' + 'status TEXT, ' + 'created_at TEXT, ' + 'UNIQUE(mr_iid, pipeline_id))') + def migrate_to(self, dest_version): """Migrate the database to the selected version @@ -165,6 +178,8 @@ class Database: # pylint: disable=too-many-public-methods self._create_v2() elif version == 3: self._create_v3() + elif version == 4: + self._create_v4() self.cur.execute('DELETE FROM schema_version') self.cur.execute( @@ -481,3 +496,51 @@ class Database: # pylint: disable=too-many-public-methods 'SELECT comment_id FROM comment WHERE mr_iid = ?', (mr_iid,)) return [row[0] for row in res.fetchall()] + + # pipeline_fix functions + + def pfix_count(self, mr_iid): + """Count fix attempts for an MR + + Args: + mr_iid (int): Merge request IID + + Return: + int: Number of fix attempts + """ + res = self.execute( + 'SELECT COUNT(*) FROM pipeline_fix WHERE mr_iid = ?', + (mr_iid,)) + return res.fetchone()[0] + + def pfix_add(self, mr_iid, pipeline_id, attempt, status): + """Record a pipeline fix attempt + + Args: + mr_iid (int): Merge request IID + pipeline_id (int): Pipeline ID + attempt (int): Attempt number + status (str): Status ('success' or 'failure') + """ + self.execute( + 'INSERT OR IGNORE INTO pipeline_fix ' + '(mr_iid, pipeline_id, attempt, status, created_at) ' + 'VALUES (?, ?, ?, ?, ?)', + (mr_iid, pipeline_id, attempt, status, + datetime.now().isoformat())) + + def pfix_has(self, mr_iid, pipeline_id): + """Check if a pipeline has already been handled + + Args: + mr_iid (int): Merge request IID + pipeline_id (int): Pipeline ID + + Return: + bool: True if already handled + """ + res = self.execute( + 'SELECT id FROM pipeline_fix ' + 'WHERE mr_iid = ? AND pipeline_id = ?', + (mr_iid, pipeline_id)) + return res.fetchone() is not None diff --git a/tools/pickman/ftest.py b/tools/pickman/ftest.py index 4a58a9371ce..67a7d004ca6 100644 --- a/tools/pickman/ftest.py +++ b/tools/pickman/ftest.py @@ -818,6 +818,90 @@ class TestDatabaseComment(unittest.TestCase): dbs.close() +class TestDatabasePipelineFix(unittest.TestCase): + """Tests for Database pipeline_fix functions.""" + + def setUp(self): + """Set up test fixtures.""" + fd, self.db_path = tempfile.mkstemp(suffix='.db') + os.close(fd) + os.unlink(self.db_path) + + def tearDown(self): + """Clean up test fixtures.""" + if os.path.exists(self.db_path): + os.unlink(self.db_path) + database.Database.instances.clear() + + def test_pfix_add(self): + """Test adding a pipeline fix record""" + with terminal.capture(): + dbs = database.Database(self.db_path) + dbs.start() + + dbs.pfix_add(123, 456, 1, 'success') + dbs.commit() + + self.assertTrue(dbs.pfix_has(123, 456)) + + dbs.close() + + def test_pfix_count(self): + """Test counting pipeline fix attempts""" + with terminal.capture(): + dbs = database.Database(self.db_path) + dbs.start() + + self.assertEqual(dbs.pfix_count(123), 0) + + dbs.pfix_add(123, 100, 1, 'failure') + dbs.pfix_add(123, 200, 2, 'success') + dbs.commit() + + self.assertEqual(dbs.pfix_count(123), 2) + # Different MR should have 0 + self.assertEqual(dbs.pfix_count(999), 0) + + dbs.close() + + def test_pfix_has(self): + """Test checking if a pipeline was already handled""" + with terminal.capture(): + dbs = database.Database(self.db_path) + dbs.start() + + self.assertFalse(dbs.pfix_has(123, 456)) + + dbs.pfix_add(123, 456, 1, 'success') + dbs.commit() + + self.assertTrue(dbs.pfix_has(123, 456)) + # Different pipeline should not be handled + self.assertFalse(dbs.pfix_has(123, 789)) + # Different MR should not be handled + self.assertFalse(dbs.pfix_has(999, 456)) + + dbs.close() + + def test_pfix_unique(self): + """Test that duplicate mr_iid/pipeline_id pairs are ignored""" + with terminal.capture(): + dbs = database.Database(self.db_path) + dbs.start() + + dbs.pfix_add(123, 456, 1, 'failure') + dbs.commit() + + # Adding same pair again should not raise (OR IGNORE) + dbs.pfix_add(123, 456, 2, 'success') + dbs.commit() + + # Count should still be 1 (second insert ignored) + self.assertEqual(dbs.pfix_count(123), 1) + + dbs.close() + + class TestListSources(unittest.TestCase): """Tests for list-sources command."""