From patchwork Thu Feb 26 20:00:15 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 1946 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=1772136130; bh=dQNsGtO7kNY9dYiANmRinpdt6/ipLwqanlm6wH3OP3g=; 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=XNGemrKOKs1s+BkYxMeAeGIezDWfvwTRwaepQRWfoRtDnGn6PthZncxzL087Og37X +OpqPvZ7Dl2EqLPIs1VN9HOpYpWZNVEoKq5sB9yKprBvy79763ordKbJ2K/MYSQRpc qfysuVKXBWcXUAmnhIx9Yn+4dMRd+SrrDi0Jie3YIZrk+ln6FHoQrZ3uYn7z3FACoq WQkXmLmpqvC7tRhUhry1QAThMQ3uKaJiiVUpLJjZMhtXpgkyQmizVWAx4695dTJBEm 4cZigy1Tg6Dcb/5yPPiRst3Tf32SL4r0bdmWDLjI+dHY4SIT/53rgCAkioemlwXgWR v0MQDp3ZZUm7w== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 0995E69D79 for ; Thu, 26 Feb 2026 13:02:10 -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 9l3Layt1qdxv for ; Thu, 26 Feb 2026 13:02:09 -0700 (MST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1772136129; bh=dQNsGtO7kNY9dYiANmRinpdt6/ipLwqanlm6wH3OP3g=; 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=s5R5qU1OocZXnDYvcdMTrcB94XegPXJ5Kla0rel/u7d5L7F151nvCTVm07qqWw+We iePgC8xi5ZXOPsCFaWfrw+DjslCXpVQ6LARCFyPRcWVq73V/6A2/70yr8m+MiJGPjJ Zs1WWvAIMpbpoNjU2uGanWrZy3ZzdyVFatxURuE0j0FjEmT7MUH1qG3QjAzGNPN7gX 6dNusJ0p9zzyOi5Pye5WwLmVwpUXgCteqzCcOYBaH6tHno3uTDQ0Wwoff+ivajodJb nEd9cvIt3yz2udrJ5rOXXe4seNv6XVvzSMtpNfZRHAn5g5rpBk+UAItdBSAGamv/k7 x3jH9uBFLd04g== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id EA53269DAB for ; Thu, 26 Feb 2026 13:02:09 -0700 (MST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1772136127; bh=qofrSQMNKwGi2GKxIqR2Z7cZzpGuya2XOmSKGZA4BHA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=OB2KgY1hQenrVrPw2Gq1UKSTlw9FEhL7jYily+GO0CLe1BImGgbazAOd0MJGdfX+h SYCQsJZ48/6Zs673mUwB+OdfOpi1POLk1po5gmHox+BihMyCfhZH3qHyIgDUPAIiOT MODiRpiavCpv/dbxAl/lWtRzu9cwFFMnMySdadp1ZaXhjXkfE/yqdBkS4+FLIS698y DnSfbpUdEnV0EjsccMLPLeK/+KV8VIYw+d4KX7NY1JfuLwia71/UNQR2znwMMbeG0H sU/3z1TumJwW9yIQ+OSumkSCaVuczn3MtyCZXGgfYfToDpW5mT+KnYlQtYHrIynf8m me+li1J2BQwBg== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 87F8B69DAB; Thu, 26 Feb 2026 13:02:07 -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 2O2X7Im-q7NK; Thu, 26 Feb 2026 13:02:07 -0700 (MST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1772136123; bh=pwT9VRkypmvCiX5OK70K/in+uizZqq3M7/OqiJGHJYs=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=lDPykFgla+mHk8IE7qwKCPbBnYEe80MK+NM93we9wpj6zbRfYPP0NmQz+LwJvcrpg reCHe2c30skB4Zi9Ood0tG6nD9E8ttar5VfVMzZOocO9TAnrPK2qr8u1r6bRQnEE29 uIgC5fvvLTzCne+bSYrGE8G5KJrd2bTJG1mCASv2p8pRrioL8kF4ZikK/hQNka6FJe pnTpeGwV5lYh5BK35GqsH60y5wSTEtWCzqK9eHj7sNgGH35kNTmx45HUJ+DosmUkKA W54jX+N7LZRWSFOVOCAmjAsJ2W+hw2s7Ck6sbZgy2HWnMZvO344hRJ37G0wU29gM+J fZcvGpKyPly9Q== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 6F66469D4A; Thu, 26 Feb 2026 13:02:03 -0700 (MST) From: Simon Glass To: U-Boot Concept Date: Thu, 26 Feb 2026 13:00:15 -0700 Message-ID: <20260226200106.1727176-10-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260226200106.1727176-1-sjg@u-boot.org> References: <20260226200106.1727176-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: 3KRW2ZFMI5MRBS7RX22KK6757DDXHUOR X-Message-ID-Hash: 3KRW2ZFMI5MRBS7RX22KK6757DDXHUOR 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/32] patman: Associate patchwork projects with upstreams 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 Each upstream typically has its own patchwork project. Add an upstream column to the patchwork table (schema v5) so each upstream can have its own project configuration. Rename the table from 'settings' to 'patchwork' to better reflect its purpose. During migration, any existing row is associated with the default upstream. The patchwork_update() and patchwork_get() methods accept an optional upstream parameter to store and retrieve per-upstream project configuration. Signed-off-by: Simon Glass --- tools/patman/cseries.py | 4 +-- tools/patman/database.py | 49 +++++++++++++++++------------ tools/patman/test_cseries.py | 60 ++++++++++++++++++++++++++++++++++++ 3 files changed, 92 insertions(+), 21 deletions(-) diff --git a/tools/patman/cseries.py b/tools/patman/cseries.py index 246610215ed..5d4a9592de9 100644 --- a/tools/patman/cseries.py +++ b/tools/patman/cseries.py @@ -659,7 +659,7 @@ class Cseries(cser_helper.CseriesHelper): tout.detail(f'Name match: ID {proj_id}') if not proj_id: raise ValueError(f"Unknown project name '{name}'") - self.db.settings_update(name, proj_id, link_name) + self.db.patchwork_update(name, proj_id, link_name) self.commit() if not quiet: tout.notice(f"Project '{name}' patchwork-ID {proj_id} " @@ -674,7 +674,7 @@ class Cseries(cser_helper.CseriesHelper): proj_id (int): Patchworks project ID for this project link_name (str): Patchwork's link-name for the project """ - return self.db.settings_get() + return self.db.patchwork_get() def remove(self, name, dry_run=False): """Remove a series from the database diff --git a/tools/patman/database.py b/tools/patman/database.py index 2bd225e4deb..22f21ce715f 100644 --- a/tools/patman/database.py +++ b/tools/patman/database.py @@ -165,12 +165,12 @@ class Database: # pylint:disable=R0904 self.cur.execute('ALTER TABLE ser_ver ADD COLUMN archive_tag') def _migrate_to_v5(self): - """Add upstream support to series, settings and upstream tables + """Add upstream support to series, patchwork and upstream tables - Add upstream column to series table - - Recreate settings table without UNIQUE constraint on name, adding - an upstream column (since the same project can have multiple - remotes) + - Rename and recreate patchwork table (formerly 'settings') without + UNIQUE constraint on name, adding an upstream column (since the + same project can have multiple remotes) - Add patchwork_url, identity, series_to, no_maintainers and no_tags columns to upstream table - Add desc column to ser_ver table @@ -178,17 +178,17 @@ class Database: # pylint:disable=R0904 self.cur.execute('ALTER TABLE series ADD COLUMN upstream') self.cur.execute( - 'CREATE TABLE settings_new ' + 'CREATE TABLE patchwork_new ' '(name, proj_id INT, link_name, upstream)') self.cur.execute( - 'INSERT INTO settings_new SELECT name, proj_id, link_name, NULL ' + 'INSERT INTO patchwork_new SELECT name, proj_id, link_name, NULL ' 'FROM settings') self.cur.execute('DROP TABLE settings') - self.cur.execute('ALTER TABLE settings_new RENAME TO settings') + self.cur.execute('ALTER TABLE patchwork_new RENAME TO patchwork') default_ups = self.upstream_get_default() if default_ups: self.cur.execute( - 'UPDATE settings SET upstream = ?', (default_ups,)) + 'UPDATE patchwork SET upstream = ?', (default_ups,)) self.cur.execute('ALTER TABLE upstream ADD COLUMN patchwork_url') self.cur.execute('ALTER TABLE upstream ADD COLUMN identity') @@ -867,32 +867,43 @@ class Database: # pylint:disable=R0904 udict[name] = url, is_default return udict - # settings functions + # patchwork functions - def settings_update(self, name, proj_id, link_name): - """Set the patchwork settings of the project + def patchwork_update(self, name, proj_id, link_name, ups=None): + """Set the patchwork project details for an upstream Args: name (str): Name of the project to use in patchwork proj_id (int): Project ID for the project link_name (str): Link name for the project + ups (str or None): Upstream name to associate with, or None """ - self.execute('DELETE FROM settings') self.execute( - 'INSERT INTO settings (name, proj_id, link_name) ' - 'VALUES (?, ?, ?)', (name, proj_id, link_name)) + 'DELETE FROM patchwork WHERE upstream IS ?', (ups,)) + self.execute( + 'INSERT INTO patchwork (name, proj_id, link_name, upstream) ' + 'VALUES (?, ?, ?, ?)', (name, proj_id, link_name, ups)) + + def patchwork_get(self, ups=None): + """Get the patchwork project details for an upstream - def settings_get(self): - """Get the patchwork settings of the project + Args: + ups (str or None): Upstream name to look up, or None for any Returns: - tuple or None if there are no settings: + tuple or None if there is no project set: name (str): Project name, e.g. 'U-Boot' proj_id (int): Patchworks project ID for this project link_name (str): Patchwork's link-name for the project """ - res = self.execute("SELECT name, proj_id, link_name FROM settings") + if ups is not None: + res = self.execute( + 'SELECT name, proj_id, link_name FROM patchwork ' + 'WHERE upstream = ?', (ups,)) + else: + res = self.execute( + 'SELECT name, proj_id, link_name FROM patchwork') recs = res.fetchall() - if len(recs) != 1: + if not recs: return None return recs[0] diff --git a/tools/patman/test_cseries.py b/tools/patman/test_cseries.py index c6b90d5223d..0922c3e57f5 100644 --- a/tools/patman/test_cseries.py +++ b/tools/patman/test_cseries.py @@ -2502,6 +2502,66 @@ Tested-by: Mary Smith # yak f"Project 'U-Boot' patchwork-ID {self.PROJ_ID} link-name 'uboot'", out.getvalue().strip()) + def test_patchwork_upstream(self): + """Test patchwork project with upstream association""" + cser = self.get_cser() + + # Add two upstreams + cser.db.upstream_add('us', 'https://us.example.com') + cser.db.upstream_add('ci', 'https://ci.example.com') + cser.db.commit() + + # Set project for a specific upstream + cser.db.patchwork_update('U-Boot', 6, 'uboot', 'us') + cser.db.commit() + + # Look up by upstream + info = cser.db.patchwork_get('us') + self.assertEqual(('U-Boot', 6, 'uboot'), info) + + # Different upstream has no project + self.assertIsNone(cser.db.patchwork_get('ci')) + + # No upstream arg returns any match + info = cser.db.patchwork_get() + self.assertEqual(('U-Boot', 6, 'uboot'), info) + + # Set a different project for ci + cser.db.patchwork_update('Linux', 10, 'linux', 'ci') + cser.db.commit() + + self.assertEqual(('Linux', 10, 'linux'), cser.db.patchwork_get('ci')) + self.assertEqual(('U-Boot', 6, 'uboot'), cser.db.patchwork_get('us')) + + def test_migrate_patchwork_upstream(self): + """Test that migrating to v5 renames settings to patchwork""" + db = database.Database(f'{self.tmpdir}/.patman3.db') + with terminal.capture(): + db.open_it() + + # Create a v4 database with an upstream and a patchwork row + with terminal.capture(): + db.migrate_to(4) + db.execute( + "INSERT INTO upstream (name, url, is_default) " + "VALUES ('us', 'https://us.example.com', 1)") + db.execute( + "INSERT INTO settings (name, proj_id, link_name) " + "VALUES ('U-Boot', 6, 'uboot')") + db.commit() + + # Migrate to v5 + with terminal.capture(): + db.migrate_to(5) + + # The existing row should now be in 'patchwork' with the default upstream + res = db.execute( + 'SELECT name, proj_id, link_name, upstream FROM patchwork') + recs = res.fetchall() + self.assertEqual(1, len(recs)) + self.assertEqual(('U-Boot', 6, 'uboot', 'us'), recs[0]) + db.close() + def check_series_list_patches(self): """Test listing the patches for a series""" cser = self.get_cser()