diff --git a/wireless/__init__.py b/wireless/__init__.py new file mode 100644 index 0000000..40934e0 --- /dev/null +++ b/wireless/__init__.py @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0 + +"""The wireless module + +Collection of files and code which is specific to the wireless process. +""" + +from .tree_match import series_tree_name_direct, \ + series_ignore_missing_tree_name, \ + series_tree_name_should_be_local, \ + series_is_a_fix_for, \ + series_needs_async + +current_tree = 'wireless' +next_tree = 'wireless-next' diff --git a/wireless/tree_match.py b/wireless/tree_match.py new file mode 100644 index 0000000..9218a19 --- /dev/null +++ b/wireless/tree_match.py @@ -0,0 +1,181 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Copyright (C) 2019 Netronome Systems, Inc. + +# TODO: document + +import re + +from core import log, log_open_sec, log_end_sec + + +def series_tree_name_direct(conf_trees, series): + for t in conf_trees: + if re.match(r'\[.*{pfx}.*\]'.format(pfx=t), series.subject): + return t + + +def _file_name_match_start(pfx, fn): + return fn.startswith(pfx) + + +def _file_name_match_dotted(pfx, fn): + dirs = pfx.split('/') + while True: + dirs.pop(0) + dotted = '.../' + '/'.join(dirs) + if dotted == '.../': + return False + + if fn.startswith(dotted): + return True + + +def _tree_name_should_be_local_files(raw_email): + """ + Returns True: patch should have been explicitly designated for local tree + False: patch has nothing to do with local trees + None: patch has mixed contents, it touches local code, but also code outside + """ + acceptable_files = { + '.../', + 'CREDITS', + 'MAINTAINERS', + 'Documentation/', + 'include/', + 'rust/', + 'tools/', + 'drivers/phy/', + 'drivers/vhost/', + } + required_files = { + 'include/net/ieee80211.h', + 'include/linux/ieee80211-eht.h', + 'include/linux/ieee80211-he.h', + 'include/linux/ieee80211-ht.h', + 'include/linux/ieee80211-mesh.h', + 'include/linux/ieee80211-nan.h', + 'include/linux/ieee80211-p2p.h', + 'include/linux/ieee80211-s1g.h', + 'include/linux/ieee80211-uhr.h', + 'include/linux/ieee80211-vht.h', + 'include/net/cfg80211.h', + 'include/net/mac80211.h', + # lib/ is pretty broad but patch volume is low + 'lib/', + 'net/rfkill/', + 'net/mac80211/', + 'net/wireless/', + 'drivers/net/wireless/', + } + excluded_files = { + } + all_files = acceptable_files.union(required_files) + required_found = False + foreign_found = False + + lines = raw_email.split('\n') + r_diffstat = re.compile(r'^\s*([-\w/._,]+)\s+\|\s+\d+\s*[-+]*\s*$') + r_header = re.compile(r'\+\+\+ b/([-\w/._,]+)$') + for line in lines: + match = r_header.match(line) + if not match: + match = r_diffstat.match(line) + if not match: + continue + + found = False + excluded = False + file_name = match.group(1) + log_open_sec(f'Checking file name {file_name}') + if file_name.startswith('.../'): + compare = _file_name_match_dotted + else: + compare = _file_name_match_start + + for fn in excluded_files: + if compare(fn, file_name): + log(f'Excluded by {fn}', "") + excluded = True + break + if excluded: + foreign_found = True + log_end_sec() + continue + for fn in all_files: + matches = compare(fn, file_name) + if not matches: + continue + log(f'Matched by {fn}', "") + found = True + required_found = required_found or fn in required_files + log_end_sec() + if not found: + log(f'File name {file_name} was not matched by any list', "") + foreign_found = True + + log(f'Required found: {required_found}, foreign_found: {foreign_found}', "") + if not required_found: + return False + if foreign_found: + return None + return True + + +def _tree_name_should_be_local(raw_email): + return _tree_name_should_be_local_files(raw_email) + + +def series_tree_name_should_be_local(series): + all_local = True + some_local = False + for p in series.patches: + ret = _tree_name_should_be_local(p.raw_patch) + # Returns tri-state True, None, False. And works well: + # True and None -> None + # True and False -> False + # False and None -> False + all_local = all_local and ret + # True or None -> True + # True or False -> True + # False or None -> False + some_local = some_local or ret + return all_local, some_local + + +def _ignore_missing_tree_name(subject): + log(f'checking ignore for {subject}', "") + return subject.count('] can: ') != 0 or \ + subject.count('pull-request:') != 0 or \ + subject.count('[GIT.*]') + + +def series_ignore_missing_tree_name(series): + if series.cover_letter: + return _ignore_missing_tree_name(series.subject) + for p in series.patches: + if not _ignore_missing_tree_name(p.subject): + return False + return True + + +def series_is_a_fix_for(s, tree): + commits = [] + regex = re.compile(r'^Fixes: [a-f0-9]* \(') + for p in s.patches: + commits += regex.findall(p.raw_patch) + for c in commits: + if not tree.contains(c): + return False + + return commits and tree.check_applies(s) + + +def series_needs_async(s) -> bool: + # not sure about this right now + bad_files = {} + for p in s.patches: + for needle in bad_files: + if p.raw_patch.find(needle) > 0: + return True + return False