27 git_root = (subprocess.check_output([
'git',
'rev-parse',
'--show-toplevel'
28 ]).
decode(
'utf-8').strip())
34 default_out = os.path.join(git_root,
'.github',
'CODEOWNERS')
36 argp = argparse.ArgumentParser(
'Generate .github/CODEOWNERS file')
37 argp.add_argument(
'--out',
41 help=
'Output file (default %s)' % default_out)
42 args = argp.parse_args()
49 os.path.join(root,
'OWNERS')
50 for root, dirs, files
in os.walk(git_root)
58 Owners = collections.namedtuple(
'Owners',
'parent directives dir')
59 Directive = collections.namedtuple(
'Directive',
'who globs')
63 with open(filename)
as f:
64 src = f.read().splitlines()
76 if line ==
'set noparent':
81 (who, globs) = line.split(
' ', 1)
82 globs_list = [glob
for glob
in globs.split(
' ')
if glob]
83 directive =
Directive(who=who, globs=globs_list)
87 directives.append(directive)
88 return Owners(parent=parent,
89 directives=directives,
90 dir=os.path.relpath(os.path.dirname(filename), git_root))
93 owners_data = sorted([
parse_owners(filename)
for filename
in owners_files],
94 key=operator.attrgetter(
'dir'))
102 for owners
in owners_data:
103 if owners.parent ==
True:
105 best_parent_score =
None
106 for possible_parent
in owners_data:
107 if possible_parent
is owners:
109 rel = os.path.relpath(owners.dir, possible_parent.dir)
114 depth =
len(rel.split(os.sep))
115 if not best_parent
or depth < best_parent_score:
116 best_parent = possible_parent
117 best_parent_score = depth
119 owners = owners._replace(parent=best_parent.dir)
121 owners = owners._replace(parent=
None)
122 new_owners_data.append(owners)
123 owners_data = new_owners_data
132 return os.path.join(rules_dir, sub_path)
if rules_dir !=
'.' else sub_path
142 return gg_cache[glob]
144 subprocess.check_output([
145 'git',
'ls-files', os.path.join(git_root, glob)
146 ]).
decode(
'utf-8').strip().splitlines())
152 globs = collections.OrderedDict()
154 for directive
in directives:
155 for glob
in directive.globs
or [
'**']:
156 if glob
not in globs:
158 if directive.who
not in globs[glob]:
159 globs[glob].append(directive.who)
161 sorted_globs = sorted(list(globs.keys()),
164 out_globs = collections.OrderedDict()
165 for glob_add
in sorted_globs:
166 who_add = globs[glob_add]
167 pre_items = [i
for i
in list(out_globs.items())]
168 out_globs[glob_add] = who_add.copy()
169 for glob_have, who_have
in pre_items:
172 intersect = files_have.intersection(files_add)
174 for f
in sorted(files_add):
175 if f
not in intersect:
176 out_globs[os.path.relpath(f, start=root)] = who_add
178 if who
not in out_globs[glob_add]:
179 out_globs[glob_add].append(who)
186 for owners
in owners_data:
187 if owners.dir == parent:
189 for oglob, oglob_who
in list(owners_globs.items()):
190 for gglob, gglob_who
in list(globs.items()):
193 intersect = files_parent.intersection(files_child)
194 gglob_who_orig = gglob_who.copy()
196 for f
in sorted(files_child
198 if f
not in intersect:
199 who = gglob_who_orig.copy()
200 globs[os.path.relpath(f, start=globs_dir)] = who
201 for who
in oglob_who:
202 if who
not in gglob_who:
203 gglob_who.append(who)
209 todo = owners_data.copy()
211 with open(args.out,
'w')
as out:
212 out.write(
'# Auto-generated by the tools/mkowners/mkowners.py tool\n')
213 out.write(
'# Uses OWNERS files in different modules throughout the\n')
214 out.write(
'# repository as the source of truth for module ownership.\n')
218 if head.parent
and not head.parent
in done:
223 for glob, owners
in list(globs.items()):
225 for glob1, owners1, dir1
in reversed(written_globs):
228 intersect = files.intersection(files1)
229 if files == intersect:
230 if sorted(owners) == sorted(owners1):
238 out.write(
'/%s %s\n' %
239 (
full_dir(head.dir, glob),
' '.join(owners)))
240 written_globs.append((glob, owners, head.dir))