| Index: owners.py
|
| diff --git a/owners.py b/owners.py
|
| index 30646ffa24dd203754a08abf5bba6d60f0f938cf..563675a15bce4743b2d0d65df6da58289120789b 100644
|
| --- a/owners.py
|
| +++ b/owners.py
|
| @@ -18,6 +18,7 @@ line := directive
|
| | comment
|
|
|
| directive := "set noparent"
|
| + | "file:" glob
|
| | email_address
|
| | "*"
|
|
|
| @@ -45,6 +46,11 @@ If "per-file glob=set noparent" is used, then global directives are ignored
|
| for the glob, and only the "per-file" owners are used for files matching that
|
| glob.
|
|
|
| +If the "file:" directive is used, the referred to OWNERS file will be parsed and
|
| +considered when determining the valid set of OWNERS. If the filename starts with
|
| +"//" it is relative to the root of the repository, otherwise it is relative to
|
| +the current file
|
| +
|
| Examples for all of these combinations can be found in tests/owners_unittest.py.
|
| """
|
|
|
| @@ -118,6 +124,9 @@ class Database(object):
|
| # (This is implicitly true for the root directory).
|
| self.stop_looking = set([''])
|
|
|
| + # Set of files which have already been read.
|
| + self.read_files = set()
|
| +
|
| def reviewers_for(self, files, author):
|
| """Returns a suggested set of reviewers that will cover the files.
|
|
|
| @@ -189,16 +198,23 @@ class Database(object):
|
| for f in files:
|
| dirpath = self.os_path.dirname(f)
|
| while not dirpath in self.owners_for:
|
| - self._read_owners_in_dir(dirpath)
|
| + self._read_owners(self.os_path.join(dirpath, 'OWNERS'))
|
| if self._stop_looking(dirpath):
|
| break
|
| dirpath = self.os_path.dirname(dirpath)
|
|
|
| - def _read_owners_in_dir(self, dirpath):
|
| - owners_path = self.os_path.join(self.root, dirpath, 'OWNERS')
|
| + def _read_owners(self, path):
|
| + owners_path = self.os_path.join(self.root, path)
|
| if not self.os_path.exists(owners_path):
|
| return
|
| +
|
| + if owners_path in self.read_files:
|
| + return
|
| +
|
| + self.read_files.add(owners_path)
|
| +
|
| comment = []
|
| + dirpath = self.os_path.dirname(path)
|
| in_comment = False
|
| lineno = 0
|
| for line in self.fopen(owners_path):
|
| @@ -244,6 +260,23 @@ class Database(object):
|
| line_type, owners_path, lineno, comment):
|
| if directive == 'set noparent':
|
| self.stop_looking.add(path)
|
| + elif directive.startswith('file:'):
|
| + owners_file = self._resolve_include(directive[5:], owners_path)
|
| + if not owners_file:
|
| + raise SyntaxErrorInOwnersFile(owners_path, lineno,
|
| + ('%s does not refer to an existing file.' % directive[5:]))
|
| +
|
| + self._read_owners(owners_file)
|
| +
|
| + dirpath = self.os_path.dirname(owners_file)
|
| + for key in self.owned_by:
|
| + if not dirpath in self.owned_by[key]:
|
| + continue
|
| + self.owned_by[key].add(path)
|
| +
|
| + if dirpath in self.owners_for:
|
| + self.owners_for.setdefault(path, set()).update(self.owners_for[dirpath])
|
| +
|
| elif self.email_regexp.match(directive) or directive == EVERYONE:
|
| self.comments.setdefault(directive, {})
|
| self.comments[directive][path] = comment
|
| @@ -251,9 +284,23 @@ class Database(object):
|
| self.owners_for.setdefault(path, set()).add(directive)
|
| else:
|
| raise SyntaxErrorInOwnersFile(owners_path, lineno,
|
| - ('%s is not a "set" directive, "*", '
|
| + ('%s is not a "set" directive, file include, "*", '
|
| 'or an email address: "%s"' % (line_type, directive)))
|
|
|
| + def _resolve_include(self, path, start):
|
| + if path.startswith('//'):
|
| + include_path = path[2:]
|
| + else:
|
| + assert start.startswith(self.root)
|
| + start = self.os_path.dirname(start[len(self.root):])
|
| + include_path = self.os_path.join(start, path)
|
| +
|
| + owners_path = self.os_path.join(self.root, include_path)
|
| + if not self.os_path.exists(owners_path):
|
| + return None
|
| +
|
| + return include_path
|
| +
|
| def _covering_set_of_owners_for(self, files, author):
|
| dirs_remaining = set(self._enclosing_dir_with_owners(f) for f in files)
|
| all_possible_owners = self.all_possible_owners(dirs_remaining, author)
|
|
|