diff options
author | Konstantin Ryabitsev <konstantin@linuxfoundation.org> | 2024-03-21 17:24:54 -0400 |
---|---|---|
committer | Konstantin Ryabitsev <konstantin@linuxfoundation.org> | 2024-03-21 17:24:54 -0400 |
commit | 43bf93cba1bd59a130a5f62154c65655377cece9 (patch) | |
tree | a95a25b454844e298d9a13bec8b39aac07227276 | |
parent | 14bf644c74114cb69672cafde9d8a0d8e80f6bfa (diff) | |
download | b4-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__.py | 43 | ||||
-rw-r--r-- | src/b4/command.py | 4 | ||||
-rw-r--r-- | src/b4/ez.py | 88 |
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() |