From patchwork Thu Feb 26 20:00:24 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 1955 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=1772136161; bh=ty1LtIQtOM2I0RsLNfSoejMeRwZOERJwKLlIlYpTNm4=; 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=tj34SlW8G6fiXfWfPLkwCpC5mDiBRpQoF6zzx0BSzgYTQAMHElyBu0hHFJQMyjy/A xHgQYhv+1A01dlMigavVPk1wb5E5pfQ5j6OIK6cojSWymBVKgL+2269t+nptF+CvzN 8K8OWMQTB0Y4qtDiCpVpw6z/Ygie0xgvS1cfcz3h7LzLYisMNi3Vtmeysbgzw7ds/W PrG5IvmPNOA+egNiUu87Fngq8Q5q9aRmndHSqbUsOPYgq2pfdGyD5NJ5d5QnAPK/E8 BK1lBvMGz+h5yb/XH/2P6YUuVVckN8jdy9p+l8Vv52QSaNcnoJyyWfx4qhc5Yz7mF6 nBzYySjq0VwGg== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 02C2E69E39 for ; Thu, 26 Feb 2026 13:02:41 -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 ZUh1xdgOWQZx for ; Thu, 26 Feb 2026 13:02:40 -0700 (MST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1772136160; bh=ty1LtIQtOM2I0RsLNfSoejMeRwZOERJwKLlIlYpTNm4=; 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=Pg16Jxly7yNiZwsBMV3KQDexY2z5vzAxWw6Ioj8Co0CZ6sst6s2vKZV1vpK9itvvX ZIWalk1DVIixYKVIG2pFgTMqCJCqSYvL13JlquQrMKCvJ1rXgiOJ7DbUQ5TxEfmzN7 0WfSRSu/9gbQib2KO4ZBa11+y5Rh+9gTuuI362whz/vj05GdwgRUDl5mB9De60hAfP it8ARRM7v0NFJGMr7uKgN6iztyK+ih/GVfDw9dDDu8Ub9mIWSxspy31mjYx8N0ms/q HOWWUd0SEVIdqD2ks9e73aMrx8wl09T/9gf8KN7coxmOmGMfvXEvhaPr3qDA7a/4Oa yfoSWwoqFtniA== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id E672969DAB for ; Thu, 26 Feb 2026 13:02:40 -0700 (MST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1772136158; bh=fzyyS7HhgpfV73vM+EcaNebj9e3LQkHA8d/dM4RBLU4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Pollu76iicqPrsWajOsYC/Pa2cQB6mjIbht5EINHII9+ia19Ri/ilQKiKMTmZ+BWH 9MchNbI3oezeKC5VH0zoT0LyooCbdyfonrnzsguPqmFqwXFbYslNxfTW6WsLKo9Vwc xz9xX13K+V2Qdau8pYCcpwCHztgIDj9znPVDFWBJOskLFoKzMDByFmXQ112tecrgvY jacApHCGoQZ8kaB7NEkT+4xRflHgw1AsVp0qiFOGLCxZqWtTjhZmZK4AckYduzMdRi cACTags9y2ohBLDVo4YaM6JYIkK1R8jZa1WMf5x+6bSgXwh9Bgmf3xCgHi8j6wbEZu dXGxOciJFOy0w== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id E640869DAB; Thu, 26 Feb 2026 13:02:38 -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 lS0id_8bl1D6; Thu, 26 Feb 2026 13:02:38 -0700 (MST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1772136153; bh=Xd0+dNb1KbAmIZHnAxgNxRfTqDC3YdD/lvhi7iM1pZc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=hhnEcQDY3Y62bri05eqXZgzj8Fe8H5o8artiJOll7zyWj28Gz/VvsRucSlX5V3rmH 4u2TvrOlJ5Qoxpp+8Dk6GYWaEeBCYJnshCfhEp5xnEFkYo0S/UJNv1dr5CrAzOeDPC INYk6VwelpC0fhA3EnQvWmt6bgfQZ2E4MsLjaGHclCUcepwjizu9gXLN+zx5SRIYI7 cxU5DzyfjQnCvviWVlUpvHX92H2hM+v78MLVHga9WTsqA+/kZwJys8dIUH1VRF2thK TWQs3SbIKiimjQPv3rzvlWhYUUda2PFiL9AGPKhJ5UkiO6dYsXr5h8zqEKHfYTWm5j OeIsUp2jIL1CQ== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 441F669D4A; Thu, 26 Feb 2026 13:02:33 -0700 (MST) From: Simon Glass To: U-Boot Concept Date: Thu, 26 Feb 2026 13:00:24 -0700 Message-ID: <20260226200106.1727176-19-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: YXINTPCZEGPY7PAE3ZAG5HLDTUQ55DAG X-Message-ID-Hash: YXINTPCZEGPY7PAE3ZAG5HLDTUQ55DAG 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 18/32] patman: Add per-upstream patchwork URL 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 Currently patman uses a single global --patchwork-url flag. Add a patchwork_url field to each upstream so the URL can be looked up automatically from the database. Update upstream_add() and upstream_get_dict() to handle the new field and add upstream_get_patchwork_url() for direct lookup. Signed-off-by: Simon Glass --- tools/patman/cmdline.py | 6 +++- tools/patman/control.py | 28 +++++++++++++++---- tools/patman/cseries.py | 27 ++++++++++++++---- tools/patman/database.py | 34 +++++++++++++++++++---- tools/patman/test_cseries.py | 53 ++++++++++++++++++++++++++++++------ 5 files changed, 122 insertions(+), 26 deletions(-) diff --git a/tools/patman/cmdline.py b/tools/patman/cmdline.py index 090d9845ec1..f24d0aacdff 100644 --- a/tools/patman/cmdline.py +++ b/tools/patman/cmdline.py @@ -406,7 +406,7 @@ def add_upstream_subparser(subparsers): upstream = subparsers.add_parser('upstream', aliases=ALIASES['upstream'], help='Manage upstream destinations') upstream.defaults_cmds = [ - ['add', 'us', 'http://fred', 'U-Boot'], + ['add', 'us', 'http://fred', '-p', 'http://pw', 'U-Boot'], ['delete', 'us'], ] upstream_subparsers = upstream.add_subparsers(dest='subcmd') @@ -416,6 +416,10 @@ def add_upstream_subparser(subparsers): uadd.add_argument( 'url', help='URL to use for this upstream, e.g. ' "'https://gitlab.denx.de/u-boot/u-boot.git'") + uadd.add_argument( + '-p', '--patchwork-url', + help='URL of patchwork server for this upstream, e.g. ' + "'https://patchwork.ozlabs.org'") uadd.add_argument( 'project_name', nargs='?', help="Patchwork project name, e.g. 'U-Boot'") diff --git a/tools/patman/control.py b/tools/patman/control.py index c65fd3d5f1f..e1f52300a17 100644 --- a/tools/patman/control.py +++ b/tools/patman/control.py @@ -134,8 +134,18 @@ def do_series(args, test_db=None, pwork=None, cser=None): cser.open_database() if args.subcmd in needs_patchwork: if not pwork: - pwork = Patchwork(args.patchwork_url) ups = cser.get_series_upstream(args.series) + pw_url = None + if ups: + pw_url = cser.db.upstream_get_patchwork_url(ups) + if not pw_url: + pw_url = args.patchwork_url + if not pw_url: + raise ValueError( + 'No patchwork URL found for upstream ' + f"'{ups}'; use 'patman upstream add' with " + '-p or pass --patchwork-url') + pwork = Patchwork(pw_url) proj = cser.project_get(ups) if not proj: proj = cser.project_get() @@ -239,11 +249,9 @@ def upstream(args, test_db=None): try: cser.open_database() if args.subcmd == 'add': - pwork = None - if args.project_name: - pwork = Patchwork(args.patchwork_url) cser.upstream_add(args.remote_name, args.url, - args.project_name, pwork) + args.project_name, + patchwork_url=args.patchwork_url) elif args.subcmd == 'default': if args.unset: cser.upstream_set_default(None) @@ -279,7 +287,15 @@ def patchwork(args, test_db=None, pwork=None): if not args.remote: raise ValueError('Please specify the remote name') if not pwork: - pwork = Patchwork(args.patchwork_url) + pw_url = cser.db.upstream_get_patchwork_url(args.remote) + if not pw_url: + pw_url = args.patchwork_url + if not pw_url: + raise ValueError( + f"No patchwork URL for remote '{args.remote}'" + "; use 'patman upstream add' with -p" + ' or pass --patchwork-url') + pwork = Patchwork(pw_url) cser.project_set(pwork, args.project_name, ups=args.remote) elif args.subcmd == 'get-project': diff --git a/tools/patman/cseries.py b/tools/patman/cseries.py index c48267964a1..612ccfda7dc 100644 --- a/tools/patman/cseries.py +++ b/tools/patman/cseries.py @@ -16,6 +16,7 @@ from u_boot_pylib import terminal from u_boot_pylib import tout from patman import patchstream +from patman.patchwork import Patchwork from patman import cser_helper from patman.cser_helper import AUTOLINK, oid from patman import send @@ -1145,7 +1146,8 @@ class Cseries(cser_helper.CseriesHelper): self.rollback() tout.info('Dry run completed') - def upstream_add(self, name, url, project=None, pwork=None): + def upstream_add(self, name, url, project=None, pwork=None, + patchwork_url=None): """Add a new upstream tree Args: @@ -1154,12 +1156,21 @@ class Cseries(cser_helper.CseriesHelper): project (str or None): Patchwork project name to associate pwork (Patchwork or None): Patchwork object for looking up the project + patchwork_url (str or None): URL of the patchwork server for + this upstream """ - self.db.upstream_add(name, url) + self.db.upstream_add(name, url, patchwork_url) if project: + if not pwork: + if not patchwork_url: + raise ValueError( + 'Patchwork URL is required when setting a project') + pwork = Patchwork(patchwork_url) self.project_set(pwork, project, ups=name, quiet=True) self.commit() msg = f"Added upstream '{name}' ({url})" + if patchwork_url: + msg += f" patchwork '{patchwork_url}'" if project: msg += f" project '{project}'" tout.notice(msg) @@ -1167,14 +1178,20 @@ class Cseries(cser_helper.CseriesHelper): def upstream_list(self): """List the upstream repos - Shows a list of the repos, obtained from the database + Shows a list of the repos, obtained from the database, along with + any associated patchwork project """ udict = self.get_upstream_dict() for name, items in udict.items(): - url, is_default = items + url, is_default, patchwork_url = items default = 'default' if is_default else '' - print(f'{name:15.15} {default:8} {url}') + proj = self.db.patchwork_get(name) + proj_name = proj[0] if proj else '' + line = f'{name:10.10} {default:8} {proj_name:20} {url}' + if patchwork_url: + line += f' pw:{patchwork_url}' + print(line) def upstream_set_default(self, name): """Set the default upstream target diff --git a/tools/patman/database.py b/tools/patman/database.py index 2f21e62605f..f7ab2d84877 100644 --- a/tools/patman/database.py +++ b/tools/patman/database.py @@ -791,19 +791,21 @@ class Database: # pylint:disable=R0904 # upstream functions - def upstream_add(self, name, url): + def upstream_add(self, name, url, patchwork_url=None): """Add a new upstream record Args: name (str): Name of the tree url (str): URL for the tree + patchwork_url (str or None): URL of the patchwork server Raises: ValueError if the name already exists in the database """ try: self.execute( - 'INSERT INTO upstream (name, url) VALUES (?, ?)', (name, url)) + 'INSERT INTO upstream (name, url, patchwork_url) ' + 'VALUES (?, ?, ?)', (name, url, patchwork_url)) except sqlite3.IntegrityError as exc: if 'UNIQUE constraint failed: upstream.name' in str(exc): raise ValueError(f"Upstream '{name}' already exists") from exc @@ -853,18 +855,38 @@ class Database: # pylint:disable=R0904 self.rollback() raise ValueError(f"No such upstream '{name}'") + def upstream_get_patchwork_url(self, name): + """Get the patchwork URL for an upstream + + Args: + name (str): Upstream name + + Return: + str or None: Patchwork URL, or None if not set + """ + res = self.execute( + 'SELECT patchwork_url FROM upstream WHERE name = ?', (name,)) + rec = res.fetchone() + if rec: + return rec[0] + return None + def upstream_get_dict(self): """Get a list of upstream entries from the database Return: OrderedDict: key (str): upstream name - value (str): url + value: tuple: + str: url + bool: is_default + str or None: patchwork_url """ - res = self.execute('SELECT name, url, is_default FROM upstream') + res = self.execute( + 'SELECT name, url, is_default, patchwork_url FROM upstream') udict = OrderedDict() - for name, url, is_default in res.fetchall(): - udict[name] = url, is_default + for name, url, is_default, patchwork_url in res.fetchall(): + udict[name] = url, is_default, patchwork_url return udict # patchwork functions diff --git a/tools/patman/test_cseries.py b/tools/patman/test_cseries.py index 083727889f8..b14c6c22ddc 100644 --- a/tools/patman/test_cseries.py +++ b/tools/patman/test_cseries.py @@ -1730,14 +1730,14 @@ Tested-by: Mary Smith # yak cser.upstream_add('us', 'https://one') ulist = cser.get_upstream_dict() self.assertEqual(1, len(ulist)) - self.assertEqual(('https://one', None), ulist['us']) + self.assertEqual(('https://one', None, None), ulist['us']) with terminal.capture(): cser.upstream_add('ci', 'git@two') ulist = cser.get_upstream_dict() self.assertEqual(2, len(ulist)) - self.assertEqual(('https://one', None), ulist['us']) - self.assertEqual(('git@two', None), ulist['ci']) + self.assertEqual(('https://one', None, None), ulist['us']) + self.assertEqual(('git@two', None, None), ulist['ci']) # Try to add a duplicate with self.assertRaises(ValueError) as exc: @@ -1748,8 +1748,39 @@ Tested-by: Mary Smith # yak cser.upstream_list() lines = out.getvalue().splitlines() self.assertEqual(2, len(lines)) - self.assertEqual('us https://one', lines[0]) - self.assertEqual('ci git@two', lines[1]) + self.assertEqual( + 'us https://one', + lines[0]) + self.assertEqual( + 'ci git@two', + lines[1]) + + def test_upstream_add_patchwork_url(self): + """Test adding an upstream with a patchwork URL""" + cser = self.get_cser() + + with terminal.capture(): + cser.upstream_add('us', 'https://one', + patchwork_url='https://pw.example.com') + ulist = cser.get_upstream_dict() + self.assertEqual(1, len(ulist)) + self.assertEqual( + ('https://one', None, 'https://pw.example.com'), ulist['us']) + + # Check that the patchwork URL shows in the list + with terminal.capture() as (out, _): + cser.upstream_list() + lines = out.getvalue().splitlines() + self.assertEqual(1, len(lines)) + self.assertIn('pw:https://pw.example.com', lines[0]) + + # Check database lookup + pw_url = cser.db.upstream_get_patchwork_url('us') + self.assertEqual('https://pw.example.com', pw_url) + + # Non-existent upstream returns None + pw_url = cser.db.upstream_get_patchwork_url('nonexistent') + self.assertIsNone(pw_url) def test_upstream_add_cmdline(self): """Test adding an upsream with cmdline""" @@ -1760,7 +1791,9 @@ Tested-by: Mary Smith # yak self.run_args('upstream', 'list') lines = out.getvalue().splitlines() self.assertEqual(1, len(lines)) - self.assertEqual('us https://one', lines[0]) + self.assertEqual( + 'us https://one', + lines[0]) def test_upstream_default(self): """Operation of the default upstream""" @@ -1791,8 +1824,12 @@ Tested-by: Mary Smith # yak cser.upstream_list() lines = out.getvalue().splitlines() self.assertEqual(2, len(lines)) - self.assertEqual('us https://one', lines[0]) - self.assertEqual('ci default git@two', lines[1]) + self.assertEqual( + 'us https://one', + lines[0]) + self.assertEqual( + 'ci default git@two', + lines[1]) cser.upstream_set_default(None) self.assertIsNone(cser.upstream_get_default())