From: Simon Glass <simon.glass@canonical.com> Add a 'set' subcommand for upstream that allows updating individual fields on an existing upstream without having to delete and re-add it. Supports -p/--patchwork-url, -I/--identity, -t/--series-to, -m/--no-maintainers, --maintainers, --no-tags and --tags. For example: patman upstream set us -I chromium -t concept -m Signed-off-by: Simon Glass <simon.glass@canonical.com> --- tools/patman/cmdline.py | 25 ++++++++++++++++++ tools/patman/control.py | 19 ++++++++++++++ tools/patman/cseries.py | 14 ++++++++++ tools/patman/database.py | 34 ++++++++++++++++++++++++ tools/patman/test_cseries.py | 50 ++++++++++++++++++++++++++++++++++++ 5 files changed, 142 insertions(+) diff --git a/tools/patman/cmdline.py b/tools/patman/cmdline.py index 6141f106fde..5bf917e4712 100644 --- a/tools/patman/cmdline.py +++ b/tools/patman/cmdline.py @@ -411,6 +411,7 @@ def add_upstream_subparser(subparsers): upstream.defaults_cmds = [ ['add', 'us', 'http://fred', '-p', 'http://pw', 'U-Boot'], ['delete', 'us'], + ['set', 'us'], ] upstream_subparsers = upstream.add_subparsers(dest='subcmd') uadd = upstream_subparsers.add_parser('add') @@ -443,6 +444,30 @@ def add_upstream_subparser(subparsers): 'remote_name', help="Git remote name used for this upstream, e.g. 'us'") upstream_subparsers.add_parser('ls', aliases=['list']) + uset = upstream_subparsers.add_parser('set') + uset.add_argument('remote_name', + help="Git remote name used for this upstream, e.g. 'us'") + uset.add_argument( + '-p', '--patchwork-url', + help='URL of patchwork server for this upstream') + uset.add_argument( + '-I', '--identity', + help="Git sendemail identity to use, e.g. 'chromium'") + uset.add_argument( + '-t', '--series-to', + help="Patman alias for the To address, e.g. 'u-boot'") + uset.add_argument( + '-m', '--no-maintainers', action='store_true', default=None, + help='Skip get_maintainer.pl for this upstream') + uset.add_argument( + '--maintainers', action='store_true', default=None, + help='Enable get_maintainer.pl for this upstream') + uset.add_argument( + '--no-tags', action='store_true', default=None, + help='Skip subject-tag alias processing for this upstream') + uset.add_argument( + '--tags', action='store_true', default=None, + help='Enable subject-tag alias processing for this upstream') udef = upstream_subparsers.add_parser('default') udef.add_argument('-u', '--unset', action='store_true', help='Unset the default upstream') diff --git a/tools/patman/control.py b/tools/patman/control.py index 689fd732dec..cabd2138bb3 100644 --- a/tools/patman/control.py +++ b/tools/patman/control.py @@ -267,6 +267,25 @@ def upstream(args, test_db=None): print(result if result else 'unset') elif args.subcmd == 'delete': cser.upstream_delete(args.remote_name) + elif args.subcmd == 'set': + kwargs = {} + if args.patchwork_url is not None: + kwargs['patchwork_url'] = args.patchwork_url + if args.identity is not None: + kwargs['identity'] = args.identity + if args.series_to is not None: + kwargs['series_to'] = args.series_to + if args.no_maintainers: + kwargs['no_maintainers'] = True + elif args.maintainers: + kwargs['no_maintainers'] = False + if args.no_tags: + kwargs['no_tags'] = True + elif args.tags: + kwargs['no_tags'] = False + if not kwargs: + raise ValueError('No settings to update') + cser.upstream_set(args.remote_name, **kwargs) elif args.subcmd == 'ls': cser.upstream_list() else: diff --git a/tools/patman/cseries.py b/tools/patman/cseries.py index 36f0b432073..588e83298bd 100644 --- a/tools/patman/cseries.py +++ b/tools/patman/cseries.py @@ -1237,6 +1237,20 @@ class Cseries(cser_helper.CseriesHelper): line += ' no-tags' print(line) + def upstream_set(self, name, **kwargs): + """Update settings on an existing upstream + + See Database.upstream_set() for permitted kwargs. + + Args: + name (str): Name of the upstream remote to update + kwargs: Fields to update + """ + self.db.upstream_set(name, **kwargs) + self.commit() + parts = [f'{k}={v!r}' for k, v in kwargs.items()] + tout.notice(f"Updated upstream '{name}': {', '.join(parts)}") + def upstream_set_default(self, name): """Set the default upstream target diff --git a/tools/patman/database.py b/tools/patman/database.py index 19df3c3bc82..dcd39ea2c69 100644 --- a/tools/patman/database.py +++ b/tools/patman/database.py @@ -863,6 +863,40 @@ class Database: # pylint:disable=R0904 self.rollback() raise ValueError(f"No such upstream '{name}'") + def upstream_set(self, name, **kwargs): + """Update fields on an existing upstream + + Args: + name (str): Name of the upstream remote to update + kwargs: Fields to update, each being one of: + patchwork_url (str): URL of the patchwork server, e.g. + 'patchwork.ozlabs.org' + identity (str): Git sendemail identity to use when + sending, corresponding to a [sendemail "<identity>"] + section in .gitconfig + series_to (str): Mailing-list address to use as the + default To: for this upstream + no_maintainers (bool): True to skip + get_maintainer.pl when sending + no_tags (bool): True to skip processing of subject + tags (e.g. 'dm:') when sending + + Raises: + ValueError: Upstream does not exist or invalid field + """ + valid = {'patchwork_url', 'identity', 'series_to', + 'no_maintainers', 'no_tags'} + invalid = set(kwargs) - valid + if invalid: + raise ValueError(f"Invalid upstream field(s): {invalid}") + for field, value in kwargs.items(): + self.execute( + f'UPDATE upstream SET {field} = ? WHERE name = ?', + (value, name)) + if self.rowcount() != 1: + self.rollback() + raise ValueError(f"No such upstream '{name}'") + def upstream_get_patchwork_url(self, name): """Get the patchwork URL for an upstream diff --git a/tools/patman/test_cseries.py b/tools/patman/test_cseries.py index 8f56627958e..fcfe6610321 100644 --- a/tools/patman/test_cseries.py +++ b/tools/patman/test_cseries.py @@ -1813,6 +1813,56 @@ Tested-by: Mary Smith <msmith@wibble.com> # yak 'us https://one', lines[0]) + def test_upstream_set(self): + """Test updating settings on an existing upstream""" + cser = self.get_cser() + + with terminal.capture(): + cser.upstream_add('us', 'https://one') + + # Set identity and series_to + with terminal.capture(): + cser.upstream_set('us', identity='chromium', series_to='concept') + settings = cser.db.upstream_get_send_settings('us') + self.assertEqual('chromium', settings[0]) + self.assertEqual('concept', settings[1]) + + # Set boolean flags + with terminal.capture(): + cser.upstream_set('us', no_maintainers=True, no_tags=True) + settings = cser.db.upstream_get_send_settings('us') + self.assertTrue(settings[2]) + self.assertTrue(settings[3]) + + # Clear boolean flags + with terminal.capture(): + cser.upstream_set('us', no_maintainers=False, no_tags=False) + settings = cser.db.upstream_get_send_settings('us') + self.assertFalse(settings[2]) + self.assertFalse(settings[3]) + + # Non-existent upstream + with self.assertRaises(ValueError) as exc: + cser.upstream_set('nonexistent', identity='x') + self.assertIn('nonexistent', str(exc.exception)) + + def test_upstream_set_cmdline(self): + """Test upstream set via the command line""" + with terminal.capture(): + self.run_args('upstream', 'add', 'us', 'https://one') + + with terminal.capture(): + self.run_args('upstream', 'set', 'us', '-I', 'chromium', + '-t', 'concept', '-m', '--no-tags') + + with terminal.capture() as (out, _): + self.run_args('upstream', 'list') + line = out.getvalue().strip() + self.assertIn('id:chromium', line) + self.assertIn('to:concept', line) + self.assertIn('no-maintainers', line) + self.assertIn('no-tags', line) + def test_upstream_default(self): """Operation of the default upstream""" cser = self.get_cser() -- 2.43.0