Browse Source

Merge pull request #11774 from ctiller/owners

Strawman OWNERS --> CODEOWNERS script
Craig Tiller 8 years ago
parent
commit
df4cbb1abe

+ 22 - 0
.github/CODEOWNERS

@@ -0,0 +1,22 @@
+# Auto-generated by the tools/mkowners/mkowners.py tool
+# Uses OWNERS files in different modules throughout the
+# repository as the source of truth for module ownership.
+* @a11r @nicolasnoble @ctiller
+bazel/* @nicolasnoble @dgquintas @ctiller
+cmake/* @jtattermusch @a11r @nicolasnoble @ctiller
+doc/PROTOCOL-HTTP2.md @ejona86 @a11r @nicolasnoble @ctiller
+doc/interop-test-descriptions.md @ejona86 @a11r @nicolasnoble @ctiller
+etc/* @jboeuf @nicolasnoble @a11r @ctiller
+include/* @ctiller @markdroth @dgquintas @a11r @nicolasnoble
+src/core/* @ctiller @markdroth @dgquintas @a11r @nicolasnoble
+src/cpp/* @ctiller @markdroth @dgquintas @a11r @nicolasnoble
+src/csharp/* @jtattermusch @apolcyn @a11r @nicolasnoble @ctiller
+src/node/* @murgatroid99 @a11r @nicolasnoble @ctiller
+src/objective-c/* @muxi @makdharma @a11r @nicolasnoble @ctiller
+src/php/* @stanley-cheung @murgatroid99 @a11r @nicolasnoble @ctiller
+src/python/* @nathanielmanistaatgoogle @kpayson64 @a11r @nicolasnoble @ctiller
+src/ruby/* @apolcyn @murgatroid99 @a11r @nicolasnoble @ctiller
+tools/* @matt-kwong @jtattermusch @nicolasnoble @a11r @ctiller
+tools/codegen/core/* @ctiller @dgquintas @markdroth
+tools/dockerfile/* @matt-kwong @jtattermusch @nicolasnoble @a11r @ctiller
+tools/run_tests/* @matt-kwong @jtattermusch @nicolasnoble @a11r @ctiller

+ 6 - 0
OWNERS

@@ -0,0 +1,6 @@
+# Top level ownership
+
+@a11r
+@nicolasnoble
+@ctiller
+

+ 5 - 0
bazel/OWNERS

@@ -0,0 +1,5 @@
+set noparent
+@nicolasnoble
+@dgquintas
+@ctiller
+

+ 2 - 0
cmake/OWNERS

@@ -0,0 +1,2 @@
+@jtattermusch
+

+ 2 - 0
doc/OWNERS

@@ -0,0 +1,2 @@
+@ejona86 PROTOCOL-HTTP2.md interop-test-descriptions.md
+

+ 2 - 0
etc/OWNERS

@@ -0,0 +1,2 @@
+@jboeuf
+@nicolasnoble

+ 4 - 0
include/OWNERS

@@ -0,0 +1,4 @@
+@ctiller
+@markdroth
+@dgquintas
+

+ 4 - 0
src/core/OWNERS

@@ -0,0 +1,4 @@
+@ctiller
+@markdroth
+@dgquintas
+

+ 4 - 0
src/cpp/OWNERS

@@ -0,0 +1,4 @@
+@ctiller
+@markdroth
+@dgquintas
+

+ 3 - 0
src/csharp/OWNERS

@@ -0,0 +1,3 @@
+@jtattermusch
+@apolcyn
+

+ 2 - 0
src/node/OWNERS

@@ -0,0 +1,2 @@
+@murgatroid99
+

+ 3 - 0
src/objective-c/OWNERS

@@ -0,0 +1,3 @@
+@muxi
+@makdharma
+

+ 3 - 0
src/php/OWNERS

@@ -0,0 +1,3 @@
+@stanley-cheung
+@murgatroid99
+

+ 3 - 0
src/python/OWNERS

@@ -0,0 +1,3 @@
+@nathanielmanistaatgoogle
+@kpayson64
+

+ 3 - 0
src/ruby/OWNERS

@@ -0,0 +1,3 @@
+@apolcyn
+@murgatroid99
+

+ 4 - 0
tools/OWNERS

@@ -0,0 +1,4 @@
+@matt-kwong
+@jtattermusch
+@nicolasnoble
+

+ 5 - 0
tools/codegen/core/OWNERS

@@ -0,0 +1,5 @@
+set noparent
+@ctiller
+@dgquintas
+@markdroth
+

+ 3 - 0
tools/dockerfile/OWNERS

@@ -0,0 +1,3 @@
+@matt-kwong
+@jtattermusch
+

+ 0 - 2
tools/jenkins/run_bazel_full.sh

@@ -20,6 +20,4 @@ set -ex -o igncr || set -ex
 
 export DOCKERFILE_DIR=tools/dockerfile/test/bazel
 export DOCKER_RUN_SCRIPT=tools/jenkins/run_bazel_full_in_docker.sh
-# Warn PR author if they make a change to the bazel directory
-tools/run_tests/python_utils/check_bazel_dir.py
 exec tools/run_tests/dockerize/build_and_run_docker.sh

+ 175 - 0
tools/mkowners/mkowners.py

@@ -0,0 +1,175 @@
+#!/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 glob_intersect(g1, g2):
+  if not g2:
+    return all(c == '*' for c in g1)
+  if not g1:
+    return all(c == '*' for c in g2)
+  c1, *t1 = g1
+  c2, *t2 = g2
+  if c1 == '*':
+    return glob_intersect(g1, t2) or glob_intersect(t1, g2)
+  if c2 == '*':
+    return glob_intersect(t1, g2) or glob_intersect(g1, t2)
+  return c1 == c2 and glob_intersect(t1, t2)
+
+def add_parent_to_globs(parent, globs):
+  if not parent: return
+  for owners in owners_data:
+    if owners.dir == parent:
+      for directive in owners.directives:
+        for dglob in directive.globs or ['*']:
+          for gglob, glob in globs.items():
+            if glob_intersect(dglob, gglob):
+              if directive.who not in glob:
+                glob.append(directive.who)
+      add_parent_to_globs(owners.parent, globs)
+      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')
+  while todo:
+    head, *todo = todo
+    if head.parent and not head.parent in done:
+      todo.append(head)
+      continue
+    globs = collections.OrderedDict()
+    for directive in head.directives:
+      for glob in directive.globs or ['*']:
+        if glob not in globs:
+          globs[glob] = []
+        globs[glob].append(directive.who)
+    add_parent_to_globs(head.parent, globs)
+    for glob, owners in globs.items():
+      out.write('%s %s\n' % (
+          os.path.join(head.dir, glob) if head.dir != '.' else glob,
+          ' '.join(owners)))
+    done.add(head.dir)

+ 4 - 0
tools/run_tests/OWNERS

@@ -0,0 +1,4 @@
+@matt-kwong
+@jtattermusch
+@nicolasnoble
+

+ 0 - 41
tools/run_tests/python_utils/check_bazel_dir.py

@@ -1,41 +0,0 @@
-#!/usr/bin/env python
-# 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.
-
-"""This sends out a warning if any changes to the bazel dir are made."""
-
-from __future__ import print_function
-from subprocess import check_output
-
-import comment_on_pr
-import os
-
-_WARNING_MESSAGE = 'WARNING: You are making changes in the Bazel subdirectory. ' \
-                   'Please get explicit approval from @nicolasnoble before merging.'
-
-
-def _get_changed_files(base_branch):
-  """
-  Get list of changed files between current branch and base of target merge branch
-  """
-  # Get file changes between branch and merge-base of specified branch
-  base_commit = check_output(["git", "merge-base", base_branch, "HEAD"]).rstrip()
-  return check_output(["git", "diff", base_commit, "--name-only"]).splitlines()
-
-
-# ghprbTargetBranch environment variable only available during a Jenkins PR tests
-if 'ghprbTargetBranch' in os.environ:
-  changed_files = _get_changed_files('origin/%s' % os.environ['ghprbTargetBranch'])
-  if any(file.startswith('bazel/') for file in changed_files):
-    comment_on_pr.comment_on_pr(_WARNING_MESSAGE)

+ 29 - 0
tools/run_tests/sanity/check_owners.sh

@@ -0,0 +1,29 @@
+#!/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

+ 1 - 0
tools/run_tests/sanity/sanity_tests.yaml

@@ -1,5 +1,6 @@
 # a set of tests that are run in parallel for sanity tests
 - script: tools/run_tests/sanity/check_cache_mk.sh
+- script: tools/run_tests/sanity/check_owners.sh
 - script: tools/run_tests/sanity/check_sources_and_headers.py
 - script: tools/run_tests/sanity/check_submodules.sh
 - script: tools/run_tests/sanity/check_test_filtering.py