Minor fixes/improvements to 'patman status'

-----BEGIN PGP SIGNATURE-----
 
 iQFFBAABCgAvFiEEslwAIq+Gp8wWVbYnfxc6PpAIreYFAl+xNpwRHHNqZ0BjaHJv
 bWl1bS5vcmcACgkQfxc6PpAIreby6QgA3KcBm+Iwyzkx7wID7R6PVaazKdesyCM6
 Y4dE7kOrXFkCnoXmS5szhrc7YKpWWKBkzHDvRbW9a+n5gTgvujX1OufrcMlEwI9O
 9XqhCmBb2pJ1nfGVxO+qI0hkKI/AgbXw2RedfvJpe2cXkIrzcap/vpmW8S7ri34+
 K9dUxFUMzoUwnY/96asMMF9NgW8AFZ8TE+maUK+B/wXSllb+8FoE7VXKasDkyNer
 qH8YkccU5aeWXYOxv9othxQ1Z1tt/0LX14YfLPbXn0tJhHUfoVSrWTzmqWad8gd4
 1jgSTKkS65Pa/2UqC7apMuGsEexw72kWqewGQxZtCZNphlESWckWmQ==
 =Dheq
 -----END PGP SIGNATURE-----

Merge tag 'dm-pull-15nov20' of git://git.denx.de/u-boot-dm

Minor fixes/improvements to 'patman status'
This commit is contained in:
Tom Rini 2020-11-15 10:13:22 -05:00
commit cd0d3749af
12 changed files with 149 additions and 76 deletions

View File

