aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKonstantin Ryabitsev <konstantin@linuxfoundation.org>2024-03-21 17:24:54 -0400
committerKonstantin Ryabitsev <konstantin@linuxfoundation.org>2024-03-21 17:24:54 -0400
commit43bf93cba1bd59a130a5f62154c65655377cece9 (patch)
treea95a25b454844e298d9a13bec8b39aac07227276
parent14bf644c74114cb69672cafde9d8a0d8e80f6bfa (diff)
downloadb4-43bf93cba1bd59a130a5f62154c65655377cece9.tar.gz
ez: add initial support for prep --check-deps
Check that all deps can be resolved on the server. We don't yet check if we can actually apply them, because this requires using a different base-commit than what we know about. Signed-off-by: Konstantin Ryabitsev <konstantin@linuxfoundation.org>
-rw-r--r--src/b4/__init__.py43
-rw-r--r--src/b4/command.py4
-rw-r--r--src/b4/ez.py88
3 files changed, 128 insertions, 7 deletions
diff --git a/src/b4/__init__.py b/src/b4/__init__.py
index 3f21912..169b461 100644
--- a/src/b4/__init__.py
+++ b/src/b4/__init__.py
@@ -110,7 +110,7 @@ LOREADDR = 'https://lore.kernel.org'
DEFAULT_CONFIG = {
'midmask': LOREADDR + '/all/%s',
'linkmask': LOREADDR + '/r/%s',
- 'searchmask': LOREADDR + '/all/?x=m&t=1&q=%s',
+ 'searchmask': LOREADDR + '/all/?x=m&q=%s',
# You can override the format for the Link: trailer, e.g.
# if you would rather use the Message-Id trailer. It takes the
# message-id as the expansion for %s
@@ -3167,13 +3167,16 @@ def mailsplit_bytes(bmbox: bytes, outdir: str, pipesep: Optional[str] = None) ->
return msgs
-def get_pi_search_results(query: str, nocache: bool = False,
- message: Optional[str] = None) -> Optional[List[email.message.Message]]:
+def get_pi_search_results(query: str, nocache: bool = False, message: Optional[str] = None,
+ full_threads: bool = True) -> Optional[List[email.message.Message]]:
config = get_main_config()
searchmask = config.get('searchmask')
if not searchmask:
logger.critical('b4.searchmask is not defined')
return None
+ if full_threads and 't=1' not in searchmask:
+ logger.debug('full_threads specified, adding t=1')
+ searchmask = f'{searchmask}&t=1'
msgs = list()
query = urllib.parse.quote_plus(query)
query_url = searchmask % query
@@ -3202,7 +3205,7 @@ def get_pi_search_results(query: str, nocache: bool = False,
t_mbox = gzip.decompress(resp.content)
resp.close()
if not len(t_mbox):
- logger.critical('No messages found for that query')
+ logger.info('No messages found for that query')
return None
return split_and_dedupe_pi_results(t_mbox, cachedir=cachedir)
@@ -3234,6 +3237,38 @@ def split_and_dedupe_pi_results(t_mbox: bytes, cachedir: Optional[str] = None) -
return msgs
+def get_series_by_change_id(change_id: str, nocache: bool = False) -> Optional['LoreMailbox']:
+ q = f'nq:"change-id:{change_id}"'
+ q_msgs = get_pi_search_results(q, nocache=nocache, full_threads=False)
+ if not q_msgs:
+ return None
+ lmbx = LoreMailbox()
+ for q_msg in q_msgs:
+ body, bcharset = LoreMessage.get_payload(q_msg)
+ if not re.search(rf'^\s*change-id:\s*{change_id}$', body, flags=re.M | re.I):
+ logger.debug('No change-id match for %s', q_msg.get('Subject', '(no subject)'))
+ continue
+ q_msgid = LoreMessage.get_clean_msgid(q_msg)
+ t_msgs = get_pi_thread_by_msgid(q_msgid, nocache=nocache)
+ if t_msgs:
+ for t_msg in t_msgs:
+ lmbx.add_message(t_msg)
+
+ return lmbx
+
+
+def get_series_by_patch_id(patch_id: str, nocache: bool = False) -> Optional['LoreMailbox']:
+ q = f'patchid:{patch_id}'
+ q_msgs = get_pi_search_results(q, nocache=nocache)
+ if not q_msgs:
+ return None
+ lmbx = LoreMailbox()
+ for q_msg in q_msgs:
+ lmbx.add_message(q_msg)
+
+ return lmbx
+
+
def get_pi_thread_by_url(t_mbx_url: str, nocache: bool = False) -> Optional[List[email.message.Message]]:
msgs = list()
cachedir = get_cache_file(t_mbx_url, 'pi.msgs')
diff --git a/src/b4/command.py b/src/b4/command.py
index adb49cd..5dc0184 100644
--- a/src/b4/command.py
+++ b/src/b4/command.py
@@ -294,6 +294,8 @@ def setup_parser() -> argparse.ArgumentParser:
help='Force revision to be this number instead')
sp_prep.add_argument('--set-prefixes', metavar='PREFIX', nargs='+',
help='Extra prefixes to add to [PATCH] (e.g.: RFC mydrv)')
+ sp_prep.add_argument('-C', '--no-cache', dest='nocache', action='store_true', default=False,
+ help='Do not use local cache when performing remote queries')
spp_g = sp_prep.add_mutually_exclusive_group()
spp_g.add_argument('-p', '--format-patch', metavar='OUTPUT_DIR',
@@ -302,6 +304,8 @@ def setup_parser() -> argparse.ArgumentParser:
help='Edit the cover letter in your defined $EDITOR (or core.editor)')
spp_g.add_argument('--edit-deps', action='store_true', default=False,
help='Edit the series dependencies in your defined $EDITOR (or core.editor)')
+ spp_g.add_argument('--check-deps', action='store_true', default=False,
+ help='Run checks for any defined series dependencies')
spp_g.add_argument('--show-revision', action='store_true', default=False,
help='Show current series revision number')
spp_g.add_argument('--compare-to', metavar='vN',
diff --git a/src/b4/ez.py b/src/b4/ez.py
index 35e5e0d..133e2cc 100644
--- a/src/b4/ez.py
+++ b/src/b4/ez.py
@@ -786,11 +786,12 @@ def edit_deps() -> None:
new_data = new_bdata.decode(errors='replace').strip()
prereqs = list()
if len(new_data):
- for entry in new_data.split('\n'):
+ for line in new_data.split('\n'):
+ entry = line.strip()
if entry.startswith('patch-id:') or entry.startswith('change-id:') or entry.startswith('message-id:'):
prereqs.append(entry)
- elif entry.startswith('#'):
- logger.debug('Ignoring comment: %s', entry)
+ elif not entry or entry.startswith('#'):
+ logger.debug('Ignoring: %s', entry)
else:
logger.critical('Unknown dependency format, ignored:')
logger.critical(entry)
@@ -799,6 +800,84 @@ def edit_deps() -> None:
store_cover(cover, tracking)
+def check_deps(cmdargs: argparse.Namespace) -> None:
+ cover, tracking = load_cover()
+ prereqs = tracking['series'].get('prerequisites', list())
+ res = dict()
+ known_patch_ids = set()
+ for prereq in prereqs:
+ logger.info('Checking %s', prereq)
+ chunks = prereq.split(':')
+ parts = [x.strip() for x in chunks]
+
+ if parts[0] == 'change-id':
+ change_id = parts[1]
+ lmbx = b4.get_series_by_change_id(change_id, nocache=cmdargs.nocache)
+ if not lmbx:
+ logger.debug('FAIL: No such change-id found: %s', change_id)
+ res[prereq] = (False, 'No matching change-id found on the server')
+ continue
+ if len(parts) > 2:
+ logger.debug('Checking if %s is the latest series', parts[2])
+ matches = re.search(r'^v?(\d+)', parts[2])
+ if matches:
+ wantser = int(matches.groups()[0])
+ if wantser not in lmbx.series:
+ logger.debug('FAIL: No matching series %s for change-id %s', wantser, change_id)
+ res[prereq] = (False, f'No version {wantser} found for change-id {change_id}')
+ continue
+ # Is it the latest version?
+ maxser = max(lmbx.series.keys())
+ if wantser < maxser:
+ logger.debug('Fail: Newer version v%s available for change-id %s', maxser, change_id)
+ res[prereq] = (False, f'v{maxser} available for change-id {change_id} (you have: v{wantser})')
+ continue
+ logger.debug('Pass: change-id %s found and is the latest posted series', change_id)
+ res[prereq] = (True, f'Change-id {change_id} found and is the latest available version')
+ else:
+ maxser = max(lmbx.series.keys())
+ res[prereq] = (False, f'change-id should include the revision, e.g.: {change_id}:v{maxser}')
+ continue
+
+ elif parts[0] == 'patch-id':
+ patch_id = parts[1]
+ if patch_id not in known_patch_ids:
+ lmbx = b4.get_series_by_patch_id(patch_id, nocache=cmdargs.nocache)
+ if lmbx:
+ for rev, lser in lmbx.series.items():
+ for lmsg in lser.patches:
+ if not lmsg:
+ continue
+ ppid = lmsg.git_patch_id
+ if ppid:
+ known_patch_ids.add(ppid)
+ if patch_id not in known_patch_ids:
+ logger.debug('FAIL: No such patch-id found: %s', patch_id)
+ res[prereq] = (False, 'No matching patch-id found on the server')
+ continue
+ logger.debug('PASS: patch-id found: %s', patch_id)
+ res[prereq] = (True, 'Matching patch-id found on the server')
+
+ elif parts[0] == 'message-id':
+ msgid = parts[1].strip('<>')
+ q_msgs = b4.get_pi_thread_by_msgid(msgid, nocache=cmdargs.nocache)
+ if not q_msgs:
+ logger.debug('FAIL: No such message-id found: %s', msgid)
+ res[prereq] = (False, 'No matching message-id found on the server')
+ continue
+ logger.debug('PASS: message-id found: %s', msgid)
+ res[prereq] = (True, 'Matching message-id found on the server')
+
+ if res:
+ logger.info('---')
+ for prereq, info in res.items():
+ if info[0]:
+ logger.info('%s %s', b4.ATT_PASS_FANCY, prereq)
+ else:
+ logger.info('%s %s', b4.ATT_FAIL_FANCY, prereq)
+ logger.info(' - %s', info[1])
+
+
def get_series_start(usebranch: Optional[str] = None) -> str:
if usebranch:
mybranch = usebranch
@@ -2373,6 +2452,9 @@ def cmd_prep(cmdargs: argparse.Namespace) -> None:
if cmdargs.edit_deps:
return edit_deps()
+ if cmdargs.check_deps:
+ return check_deps(cmdargs)
+
def cmd_trailers(cmdargs: argparse.Namespace) -> None:
check_can_gfr()