mirror of
https://source.denx.de/u-boot/u-boot.git
synced 2025-08-26 17:11:36 +02:00
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:
commit
cd0d3749af
@ -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__);
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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),
|
||||||
|
@ -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
|
||||||
|
@ -28,7 +28,10 @@ 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):
|
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.'''
|
||||||
|
|
||||||
|
parser = ArgumentParser(epilog=epilog)
|
||||||
parser.add_argument('-b', '--branch', type=str,
|
parser.add_argument('-b', '--branch', type=str,
|
||||||
help="Branch to process (by default, the current branch)")
|
help="Branch to process (by default, the current branch)")
|
||||||
parser.add_argument('-c', '--count', dest='count', type=int,
|
parser.add_argument('-c', '--count', dest='count', type=int,
|
||||||
@ -37,17 +40,21 @@ def AddCommonArgs(parser):
|
|||||||
help='Commits to skip at end of patch list')
|
help='Commits to skip at end of patch list')
|
||||||
parser.add_argument('-D', '--debug', action='store_true',
|
parser.add_argument('-D', '--debug', action='store_true',
|
||||||
help='Enabling debugging (provides a full traceback on error)')
|
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,
|
parser.add_argument('-s', '--start', dest='start', type=int,
|
||||||
default=0, help='Commit to start creating patches from (0 = HEAD)')
|
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')
|
||||||
|
|
||||||
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.'''
|
|
||||||
|
|
||||||
parser = ArgumentParser(epilog=epilog)
|
|
||||||
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)
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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.
|
||||||
|
@ -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.
|
||||||
|
@ -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()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user