diff options
Diffstat (limited to 'BaseTools/Scripts/GetMaintainer.py')
| -rw-r--r-- | BaseTools/Scripts/GetMaintainer.py | 414 |
1 files changed, 207 insertions, 207 deletions
diff --git a/BaseTools/Scripts/GetMaintainer.py b/BaseTools/Scripts/GetMaintainer.py index 8097ba4e7b..b17bd31ed6 100644 --- a/BaseTools/Scripts/GetMaintainer.py +++ b/BaseTools/Scripts/GetMaintainer.py @@ -1,207 +1,207 @@ -## @file
-# Retrieves the people to request review from on submission of a commit.
-#
-# Copyright (c) 2019, Linaro Ltd. All rights reserved.<BR>
-#
-# SPDX-License-Identifier: BSD-2-Clause-Patent
-#
-
-from __future__ import print_function
-from collections import defaultdict
-from collections import OrderedDict
-import argparse
-import os
-import re
-import SetupGit
-
-EXPRESSIONS = {
- 'exclude': re.compile(r'^X:\s*(?P<exclude>.*?)\r*$'),
- 'file': re.compile(r'^F:\s*(?P<file>.*?)\r*$'),
- 'list': re.compile(r'^L:\s*(?P<list>.*?)\r*$'),
- 'maintainer': re.compile(r'^M:\s*(?P<maintainer>.*?)\r*$'),
- 'reviewer': re.compile(r'^R:\s*(?P<reviewer>.*?)\r*$'),
- 'status': re.compile(r'^S:\s*(?P<status>.*?)\r*$'),
- 'tree': re.compile(r'^T:\s*(?P<tree>.*?)\r*$'),
- 'webpage': re.compile(r'^W:\s*(?P<webpage>.*?)\r*$')
-}
-
-def printsection(section):
- """Prints out the dictionary describing a Maintainers.txt section."""
- print('===')
- for key in section.keys():
- print("Key: %s" % key)
- for item in section[key]:
- print(' %s' % item)
-
-def pattern_to_regex(pattern):
- """Takes a string containing regular UNIX path wildcards
- and returns a string suitable for matching with regex."""
-
- pattern = pattern.replace('.', r'\.')
- pattern = pattern.replace('?', r'.')
- pattern = pattern.replace('*', r'.*')
-
- if pattern.endswith('/'):
- pattern += r'.*'
- elif pattern.endswith('.*'):
- pattern = pattern[:-2]
- pattern += r'(?!.*?/.*?)'
-
- return pattern
-
-def path_in_section(path, section):
- """Returns True of False indicating whether the path is covered by
- the current section."""
- if not 'file' in section:
- return False
-
- for pattern in section['file']:
- regex = pattern_to_regex(pattern)
-
- match = re.match(regex, path)
- if match:
- # Check if there is an exclude pattern that applies
- for pattern in section['exclude']:
- regex = pattern_to_regex(pattern)
-
- match = re.match(regex, path)
- if match:
- return False
-
- return True
-
- return False
-
-def get_section_maintainers(path, section):
- """Returns a list with email addresses to any M: and R: entries
- matching the provided path in the provided section."""
- maintainers = []
- reviewers = []
- lists = []
- nowarn_status = ['Supported', 'Maintained']
-
- if path_in_section(path, section):
- for status in section['status']:
- if status not in nowarn_status:
- print('WARNING: Maintained status for "%s" is \'%s\'!' % (path, status))
- for address in section['maintainer']:
- # Convert to list if necessary
- if isinstance(address, list):
- maintainers += address
- else:
- maintainers += [address]
- for address in section['reviewer']:
- # Convert to list if necessary
- if isinstance(address, list):
- reviewers += address
- else:
- reviewers += [address]
- for address in section['list']:
- # Convert to list if necessary
- if isinstance(address, list):
- lists += address
- else:
- lists += [address]
-
- return {'maintainers': maintainers, 'reviewers': reviewers, 'lists': lists}
-
-def get_maintainers(path, sections, level=0):
- """For 'path', iterates over all sections, returning maintainers
- for matching ones."""
- maintainers = []
- reviewers = []
- lists = []
- for section in sections:
- recipients = get_section_maintainers(path, section)
- maintainers += recipients['maintainers']
- reviewers += recipients['reviewers']
- lists += recipients['lists']
-
- if not maintainers:
- # If no match found, look for match for (nonexistent) file
- # REPO.working_dir/<default>
- print('"%s": no maintainers found, looking for default' % path)
- if level == 0:
- recipients = get_maintainers('<default>', sections, level=level + 1)
- maintainers += recipients['maintainers']
- reviewers += recipients['reviewers']
- lists += recipients['lists']
- else:
- print("No <default> maintainers set for project.")
- if not maintainers:
- return None
-
- return {'maintainers': maintainers, 'reviewers': reviewers, 'lists': lists}
-
-def parse_maintainers_line(line):
- """Parse one line of Maintainers.txt, returning any match group and its key."""
- for key, expression in EXPRESSIONS.items():
- match = expression.match(line)
- if match:
- return key, match.group(key)
- return None, None
-
-def parse_maintainers_file(filename):
- """Parse the Maintainers.txt from top-level of repo and
- return a list containing dictionaries of all sections."""
- with open(filename, 'r') as text:
- line = text.readline()
- sectionlist = []
- section = defaultdict(list)
- while line:
- key, value = parse_maintainers_line(line)
- if key and value:
- section[key].append(value)
-
- line = text.readline()
- # If end of section (end of file, or non-tag line encountered)...
- if not key or not value or not line:
- # ...if non-empty, append section to list.
- if section:
- sectionlist.append(section.copy())
- section.clear()
-
- return sectionlist
-
-def get_modified_files(repo, args):
- """Returns a list of the files modified by the commit specified in 'args'."""
- commit = repo.commit(args.commit)
- return commit.stats.files
-
-if __name__ == '__main__':
- PARSER = argparse.ArgumentParser(
- description='Retrieves information on who to cc for review on a given commit')
- PARSER.add_argument('commit',
- action="store",
- help='git revision to examine (default: HEAD)',
- nargs='?',
- default='HEAD')
- PARSER.add_argument('-l', '--lookup',
- help='Find section matches for path LOOKUP',
- required=False)
- ARGS = PARSER.parse_args()
-
- REPO = SetupGit.locate_repo()
-
- CONFIG_FILE = os.path.join(REPO.working_dir, 'Maintainers.txt')
-
- SECTIONS = parse_maintainers_file(CONFIG_FILE)
-
- if ARGS.lookup:
- FILES = [ARGS.lookup.replace('\\','/')]
- else:
- FILES = get_modified_files(REPO, ARGS)
-
- # Accumulate a sorted list of addresses
- ADDRESSES = set([])
- for file in FILES:
- print(file)
- recipients = get_maintainers(file, SECTIONS)
- ADDRESSES |= set(recipients['maintainers'] + recipients['reviewers'] + recipients['lists'])
- ADDRESSES = list(ADDRESSES)
- ADDRESSES.sort()
-
- for address in ADDRESSES:
- if '<' in address and '>' in address:
- address = address.split('>', 1)[0] + '>'
- print(' %s' % address)
+## @file +# Retrieves the people to request review from on submission of a commit. +# +# Copyright (c) 2019, Linaro Ltd. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# + +from __future__ import print_function +from collections import defaultdict +from collections import OrderedDict +import argparse +import os +import re +import SetupGit + +EXPRESSIONS = { + 'exclude': re.compile(r'^X:\s*(?P<exclude>.*?)\r*$'), + 'file': re.compile(r'^F:\s*(?P<file>.*?)\r*$'), + 'list': re.compile(r'^L:\s*(?P<list>.*?)\r*$'), + 'maintainer': re.compile(r'^M:\s*(?P<maintainer>.*?)\r*$'), + 'reviewer': re.compile(r'^R:\s*(?P<reviewer>.*?)\r*$'), + 'status': re.compile(r'^S:\s*(?P<status>.*?)\r*$'), + 'tree': re.compile(r'^T:\s*(?P<tree>.*?)\r*$'), + 'webpage': re.compile(r'^W:\s*(?P<webpage>.*?)\r*$') +} + +def printsection(section): + """Prints out the dictionary describing a Maintainers.txt section.""" + print('===') + for key in section.keys(): + print("Key: %s" % key) + for item in section[key]: + print(' %s' % item) + +def pattern_to_regex(pattern): + """Takes a string containing regular UNIX path wildcards + and returns a string suitable for matching with regex.""" + + pattern = pattern.replace('.', r'\.') + pattern = pattern.replace('?', r'.') + pattern = pattern.replace('*', r'.*') + + if pattern.endswith('/'): + pattern += r'.*' + elif pattern.endswith('.*'): + pattern = pattern[:-2] + pattern += r'(?!.*?/.*?)' + + return pattern + +def path_in_section(path, section): + """Returns True of False indicating whether the path is covered by + the current section.""" + if not 'file' in section: + return False + + for pattern in section['file']: + regex = pattern_to_regex(pattern) + + match = re.match(regex, path) + if match: + # Check if there is an exclude pattern that applies + for pattern in section['exclude']: + regex = pattern_to_regex(pattern) + + match = re.match(regex, path) + if match: + return False + + return True + + return False + +def get_section_maintainers(path, section): + """Returns a list with email addresses to any M: and R: entries + matching the provided path in the provided section.""" + maintainers = [] + reviewers = [] + lists = [] + nowarn_status = ['Supported', 'Maintained'] + + if path_in_section(path, section): + for status in section['status']: + if status not in nowarn_status: + print('WARNING: Maintained status for "%s" is \'%s\'!' % (path, status)) + for address in section['maintainer']: + # Convert to list if necessary + if isinstance(address, list): + maintainers += address + else: + maintainers += [address] + for address in section['reviewer']: + # Convert to list if necessary + if isinstance(address, list): + reviewers += address + else: + reviewers += [address] + for address in section['list']: + # Convert to list if necessary + if isinstance(address, list): + lists += address + else: + lists += [address] + + return {'maintainers': maintainers, 'reviewers': reviewers, 'lists': lists} + +def get_maintainers(path, sections, level=0): + """For 'path', iterates over all sections, returning maintainers + for matching ones.""" + maintainers = [] + reviewers = [] + lists = [] + for section in sections: + recipients = get_section_maintainers(path, section) + maintainers += recipients['maintainers'] + reviewers += recipients['reviewers'] + lists += recipients['lists'] + + if not maintainers: + # If no match found, look for match for (nonexistent) file + # REPO.working_dir/<default> + print('"%s": no maintainers found, looking for default' % path) + if level == 0: + recipients = get_maintainers('<default>', sections, level=level + 1) + maintainers += recipients['maintainers'] + reviewers += recipients['reviewers'] + lists += recipients['lists'] + else: + print("No <default> maintainers set for project.") + if not maintainers: + return None + + return {'maintainers': maintainers, 'reviewers': reviewers, 'lists': lists} + +def parse_maintainers_line(line): + """Parse one line of Maintainers.txt, returning any match group and its key.""" + for key, expression in EXPRESSIONS.items(): + match = expression.match(line) + if match: + return key, match.group(key) + return None, None + +def parse_maintainers_file(filename): + """Parse the Maintainers.txt from top-level of repo and + return a list containing dictionaries of all sections.""" + with open(filename, 'r') as text: + line = text.readline() + sectionlist = [] + section = defaultdict(list) + while line: + key, value = parse_maintainers_line(line) + if key and value: + section[key].append(value) + + line = text.readline() + # If end of section (end of file, or non-tag line encountered)... + if not key or not value or not line: + # ...if non-empty, append section to list. + if section: + sectionlist.append(section.copy()) + section.clear() + + return sectionlist + +def get_modified_files(repo, args): + """Returns a list of the files modified by the commit specified in 'args'.""" + commit = repo.commit(args.commit) + return commit.stats.files + +if __name__ == '__main__': + PARSER = argparse.ArgumentParser( + description='Retrieves information on who to cc for review on a given commit') + PARSER.add_argument('commit', + action="store", + help='git revision to examine (default: HEAD)', + nargs='?', + default='HEAD') + PARSER.add_argument('-l', '--lookup', + help='Find section matches for path LOOKUP', + required=False) + ARGS = PARSER.parse_args() + + REPO = SetupGit.locate_repo() + + CONFIG_FILE = os.path.join(REPO.working_dir, 'Maintainers.txt') + + SECTIONS = parse_maintainers_file(CONFIG_FILE) + + if ARGS.lookup: + FILES = [ARGS.lookup.replace('\\','/')] + else: + FILES = get_modified_files(REPO, ARGS) + + # Accumulate a sorted list of addresses + ADDRESSES = set([]) + for file in FILES: + print(file) + recipients = get_maintainers(file, SECTIONS) + ADDRESSES |= set(recipients['maintainers'] + recipients['reviewers'] + recipients['lists']) + ADDRESSES = list(ADDRESSES) + ADDRESSES.sort() + + for address in ADDRESSES: + if '<' in address and '>' in address: + address = address.split('>', 1)[0] + '>' + print(' %s' % address) |
