mirror of https://github.com/grpc/grpc.git
[owners] Remove the mkowners tool (#34533)
We originally wanted a common format with internal OWNERS files on the thought that we'd use them and import them and oh gosh that was the wrong thing to think. With upcoming changes to tree management having them here is going to cause problems, so lets just update the CODEOWNERS files directly.pull/34589/head
parent
f27e1156e2
commit
579e0f1881
11 changed files with 0 additions and 320 deletions
@ -1,5 +0,0 @@ |
||||
set noparent |
||||
@jtattermusch |
||||
@veblush |
||||
@gnossen |
||||
|
@ -1,3 +0,0 @@ |
||||
set noparent |
||||
@jtattermusch |
||||
@apolcyn |
@ -1,3 +0,0 @@ |
||||
set noparent |
||||
@markdroth |
||||
|
@ -1,2 +0,0 @@ |
||||
set noparent |
||||
@ctiller |
@ -1,3 +0,0 @@ |
||||
set noparent |
||||
@markdroth |
||||
|
@ -1,3 +0,0 @@ |
||||
set noparent |
||||
@markdroth |
||||
|
@ -1,3 +0,0 @@ |
||||
set noparent |
||||
@markdroth |
||||
|
@ -1,10 +0,0 @@ |
||||
set noparent |
||||
|
||||
# These owners are in place to ensure that dockerfiles are changed in a way |
||||
# that maximizes use of docker cache when rebuilding and that the docker images |
||||
# are fast to build and sane in general. |
||||
# Also, push_testing_images.sh needs to be used upon changing the docker images |
||||
# for kokoro to be able to access the pre-built images. |
||||
|
||||
@jtattermusch |
||||
@apolcyn |
@ -1,258 +0,0 @@ |
||||
#!/usr/bin/env python3 |
||||
# Copyright 2017 gRPC authors. |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
|
||||
import argparse |
||||
import collections |
||||
import operator |
||||
import os |
||||
import re |
||||
import subprocess |
||||
|
||||
# |
||||
# Find the root of the git tree |
||||
# |
||||
|
||||
git_root = ( |
||||
subprocess.check_output(["git", "rev-parse", "--show-toplevel"]) |
||||
.decode("utf-8") |
||||
.strip() |
||||
) |
||||
|
||||
# |
||||
# Parse command line arguments |
||||
# |
||||
|
||||
default_out = os.path.join(git_root, ".github", "CODEOWNERS") |
||||
|
||||
argp = argparse.ArgumentParser("Generate .github/CODEOWNERS file") |
||||
argp.add_argument( |
||||
"--out", |
||||
"-o", |
||||
type=str, |
||||
default=default_out, |
||||
help="Output file (default %s)" % default_out, |
||||
) |
||||
args = argp.parse_args() |
||||
|
||||
# |
||||
# Walk git tree to locate all OWNERS files |
||||
# |
||||
|
||||
owners_files = [ |
||||
os.path.join(root, "OWNERS") |
||||
for root, dirs, files in os.walk(git_root) |
||||
if "OWNERS" in files |
||||
] |
||||
|
||||
# |
||||
# Parse owners files |
||||
# |
||||
|
||||
Owners = collections.namedtuple("Owners", "parent directives dir") |
||||
Directive = collections.namedtuple("Directive", "who globs") |
||||
|
||||
|
||||
def parse_owners(filename): |
||||
with open(filename) as f: |
||||
src = f.read().splitlines() |
||||
parent = True |
||||
directives = [] |
||||
for line in src: |
||||
line = line.strip() |
||||
# line := directive | comment |
||||
if not line: |
||||
continue |
||||
if line[0] == "#": |
||||
continue |
||||
# it's a directive |
||||
directive = None |
||||
if line == "set noparent": |
||||
parent = False |
||||
elif line == "*": |
||||
directive = Directive(who="*", globs=[]) |
||||
elif " " in line: |
||||
(who, globs) = line.split(" ", 1) |
||||
globs_list = [glob for glob in globs.split(" ") if glob] |
||||
directive = Directive(who=who, globs=globs_list) |
||||
else: |
||||
directive = Directive(who=line, globs=[]) |
||||
if directive: |
||||
directives.append(directive) |
||||
return Owners( |
||||
parent=parent, |
||||
directives=directives, |
||||
dir=os.path.relpath(os.path.dirname(filename), git_root), |
||||
) |
||||
|
||||
|
||||
owners_data = sorted( |
||||
[parse_owners(filename) for filename in owners_files], |
||||
key=operator.attrgetter("dir"), |
||||
) |
||||
|
||||
# |
||||
# Modify owners so that parented OWNERS files point to the actual |
||||
# Owners tuple with their parent field |
||||
# |
||||
|
||||
new_owners_data = [] |
||||
for owners in owners_data: |
||||
if owners.parent == True: |
||||
best_parent = None |
||||
best_parent_score = None |
||||
for possible_parent in owners_data: |
||||
if possible_parent is owners: |
||||
continue |
||||
rel = os.path.relpath(owners.dir, possible_parent.dir) |
||||
# '..' ==> we had to walk up from possible_parent to get to owners |
||||
# ==> not a parent |
||||
if ".." in rel: |
||||
continue |
||||
depth = len(rel.split(os.sep)) |
||||
if not best_parent or depth < best_parent_score: |
||||
best_parent = possible_parent |
||||
best_parent_score = depth |
||||
if best_parent: |
||||
owners = owners._replace(parent=best_parent.dir) |
||||
else: |
||||
owners = owners._replace(parent=None) |
||||
new_owners_data.append(owners) |
||||
owners_data = new_owners_data |
||||
|
||||
# |
||||
# In bottom to top order, process owners data structures to build up |
||||
# a CODEOWNERS file for GitHub |
||||
# |
||||
|
||||
|
||||
def full_dir(rules_dir, sub_path): |
||||
return os.path.join(rules_dir, sub_path) if rules_dir != "." else sub_path |
||||
|
||||
|
||||
# glob using git |
||||
gg_cache = {} |
||||
|
||||
|
||||
def git_glob(glob): |
||||
global gg_cache |
||||
if glob in gg_cache: |
||||
return gg_cache[glob] |
||||
r = set( |
||||
subprocess.check_output( |
||||
["git", "ls-files", os.path.join(git_root, glob)] |
||||
) |
||||
.decode("utf-8") |
||||
.strip() |
||||
.splitlines() |
||||
) |
||||
gg_cache[glob] = r |
||||
return r |
||||
|
||||
|
||||
def expand_directives(root, directives): |
||||
globs = collections.OrderedDict() |
||||
# build a table of glob --> owners |
||||
for directive in directives: |
||||
for glob in directive.globs or ["**"]: |
||||
if glob not in globs: |
||||
globs[glob] = [] |
||||
if directive.who not in globs[glob]: |
||||
globs[glob].append(directive.who) |
||||
# expand owners for intersecting globs |
||||
sorted_globs = sorted( |
||||
list(globs.keys()), |
||||
key=lambda g: len(git_glob(full_dir(root, g))), |
||||
reverse=True, |
||||
) |
||||
out_globs = collections.OrderedDict() |
||||
for glob_add in sorted_globs: |
||||
who_add = globs[glob_add] |
||||
pre_items = [i for i in list(out_globs.items())] |
||||
out_globs[glob_add] = who_add.copy() |
||||
for glob_have, who_have in pre_items: |
||||
files_add = git_glob(full_dir(root, glob_add)) |
||||
files_have = git_glob(full_dir(root, glob_have)) |
||||
intersect = files_have.intersection(files_add) |
||||
if intersect: |
||||
for f in sorted(files_add): # sorted to ensure merge stability |
||||
if f not in intersect: |
||||
out_globs[os.path.relpath(f, start=root)] = who_add |
||||
for who in who_have: |
||||
if who not in out_globs[glob_add]: |
||||
out_globs[glob_add].append(who) |
||||
return out_globs |
||||
|
||||
|
||||
def add_parent_to_globs(parent, globs, globs_dir): |
||||
if not parent: |
||||
return |
||||
for owners in owners_data: |
||||
if owners.dir == parent: |
||||
owners_globs = expand_directives(owners.dir, owners.directives) |
||||
for oglob, oglob_who in list(owners_globs.items()): |
||||
for gglob, gglob_who in list(globs.items()): |
||||
files_parent = git_glob(full_dir(owners.dir, oglob)) |
||||
files_child = git_glob(full_dir(globs_dir, gglob)) |
||||
intersect = files_parent.intersection(files_child) |
||||
gglob_who_orig = gglob_who.copy() |
||||
if intersect: |
||||
for f in sorted( |
||||
files_child |
||||
): # sorted to ensure merge stability |
||||
if f not in intersect: |
||||
who = gglob_who_orig.copy() |
||||
globs[os.path.relpath(f, start=globs_dir)] = who |
||||
for who in oglob_who: |
||||
if who not in gglob_who: |
||||
gglob_who.append(who) |
||||
add_parent_to_globs(owners.parent, globs, globs_dir) |
||||
return |
||||
assert False |
||||
|
||||
|
||||
todo = owners_data.copy() |
||||
done = set() |
||||
with open(args.out, "w") as out: |
||||
out.write("# Auto-generated by the tools/mkowners/mkowners.py tool\n") |
||||
out.write("# Uses OWNERS files in different modules throughout the\n") |
||||
out.write("# repository as the source of truth for module ownership.\n") |
||||
written_globs = [] |
||||
while todo: |
||||
head, *todo = todo |
||||
if head.parent and not head.parent in done: |
||||
todo.append(head) |
||||
continue |
||||
globs = expand_directives(head.dir, head.directives) |
||||
add_parent_to_globs(head.parent, globs, head.dir) |
||||
for glob, owners in list(globs.items()): |
||||
skip = False |
||||
for glob1, owners1, dir1 in reversed(written_globs): |
||||
files = git_glob(full_dir(head.dir, glob)) |
||||
files1 = git_glob(full_dir(dir1, glob1)) |
||||
intersect = files.intersection(files1) |
||||
if files == intersect: |
||||
if sorted(owners) == sorted(owners1): |
||||
skip = True # nothing new in this rule |
||||
break |
||||
elif intersect: |
||||
# continuing would cause a semantic change since some files are |
||||
# affected differently by this rule and CODEOWNERS is order dependent |
||||
break |
||||
if not skip: |
||||
out.write( |
||||
"/%s %s\n" % (full_dir(head.dir, glob), " ".join(owners)) |
||||
) |
||||
written_globs.append((glob, owners, head.dir)) |
||||
done.add(head.dir) |
@ -1,29 +0,0 @@ |
||||
#!/bin/sh |
||||
# Copyright 2017 gRPC authors. |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
|
||||
|
||||
set -e |
||||
|
||||
export TEST=true |
||||
|
||||
cd "$(dirname "$0")/../../.." |
||||
|
||||
owners=.github/CODEOWNERS |
||||
want_owners=$(mktemp /tmp/submXXXXXX) |
||||
|
||||
tools/mkowners/mkowners.py -o "$want_owners" |
||||
diff -u "$owners" "$want_owners" |
||||
|
||||
rm "$want_owners" |
Loading…
Reference in new issue