@ -495,18 +495,18 @@ int cros_ec_read_current_image(struct udevice *dev,
} }
static int cros_ec_wait_on_hash_done(struct udevice *dev, static int cros_ec_wait_on_hash_done(struct udevice *dev,
struct ec_params_vboot_hash *p,
struct ec_response_vboot_hash *hash) struct ec_response_vboot_hash *hash)
{ {
struct ec_params_vboot_hash p;
ulong start; ulong start;
start = get_timer(0); start = get_timer(0);
while (hash->status == EC_VBOOT_HASH_STATUS_BUSY) { while (hash->status == EC_VBOOT_HASH_STATUS_BUSY) {
mdelay(50); /* Insert some reasonable delay */ mdelay(50); /* Insert some reasonable delay */
p.cmd = EC_VBOOT_HASH_GET; p->cmd = EC_VBOOT_HASH_GET;
if (ec_command(dev, EC_CMD_VBOOT_HASH, 0, &p, sizeof(p), if (ec_command(dev, EC_CMD_VBOOT_HASH, 0, p, sizeof(*p), hash,
hash, sizeof(*hash)) < 0) sizeof(*hash)) < 0)
return -1; return -1;
if (get_timer(start) > CROS_EC_CMD_HASH_TIMEOUT_MS) { if (get_timer(start) > CROS_EC_CMD_HASH_TIMEOUT_MS) {
@ -530,7 +530,7 @@ int cros_ec_read_hash(struct udevice *dev, uint hash_offset,
return -1; return -1;
/* If the EC is busy calculating the hash, fidget until it's done. */ /* If the EC is busy calculating the hash, fidget until it's done. */
rv = cros_ec_wait_on_hash_done(dev, hash); rv = cros_ec_wait_on_hash_done(dev, &p, hash);
if (rv) if (rv)
return rv; return rv;
@ -553,9 +553,13 @@ int cros_ec_read_hash(struct udevice *dev, uint hash_offset,
hash, sizeof(*hash)) < 0) hash, sizeof(*hash)) < 0)
return -1; return -1;
rv = cros_ec_wait_on_hash_done(dev, hash); rv = cros_ec_wait_on_hash_done(dev, &p, hash);
if (rv) if (rv)
return rv; return rv;
if (hash->status != EC_VBOOT_HASH_STATUS_DONE) {
log_err("Hash did not complete, status=%d\n", hash->status);
return -EIO;
}
debug("%s: hash done\n", __func__); debug("%s: hash done\n", __func__);

View File

@ -25,13 +25,16 @@
#define debug_trace(fmt, b...) #define debug_trace(fmt, b...)
#endif #endif
/* Timeout waiting for a flash erase command to complete */
static const int CROS_EC_CMD_TIMEOUT_MS = 5000;
static int wait_for_sync(struct cros_ec_dev *dev) static int wait_for_sync(struct cros_ec_dev *dev)
{ {
unsigned long start; unsigned long start;
start = get_timer(0); start = get_timer(0);
while (inb(EC_LPC_ADDR_HOST_CMD) & EC_LPC_STATUS_BUSY_MASK) { while (inb(EC_LPC_ADDR_HOST_CMD) & EC_LPC_STATUS_BUSY_MASK) {
if (get_timer(start) > 1000) { if (get_timer(start) > CROS_EC_CMD_TIMEOUT_MS) {
debug("%s: Timeout waiting for CROS_EC sync\n", debug("%s: Timeout waiting for CROS_EC sync\n",
__func__); __func__);
return -1; return -1;

View File

@ -460,6 +460,16 @@ static int process_cmd(struct ec_state *ec,
case EC_CMD_ENTERING_MODE: case EC_CMD_ENTERING_MODE:
len = 0; len = 0;
break; break;
case EC_CMD_GET_NEXT_EVENT:
/*
* TODO:
* This driver emulates an old keyboard device supporting
* EC_CMD_MKBP_STATE. Current Chrome OS keyboards use
* EC_CMD_GET_NEXT_EVENT. Cf.
* "mkbp: Add support for buttons and switches"
* https://chromium.googlesource.com/chromiumos/platform/ec/+/87a071941b89e3f7fd3eb329b682e60b3fbd6c73
*/
return -EC_RES_INVALID_COMMAND;
default: default:
printf(" ** Unknown EC command %#02x\n", req_hdr->command); printf(" ** Unknown EC command %#02x\n", req_hdr->command);
return -1; return -1;

View File

@ -113,6 +113,7 @@ ignore_errors: True
process_tags: False process_tags: False
verbose: True verbose: True
smtp_server: /path/to/sendmail smtp_server: /path/to/sendmail
patchwork_server: https://patchwork.ozlabs.org
<<< <<<
@ -207,6 +208,12 @@ Series-links: [id | version:id]...
branch against patchwork to see what new reviews your series has branch against patchwork to see what new reviews your series has
collected ('patman status'). collected ('patman status').
Series-patchwork-url: url
This allows specifying the Patchwork URL for a branch. This overrides
both the setting files and the command-line argument. The URL should
include the protocol and web site, with no trailing slash, for example
'https://patchwork.ozlabs.org/project'
Cover-letter: Cover-letter:
This is the patch set title This is the patch set title
blah blah blah blah

View File

@ -177,7 +177,7 @@ def send(args):
args.smtp_server) args.smtp_server)
def patchwork_status(branch, count, start, end, dest_branch, force, def patchwork_status(branch, count, start, end, dest_branch, force,
show_comments): show_comments, url):
"""Check the status of patches in patchwork """Check the status of patches in patchwork
This finds the series in patchwork using the Series-link tag, checks for new This finds the series in patchwork using the Series-link tag, checks for new
@ -196,6 +196,8 @@ def patchwork_status(branch, count, start, end, dest_branch, force,
force (bool): With dest_branch, force overwriting an existing branch force (bool): With dest_branch, force overwriting an existing branch
show_comments (bool): True to display snippets from the comments show_comments (bool): True to display snippets from the comments
provided by reviewers provided by reviewers
url (str): URL of patchwork server, e.g. 'https://patchwork.ozlabs.org'.
This is ignored if the series provides a Series-patchwork-url tag.
Raises: Raises:
ValueError: if the branch has no Series-link value ValueError: if the branch has no Series-link value
@ -224,8 +226,12 @@ def patchwork_status(branch, count, start, end, dest_branch, force,
if not found: if not found:
raise ValueError('Series-links has no current version (without :)') raise ValueError('Series-links has no current version (without :)')
# Allow the series to override the URL
if 'patchwork_url' in series:
url = series.patchwork_url
# Import this here to avoid failing on other commands if the dependencies # Import this here to avoid failing on other commands if the dependencies
# are not present # are not present
from patman import status from patman import status
status.check_patchwork_status(series, found[0], branch, dest_branch, force, status.check_patchwork_status(series, found[0], branch, dest_branch, force,
show_comments) show_comments, url)

View File

@ -248,7 +248,7 @@ class TestFunctional(unittest.TestCase):
self.assertEqual(' Cc: %s' % rick, next(lines)) self.assertEqual(' Cc: %s' % rick, next(lines))
expected = ('Git command: git send-email --annotate ' expected = ('Git command: git send-email --annotate '
'--in-reply-to="%s" --to "u-boot@lists.denx.de" ' '--in-reply-to="%s" --to "u-boot@lists.denx.de" '
'--cc "%s" --cc-cmd "%s --cc-cmd %s" %s %s' '--cc "%s" --cc-cmd "%s send --cc-cmd %s" %s %s'
% (in_reply_to, stefan, sys.argv[0], cc_file, cover_fname, % (in_reply_to, stefan, sys.argv[0], cc_file, cover_fname,
' '.join(args))) ' '.join(args)))
self.assertEqual(expected, tools.ToUnicode(next(lines))) self.assertEqual(expected, tools.ToUnicode(next(lines)))
@ -625,11 +625,15 @@ diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c
os.chdir(orig_dir) os.chdir(orig_dir)
@staticmethod @staticmethod
def _fake_patchwork(subpath): def _fake_patchwork(url, subpath):
"""Fake Patchwork server for the function below """Fake Patchwork server for the function below
This handles accessing a series, providing a list consisting of a This handles accessing a series, providing a list consisting of a
single patch single patch
Args:
url (str): URL of patchwork server
subpath (str): URL subpath to use
""" """
re_series = re.match(r'series/(\d*)/$', subpath) re_series = re.match(r'series/(\d*)/$', subpath)
if re_series: if re_series:
@ -645,7 +649,7 @@ diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c
series = Series() series = Series()
with capture_sys_output() as (_, err): with capture_sys_output() as (_, err):
status.collect_patches(series, 1234, self._fake_patchwork) status.collect_patches(series, 1234, None, self._fake_patchwork)
self.assertIn('Warning: Patchwork reports 1 patches, series has 0', self.assertIn('Warning: Patchwork reports 1 patches, series has 0',
err.getvalue()) err.getvalue())
@ -655,7 +659,8 @@ diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c
series = Series() series = Series()
series.commits = [Commit('abcd')] series.commits = [Commit('abcd')]
patches = status.collect_patches(series, 1234, self._fake_patchwork) patches = status.collect_patches(series, 1234, None,
self._fake_patchwork)
self.assertEqual(1, len(patches)) self.assertEqual(1, len(patches))
patch = patches[0] patch = patches[0]
self.assertEqual('1', patch.id) self.assertEqual('1', patch.id)
@ -800,11 +805,15 @@ diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c
"Cannot find commit for patch 3 ('Subject 2')"], "Cannot find commit for patch 3 ('Subject 2')"],
warnings) warnings)
def _fake_patchwork2(self, subpath): def _fake_patchwork2(self, url, subpath):
"""Fake Patchwork server for the function below """Fake Patchwork server for the function below
This handles accessing series, patches and comments, providing the data This handles accessing series, patches and comments, providing the data
in self.patches to the caller in self.patches to the caller
Args:
url (str): URL of patchwork server
subpath (str): URL subpath to use
""" """
re_series = re.match(r'series/(\d*)/$', subpath) re_series = re.match(r'series/(\d*)/$', subpath)
re_patch = re.match(r'patches/(\d*)/$', subpath) re_patch = re.match(r'patches/(\d*)/$', subpath)
@ -861,12 +870,12 @@ diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c
# Check that the tags are picked up on the first patch # Check that the tags are picked up on the first patch
status.find_new_responses(new_rtag_list, review_list, 0, commit1, status.find_new_responses(new_rtag_list, review_list, 0, commit1,
patch1, self._fake_patchwork2) patch1, None, self._fake_patchwork2)
self.assertEqual(new_rtag_list[0], {'Reviewed-by': {self.joe}}) self.assertEqual(new_rtag_list[0], {'Reviewed-by': {self.joe}})
# Now the second patch # Now the second patch
status.find_new_responses(new_rtag_list, review_list, 1, commit2, status.find_new_responses(new_rtag_list, review_list, 1, commit2,
patch2, self._fake_patchwork2) patch2, None, self._fake_patchwork2)
self.assertEqual(new_rtag_list[1], { self.assertEqual(new_rtag_list[1], {
'Reviewed-by': {self.mary, self.fred}, 'Reviewed-by': {self.mary, self.fred},
'Tested-by': {self.leb}}) 'Tested-by': {self.leb}})
@ -876,7 +885,7 @@ diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c
new_rtag_list = [None] * count new_rtag_list = [None] * count
commit1.rtags = {'Reviewed-by': {self.joe}} commit1.rtags = {'Reviewed-by': {self.joe}}
status.find_new_responses(new_rtag_list, review_list, 0, commit1, status.find_new_responses(new_rtag_list, review_list, 0, commit1,
patch1, self._fake_patchwork2) patch1, None, self._fake_patchwork2)
self.assertEqual(new_rtag_list[0], {}) self.assertEqual(new_rtag_list[0], {})
# For the second commit, add Ed and Fred, so only Mary should be left # For the second commit, add Ed and Fred, so only Mary should be left
@ -884,7 +893,7 @@ diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c
'Tested-by': {self.leb}, 'Tested-by': {self.leb},
'Reviewed-by': {self.fred}} 'Reviewed-by': {self.fred}}
status.find_new_responses(new_rtag_list, review_list, 1, commit2, status.find_new_responses(new_rtag_list, review_list, 1, commit2,
patch2, self._fake_patchwork2) patch2, None, self._fake_patchwork2)
self.assertEqual(new_rtag_list[1], {'Reviewed-by': {self.mary}}) self.assertEqual(new_rtag_list[1], {'Reviewed-by': {self.mary}})
# Check that the output patches expectations: # Check that the output patches expectations:
@ -900,7 +909,7 @@ diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c
series.commits = [commit1, commit2] series.commits = [commit1, commit2]
terminal.SetPrintTestMode() terminal.SetPrintTestMode()
status.check_patchwork_status(series, '1234', None, None, False, False, status.check_patchwork_status(series, '1234', None, None, False, False,
self._fake_patchwork2) None, self._fake_patchwork2)
lines = iter(terminal.GetPrintTestLines()) lines = iter(terminal.GetPrintTestLines())
col = terminal.Color() col = terminal.Color()
self.assertEqual(terminal.PrintLine(' 1 Subject 1', col.BLUE), self.assertEqual(terminal.PrintLine(' 1 Subject 1', col.BLUE),
@ -935,11 +944,15 @@ diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c
'1 new response available in patchwork (use -d to write them to a new branch)', '1 new response available in patchwork (use -d to write them to a new branch)',
None), next(lines)) None), next(lines))
def _fake_patchwork3(self, subpath): def _fake_patchwork3(self, url, subpath):
"""Fake Patchwork server for the function below """Fake Patchwork server for the function below
This handles accessing series, patches and comments, providing the data This handles accessing series, patches and comments, providing the data
in self.patches to the caller in self.patches to the caller
Args:
url (str): URL of patchwork server
subpath (str): URL subpath to use
""" """
re_series = re.match(r'series/(\d*)/$', subpath) re_series = re.match(r'series/(\d*)/$', subpath)
re_patch = re.match(r'patches/(\d*)/$', subpath) re_patch = re.match(r'patches/(\d*)/$', subpath)
@ -1011,7 +1024,8 @@ diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c
terminal.SetPrintTestMode() terminal.SetPrintTestMode()
status.check_patchwork_status(series, '1234', branch, dest_branch, status.check_patchwork_status(series, '1234', branch, dest_branch,
False, False, self._fake_patchwork3, repo) False, False, None, self._fake_patchwork3,
repo)
lines = terminal.GetPrintTestLines() lines = terminal.GetPrintTestLines()
self.assertEqual(12, len(lines)) self.assertEqual(12, len(lines))
self.assertEqual( self.assertEqual(
@ -1214,7 +1228,7 @@ Reviewed-by: %s
series.commits = [commit1, commit2] series.commits = [commit1, commit2]
terminal.SetPrintTestMode() terminal.SetPrintTestMode()
status.check_patchwork_status(series, '1234', None, None, False, True, status.check_patchwork_status(series, '1234', None, None, False, True,
self._fake_patchwork2) None, self._fake_patchwork2)
lines = iter(terminal.GetPrintTestLines()) lines = iter(terminal.GetPrintTestLines())
col = terminal.Color() col = terminal.Color()
self.assertEqual(terminal.PrintLine(' 1 Subject 1', col.BLUE), self.assertEqual(terminal.PrintLine(' 1 Subject 1', col.BLUE),

View File

@ -455,21 +455,21 @@ def EmailPatches(series, cover_fname, args, dry_run, raise_on_error, cc_fname,
>>> EmailPatches(series, 'cover', ['p1', 'p2'], True, True, 'cc-fname', \ >>> EmailPatches(series, 'cover', ['p1', 'p2'], True, True, 'cc-fname', \
False, alias) False, alias)
'git send-email --annotate --to "f.bloggs@napier.co.nz" --cc \ 'git send-email --annotate --to "f.bloggs@napier.co.nz" --cc \
"m.poppins@cloud.net" --cc-cmd "./patman --cc-cmd cc-fname" cover p1 p2' "m.poppins@cloud.net" --cc-cmd "./patman send --cc-cmd cc-fname" cover p1 p2'
>>> EmailPatches(series, None, ['p1'], True, True, 'cc-fname', False, \ >>> EmailPatches(series, None, ['p1'], True, True, 'cc-fname', False, \
alias) alias)
'git send-email --annotate --to "f.bloggs@napier.co.nz" --cc \ 'git send-email --annotate --to "f.bloggs@napier.co.nz" --cc \
"m.poppins@cloud.net" --cc-cmd "./patman --cc-cmd cc-fname" p1' "m.poppins@cloud.net" --cc-cmd "./patman send --cc-cmd cc-fname" p1'
>>> series['cc'] = ['all'] >>> series['cc'] = ['all']
>>> EmailPatches(series, 'cover', ['p1', 'p2'], True, True, 'cc-fname', \ >>> EmailPatches(series, 'cover', ['p1', 'p2'], True, True, 'cc-fname', \
True, alias) True, alias)
'git send-email --annotate --to "this-is-me@me.com" --cc-cmd "./patman \ 'git send-email --annotate --to "this-is-me@me.com" --cc-cmd "./patman \
--cc-cmd cc-fname" cover p1 p2' send --cc-cmd cc-fname" cover p1 p2'
>>> EmailPatches(series, 'cover', ['p1', 'p2'], True, True, 'cc-fname', \ >>> EmailPatches(series, 'cover', ['p1', 'p2'], True, True, 'cc-fname', \
False, alias) 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 \ "f.bloggs@napier.co.nz" --cc "j.bloggs@napier.co.nz" --cc \
"m.poppins@cloud.net" --cc-cmd "./patman --cc-cmd cc-fname" cover p1 p2' "m.poppins@cloud.net" --cc-cmd "./patman send --cc-cmd cc-fname" cover p1 p2'
# Restore argv[0] since we clobbered it. # Restore argv[0] since we clobbered it.
>>> sys.argv[0] = _old_argv0 >>> sys.argv[0] = _old_argv0
@ -500,7 +500,7 @@ def EmailPatches(series, cover_fname, args, dry_run, raise_on_error, cc_fname,
cmd += to cmd += to
cmd += cc cmd += cc
cmd += ['--cc-cmd', '"%s --cc-cmd %s"' % (sys.argv[0], cc_fname)] cmd += ['--cc-cmd', '"%s send --cc-cmd %s"' % (sys.argv[0], cc_fname)]
if cover_fname: if cover_fname:
cmd.append(cover_fname) cmd.append(cover_fname)
cmd += args cmd += args

View File

@ -28,26 +28,33 @@ from patman import terminal
from patman import test_util from patman import test_util
from patman import test_checkpatch from patman import test_checkpatch
def AddCommonArgs(parser):
parser.add_argument('-b', '--branch', type=str,
help="Branch to process (by default, the current branch)")
parser.add_argument('-c', '--count', dest='count', type=int,
default=-1, help='Automatically create patches from top n commits')
parser.add_argument('-e', '--end', type=int, default=0,
help='Commits to skip at end of patch list')
parser.add_argument('-D', '--debug', action='store_true',
help='Enabling debugging (provides a full traceback on error)')
parser.add_argument('-s', '--start', dest='start', type=int,
default=0, help='Commit to start creating patches from (0 = HEAD)')
epilog = '''Create patches from commits in a branch, check them and email them epilog = '''Create patches from commits in a branch, check them and email them
as specified by tags you place in the commits. Use -n to do a dry run first.''' as specified by tags you place in the commits. Use -n to do a dry run first.'''
parser = ArgumentParser(epilog=epilog) parser = ArgumentParser(epilog=epilog)
parser.add_argument('-b', '--branch', type=str,
help="Branch to process (by default, the current branch)")
parser.add_argument('-c', '--count', dest='count', type=int,
default=-1, help='Automatically create patches from top n commits')
parser.add_argument('-e', '--end', type=int, default=0,
help='Commits to skip at end of patch list')
parser.add_argument('-D', '--debug', action='store_true',
help='Enabling debugging (provides a full traceback on error)')
parser.add_argument('-p', '--project', default=project.DetectProject(),
help="Project name; affects default option values and "
"aliases [default: %(default)s]")
parser.add_argument('-P', '--patchwork-url',
default='https://patchwork.ozlabs.org',
help='URL of patchwork server [default: %(default)s]')
parser.add_argument('-s', '--start', dest='start', type=int,
default=0, help='Commit to start creating patches from (0 = HEAD)')
parser.add_argument('-v', '--verbose', action='store_true', dest='verbose',
default=False, help='Verbose output of errors and warnings')
parser.add_argument('-H', '--full-help', action='store_true', dest='full_help',
default=False, help='Display the README file')
subparsers = parser.add_subparsers(dest='cmd') subparsers = parser.add_subparsers(dest='cmd')
send = subparsers.add_parser('send') send = subparsers.add_parser('send')
send.add_argument('-H', '--full-help', action='store_true', dest='full_help',
default=False, help='Display the README file')
send.add_argument('-i', '--ignore-errors', action='store_true', send.add_argument('-i', '--ignore-errors', action='store_true',
dest='ignore_errors', default=False, dest='ignore_errors', default=False,
help='Send patches email even if patch errors are found') help='Send patches email even if patch errors are found')
@ -58,15 +65,10 @@ send.add_argument('-m', '--no-maintainers', action='store_false',
help="Don't cc the file maintainers automatically") help="Don't cc the file maintainers automatically")
send.add_argument('-n', '--dry-run', action='store_true', dest='dry_run', send.add_argument('-n', '--dry-run', action='store_true', dest='dry_run',
default=False, help="Do a dry run (create but don't email patches)") default=False, help="Do a dry run (create but don't email patches)")
send.add_argument('-p', '--project', default=project.DetectProject(),
help="Project name; affects default option values and "
"aliases [default: %(default)s]")
send.add_argument('-r', '--in-reply-to', type=str, action='store', send.add_argument('-r', '--in-reply-to', type=str, action='store',
help="Message ID that this series is in reply to") help="Message ID that this series is in reply to")
send.add_argument('-t', '--ignore-bad-tags', action='store_true', send.add_argument('-t', '--ignore-bad-tags', action='store_true',
default=False, help='Ignore bad tags / aliases') default=False, help='Ignore bad tags / aliases')
send.add_argument('-v', '--verbose', action='store_true', dest='verbose',
default=False, help='Verbose output of errors and warnings')
send.add_argument('-T', '--thread', action='store_true', dest='thread', send.add_argument('-T', '--thread', action='store_true', dest='thread',
default=False, help='Create patches as a single thread') default=False, help='Create patches as a single thread')
send.add_argument('--cc-cmd', dest='cc_cmd', type=str, action='store', send.add_argument('--cc-cmd', dest='cc_cmd', type=str, action='store',
@ -81,14 +83,12 @@ send.add_argument('--no-tags', action='store_false', dest='process_tags',
default=True, help="Don't process subject tags as aliases") default=True, help="Don't process subject tags as aliases")
send.add_argument('--smtp-server', type=str, send.add_argument('--smtp-server', type=str,
help="Specify the SMTP server to 'git send-email'") help="Specify the SMTP server to 'git send-email'")
AddCommonArgs(send)
send.add_argument('patchfiles', nargs='*') send.add_argument('patchfiles', nargs='*')
test_parser = subparsers.add_parser('test', help='Run tests') test_parser = subparsers.add_parser('test', help='Run tests')
test_parser.add_argument('testname', type=str, default=None, nargs='?', test_parser.add_argument('testname', type=str, default=None, nargs='?',
help="Specify the test to run") help="Specify the test to run")
AddCommonArgs(test_parser)
status = subparsers.add_parser('status', status = subparsers.add_parser('status',
help='Check status of patches in patchwork') help='Check status of patches in patchwork')
@ -98,16 +98,24 @@ status.add_argument('-d', '--dest-branch', type=str,
help='Name of branch to create with collected responses') help='Name of branch to create with collected responses')
status.add_argument('-f', '--force', action='store_true', status.add_argument('-f', '--force', action='store_true',
help='Force overwriting an existing branch') help='Force overwriting an existing branch')
AddCommonArgs(status)
# Parse options twice: first to get the project and second to handle # Parse options twice: first to get the project and second to handle
# defaults properly (which depends on project). # defaults properly (which depends on project)
# Use parse_known_args() in case 'cmd' is omitted
argv = sys.argv[1:] argv = sys.argv[1:]
if len(argv) < 1 or argv[0].startswith('-'): args, rest = parser.parse_known_args(argv)
argv = ['send'] + argv
args = parser.parse_args(argv)
if hasattr(args, 'project'): if hasattr(args, 'project'):
settings.Setup(gitutil, send, args.project, '') settings.Setup(gitutil, parser, args.project, '')
args, rest = parser.parse_known_args(argv)
# If we have a command, it is safe to parse all arguments
if args.cmd:
args = parser.parse_args(argv)
else:
# No command, so insert it after the known arguments and before the ones
# that presumably relate to the 'send' subcommand
nargs = len(rest)
argv = argv[:-nargs] + ['send'] + rest
args = parser.parse_args(argv) args = parser.parse_args(argv)
if __name__ != "__main__": if __name__ != "__main__":
@ -174,7 +182,7 @@ elif args.cmd == 'status':
try: try:
control.patchwork_status(args.branch, args.count, args.start, args.end, control.patchwork_status(args.branch, args.count, args.start, args.end,
args.dest_branch, args.force, args.dest_branch, args.force,
args.show_comments) args.show_comments, args.patchwork_url)
except Exception as e: except Exception as e:
terminal.Print('patman: %s: %s' % (type(e).__name__, e), terminal.Print('patman: %s: %s' % (type(e).__name__, e),
colour=terminal.Color.RED) colour=terminal.Color.RED)

View File

@ -452,8 +452,8 @@ class PatchStream:
if self.is_log: if self.is_log:
if self.commit.change_id: if self.commit.change_id:
raise ValueError( raise ValueError(
"%s: Two Change-Ids: '%s' vs. '%s'" % self.commit.hash, "%s: Two Change-Ids: '%s' vs. '%s'" %
self.commit.change_id, value) (self.commit.hash, self.commit.change_id, value))
self.commit.change_id = value self.commit.change_id = value
self.skip_blank = True self.skip_blank = True

View File

@ -16,7 +16,7 @@ from patman import tools
# Series-xxx tags that we understand # Series-xxx tags that we understand
valid_series = ['to', 'cc', 'version', 'changes', 'prefix', 'notes', 'name', valid_series = ['to', 'cc', 'version', 'changes', 'prefix', 'notes', 'name',
'cover_cc', 'process_log', 'links'] 'cover_cc', 'process_log', 'links', 'patchwork_url']
class Series(dict): class Series(dict):
"""Holds information about a patch series, including all tags. """Holds information about a patch series, including all tags.

View File

@ -7,6 +7,7 @@ try:
except: except:
import ConfigParser import ConfigParser
import argparse
import os import os
import re import re
@ -216,10 +217,10 @@ nxp = Zhikang Zhang <zhikang.zhang@nxp.com>
''' % (name, email), file=f) ''' % (name, email), file=f)
f.close(); f.close();
def _UpdateDefaults(parser, config): def _UpdateDefaults(main_parser, config):
"""Update the given OptionParser defaults based on config. """Update the given OptionParser defaults based on config.
We'll walk through all of the settings from the parser We'll walk through all of the settings from all parsers.
For each setting we'll look for a default in the option parser. For each setting we'll look for a default in the option parser.
If it's found we'll update the option parser default. If it's found we'll update the option parser default.
@ -228,13 +229,24 @@ def _UpdateDefaults(parser, config):
say. say.
Args: Args:
parser: An instance of an OptionParser whose defaults will be parser: An instance of an ArgumentParser whose defaults will be
updated. updated.
config: An instance of _ProjectConfigParser that we will query config: An instance of _ProjectConfigParser that we will query
for settings. for settings.
""" """
defaults = parser.parse_known_args()[0] # Find all the parsers and subparsers
defaults = vars(defaults) parsers = [main_parser]
parsers += [subparser for action in main_parser._actions
if isinstance(action, argparse._SubParsersAction)
for _, subparser in action.choices.items()]
# Collect the defaults from each parser
defaults = {}
for parser in parsers:
pdefs = parser.parse_known_args()[0]
defaults.update(vars(pdefs))
# Go through the settings and collect defaults
for name, val in config.items('settings'): for name, val in config.items('settings'):
if name in defaults: if name in defaults:
default_val = defaults[name] default_val = defaults[name]
@ -242,10 +254,14 @@ def _UpdateDefaults(parser, config):
val = config.getboolean('settings', name) val = config.getboolean('settings', name)
elif isinstance(default_val, int): elif isinstance(default_val, int):
val = config.getint('settings', name) val = config.getint('settings', name)
elif isinstance(default_val, str):
val = config.get('settings', name)
defaults[name] = val defaults[name] = val
else: else:
print("WARNING: Unknown setting %s" % name) print("WARNING: Unknown setting %s" % name)
parser.set_defaults(**defaults)
# Set all the defaults (this propagates through all subparsers)
main_parser.set_defaults(**defaults)
def _ReadAliasFile(fname): def _ReadAliasFile(fname):
"""Read in the U-Boot git alias file if it exists. """Read in the U-Boot git alias file if it exists.

View File

@ -198,10 +198,11 @@ def compare_with_series(series, patches):
return patch_for_commit, commit_for_patch, warnings return patch_for_commit, commit_for_patch, warnings
def call_rest_api(subpath): def call_rest_api(url, subpath):
"""Call the patchwork API and return the result as JSON """Call the patchwork API and return the result as JSON
Args: Args:
url (str): URL of patchwork server, e.g. 'https://patchwork.ozlabs.org'
subpath (str): URL subpath to use subpath (str): URL subpath to use
Returns: Returns:
@ -210,13 +211,13 @@ def call_rest_api(subpath):
Raises: Raises:
ValueError: the URL could not be read ValueError: the URL could not be read
""" """
url = 'https://patchwork.ozlabs.org/api/1.2/%s' % subpath full_url = '%s/api/1.2/%s' % (url, subpath)
response = requests.get(url) response = requests.get(full_url)
if response.status_code != 200: if response.status_code != 200:
raise ValueError("Could not read URL '%s'" % url) raise ValueError("Could not read URL '%s'" % full_url)
return response.json() return response.json()
def collect_patches(series, series_id, rest_api=call_rest_api): def collect_patches(series, series_id, url, rest_api=call_rest_api):
"""Collect patch information about a series from patchwork """Collect patch information about a series from patchwork
Uses the Patchwork REST API to collect information provided by patchwork Uses the Patchwork REST API to collect information provided by patchwork
@ -226,6 +227,7 @@ def collect_patches(series, series_id, rest_api=call_rest_api):
series (Series): Series object corresponding to the local branch series (Series): Series object corresponding to the local branch
containing the series containing the series
series_id (str): Patch series ID number series_id (str): Patch series ID number
url (str): URL of patchwork server, e.g. 'https://patchwork.ozlabs.org'
rest_api (function): API function to call to access Patchwork, for rest_api (function): API function to call to access Patchwork, for
testing testing
@ -236,7 +238,7 @@ def collect_patches(series, series_id, rest_api=call_rest_api):
ValueError: if the URL could not be read or the web page does not follow ValueError: if the URL could not be read or the web page does not follow
the expected structure the expected structure
""" """
data = rest_api('series/%s/' % series_id) data = rest_api(url, 'series/%s/' % series_id)
# Get all the rows, which are patches # Get all the rows, which are patches
patch_dict = data['patches'] patch_dict = data['patches']
@ -261,7 +263,7 @@ def collect_patches(series, series_id, rest_api=call_rest_api):
patches = sorted(patches, key=lambda x: x.seq) patches = sorted(patches, key=lambda x: x.seq)
return patches return patches
def find_new_responses(new_rtag_list, review_list, seq, cmt, patch, def find_new_responses(new_rtag_list, review_list, seq, cmt, patch, url,
rest_api=call_rest_api): rest_api=call_rest_api):
"""Find new rtags collected by patchwork that we don't know about """Find new rtags collected by patchwork that we don't know about
@ -279,6 +281,7 @@ def find_new_responses(new_rtag_list, review_list, seq, cmt, patch,
seq (int): Position in new_rtag_list to update seq (int): Position in new_rtag_list to update
cmt (Commit): Commit object for this commit cmt (Commit): Commit object for this commit
patch (Patch): Corresponding Patch object for this patch patch (Patch): Corresponding Patch object for this patch
url (str): URL of patchwork server, e.g. 'https://patchwork.ozlabs.org'
rest_api (function): API function to call to access Patchwork, for rest_api (function): API function to call to access Patchwork, for
testing testing
""" """
@ -286,14 +289,14 @@ def find_new_responses(new_rtag_list, review_list, seq, cmt, patch,
return return
# Get the content for the patch email itself as well as all comments # Get the content for the patch email itself as well as all comments
data = rest_api('patches/%s/' % patch.id) data = rest_api(url, 'patches/%s/' % patch.id)
pstrm = PatchStream.process_text(data['content'], True) pstrm = PatchStream.process_text(data['content'], True)
rtags = collections.defaultdict(set) rtags = collections.defaultdict(set)
for response, people in pstrm.commit.rtags.items(): for response, people in pstrm.commit.rtags.items():
rtags[response].update(people) rtags[response].update(people)
data = rest_api('patches/%s/comments/' % patch.id) data = rest_api(url, 'patches/%s/comments/' % patch.id)
reviews = [] reviews = []
for comment in data: for comment in data:
@ -407,7 +410,7 @@ def create_branch(series, new_rtag_list, branch, dest_branch, overwrite,
return num_added return num_added
def check_patchwork_status(series, series_id, branch, dest_branch, force, def check_patchwork_status(series, series_id, branch, dest_branch, force,
show_comments, rest_api=call_rest_api, show_comments, url, rest_api=call_rest_api,
test_repo=None): test_repo=None):
"""Check the status of a series on Patchwork """Check the status of a series on Patchwork
@ -421,11 +424,12 @@ def check_patchwork_status(series, series_id, branch, dest_branch, force,
dest_branch (str): Name of new branch to create, or None dest_branch (str): Name of new branch to create, or None
force (bool): True to force overwriting dest_branch if it exists force (bool): True to force overwriting dest_branch if it exists
show_comments (bool): True to show the comments on each patch show_comments (bool): True to show the comments on each patch
url (str): URL of patchwork server, e.g. 'https://patchwork.ozlabs.org'
rest_api (function): API function to call to access Patchwork, for rest_api (function): API function to call to access Patchwork, for
testing testing
test_repo (pygit2.Repository): Repo to use (use None unless testing) test_repo (pygit2.Repository): Repo to use (use None unless testing)
""" """
patches = collect_patches(series, series_id, rest_api) patches = collect_patches(series, series_id, url, rest_api)
col = terminal.Color() col = terminal.Color()
count = len(series.commits) count = len(series.commits)
new_rtag_list = [None] * count new_rtag_list = [None] * count
@ -440,7 +444,8 @@ def check_patchwork_status(series, series_id, branch, dest_branch, force,
with concurrent.futures.ThreadPoolExecutor(max_workers=16) as executor: with concurrent.futures.ThreadPoolExecutor(max_workers=16) as executor:
futures = executor.map( futures = executor.map(
find_new_responses, repeat(new_rtag_list), repeat(review_list), find_new_responses, repeat(new_rtag_list), repeat(review_list),
range(count), series.commits, patch_list, repeat(rest_api)) range(count), series.commits, patch_list, repeat(url),
repeat(rest_api))
for fresponse in futures: for fresponse in futures:
if fresponse: if fresponse:
raise fresponse.exception() raise fresponse.exception()