summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbst-marge-bot <marge-bot@buildstream.build>2019-07-31 17:13:06 +0000
committerbst-marge-bot <marge-bot@buildstream.build>2019-07-31 17:13:06 +0000
commit3d224253b3448517f5bd36e68e9ff6ac205a0ca9 (patch)
treeeb9860d5c4ee00450b989f350531b95e28588d48
parent78a48653ea9c0701e4a6a774af49a1d35df8135a (diff)
parent702f10c0fa6b6be233f83ea97fad73ec8eca8665 (diff)
downloadbuildstream-3d224253b3448517f5bd36e68e9ff6ac205a0ca9.tar.gz
Merge branch 'alexfazakas/fetch-committers' into 'master'
Automatically build a list of committers Closes #1071 See merge request BuildStream/buildstream!1505
-rw-r--r--CONTRIBUTING.rst49
-rw-r--r--MAINTAINERS7
-rw-r--r--contrib/COMMITTERS.rst.j218
-rwxr-xr-xcontrib/update_committers.py111
4 files changed, 178 insertions, 7 deletions
diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst
index b128c7f9b..11b8d9192 100644
--- a/CONTRIBUTING.rst
+++ b/CONTRIBUTING.rst
@@ -200,6 +200,55 @@ separately.
This is a part of #123
+Committer access
+----------------
+
+Committers in the BuildStream project are those folks to whom the right to
+directly commit changes to our version controlled resources has been granted.
+While every contribution is
+valued regardless of its source, not every person who contributes code to the
+project will earn commit access. The `COMMITTERS`_ file lists all committers.
+
+Whenever someone is granted (or revoked) commit access, an Owner or Maintainer
+should run the the script located at `contrib/update_committers.py` with their
+personal access token, updating the COMMITTERS.rst list and opening an MR
+with their changes.
+
+.. _COMMITTERS: https://gitlab.com/BuildStream/buildstream/blob/master/COMMITTERS.rst
+
+
+How commit access is granted
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+After someone has successfully contributed a few non-trivial patches, some full
+committer, usually whoever has reviewed and applied the most patches from that
+contributor, proposes them for commit access. This proposal is sent only to the
+other full committers - the ensuing discussion is private, so that everyone can
+feel comfortable speaking their minds. Assuming there are no objections, the
+contributor is granted commit access. The decision is made by consensus; there
+are no formal rules governing the procedure, though generally if someone strongly
+objects the access is not offered, or is offered on a provisional basis.
+
+This of course relies on contributors being responsive and showing willingness
+to address any problems that may arise after landing patches. However, the primary
+criterion for commit access is good judgement.
+
+You do not have to be a technical wizard or demonstrate deep knowledge of the
+entire codebase to become a committer. You just need to know what you don't
+know. Non-code contributions are just as valuable in the path to commit access.
+If your patches adhere to the guidelines in this file, adhere to all the usual
+unquantifiable rules of coding (code should be readable, robust, maintainable, etc.),
+and respect the Hippocratic Principle of "first, do no harm", then you will probably
+get commit access pretty quickly. The size, complexity, and quantity of your patches
+do not matter as much as the degree of care you show in avoiding bugs and minimizing
+unnecessary impact on the rest of the code. Many full committers are people who have
+not made major code contributions, but rather lots of small, clean fixes, each of
+which was an unambiguous improvement to the code. (Of course, this does not mean the
+project needs a bunch of very trivial patches whose only purpose is to gain commit
+access; knowing what's worth a patch post and what's not is part of showing good
+judgement.)
+
+
Coding guidelines
-----------------
This section discusses coding style and other guidelines for hacking
diff --git a/MAINTAINERS b/MAINTAINERS
deleted file mode 100644
index f9d27bb29..000000000
--- a/MAINTAINERS
+++ /dev/null
@@ -1,7 +0,0 @@
-Tristan Van Berkom
-E-mail: tristan.vanberkom@codethink.co.uk
-Userid: tvb
-
-Jürg Billeter
-E-mail: juerg.billeter@codethink.co.uk
-Userid: juergbi
diff --git a/contrib/COMMITTERS.rst.j2 b/contrib/COMMITTERS.rst.j2
new file mode 100644
index 000000000..6f602d6c3
--- /dev/null
+++ b/contrib/COMMITTERS.rst.j2
@@ -0,0 +1,18 @@
+.. _committers:
+
+Committers
+==========
+
+Full commit access
+-------------------
+List of people with full commit access, i.e. blanket commit access to
+the BuildStream codebase. Note that this is not a full list of all
+contributors.
+
++-----------------------------------+-----------------------------------+
+| Full Name | GitLab User |
++===================================+===================================+
+{% for name, username in committers.items() -%}
+| {{ get_table_entry(name) }}| {{ get_table_entry(username)}}|
++-----------------------------------+-----------------------------------+
+{% endfor %}
diff --git a/contrib/update_committers.py b/contrib/update_committers.py
new file mode 100755
index 000000000..f0faac327
--- /dev/null
+++ b/contrib/update_committers.py
@@ -0,0 +1,111 @@
+#!/usr/bin/env python3
+"""A script to set up COMMITTERS.rst according to gitlab committers."""
+
+import os
+import subprocess
+import argparse
+import json
+import urllib.request
+import urllib.parse
+from jinja2 import Environment, FileSystemLoader
+from collections import OrderedDict
+
+GIT_ERROR = 1
+MERGE_REQUEST = 'https://gitlab.com/api/v4/projects/1975139/merge_requests'
+ALL_MEMBERS = 'https://gitlab.com/api/v4/projects/1975139/members/all'
+PROTECTED = 'https://gitlab.com/api/v4/projects/1975139/protected_branches/master'
+USERS = 'https://gitlab.com/api/v4/users/{}'
+MAX_LEN = len('Full Name ')
+
+
+def get_committers(token: str) -> OrderedDict:
+ request = urllib.request.Request(PROTECTED)
+ request.add_header('PRIVATE-TOKEN', token)
+ response = urllib.request.urlopen(request).read().decode('utf-8')
+ named_developers = [x['user_id'] for x in json.loads(response)['merge_access_levels']]
+ request = urllib.request.Request(ALL_MEMBERS)
+ request.add_header('PRIVATE-TOKEN', token)
+ all_members = json.loads(urllib.request.urlopen(request).read().decode('utf-8'))
+ names_usernames_dictionary = OrderedDict()
+ for contributor in all_members:
+ if contributor['access_level'] >= 40:
+ names_usernames_dictionary[contributor['name']] = contributor['username']
+ for contributor in named_developers:
+ if contributor:
+ request = urllib.request.Request(USERS.format(contributor))
+ response = json.loads(urllib.request.urlopen(request).read().decode('utf-8'))
+ if response['name'] != 'bst-marge-bot':
+ names_usernames_dictionary[response['name']] = response['username']
+ return names_usernames_dictionary
+
+
+def get_table_entry(entry: str) -> str:
+ res = entry
+ for _ in range(MAX_LEN - len(entry)):
+ res = res + ' '
+ return res
+
+
+def find_repository_root() -> str:
+ root = os.getcwd()
+ try:
+ root = subprocess.check_output('git rev-parse --show-toplevel', shell=True)
+ except CalledProcessError as e:
+ print('The current working directory is not a git repository. \
+ \"git rev-parse --show-toplevel\" exited with code {}.'.format(e.returncode))
+ sys.exit(GIT_ERROR)
+ return root.rstrip().decode('utf-8')
+
+
+def create_committers_file(committers: OrderedDict):
+ contrib_directory = os.path.join(find_repository_root(), 'contrib')
+ file_loader = FileSystemLoader(contrib_directory)
+ env = Environment(loader=file_loader)
+ template = env.get_template('COMITTERS.rst.j2')
+ render_output = template.render(committers=committers, get_table_entry=get_table_entry)
+ committers_file = os.path.join(contrib_directory, 'COMMITTERS.rst')
+
+ with open(committers_file, 'w') as f:
+ f.write(render_output)
+
+
+def commit_changes_if_needed(token: str):
+ committers_file = os.path.join(find_repository_root(), 'contrib/COMMITTERS.rst')
+ git_diff = subprocess.call('git diff --quiet {}'.format(committers_file), shell=True)
+ if git_diff:
+ commit_message = '\'contrib: Update COMMITTERS.rst\''
+ branch_name = 'update_committers'
+ subprocess.call('git add {}'.format(committers_file), shell=True)
+ subprocess.call('git commit -m {}'.format(commit_message), shell=True)
+ try:
+ subprocess.call('git push -u origin {} 2>&1'.format(branch_name),
+ shell=True)
+ except CalledProcessError as e:
+ print('Could not push to remote branch. \"git push -u origin {}\" \
+ exited with code {}.'.format(branch_name, e.returncode))
+ sys.exit(GIT_ERROR)
+ data = urllib.parse.urlencode({'source_branch': 'update_committers',
+ 'target_branch': 'master',
+ 'title': 'Update COMMITTERS.rst file'})
+ request = urllib.request.Request(MERGE_REQUEST, data=bytearray(data, 'ASCII'))
+ request.add_header('PRIVATE-TOKEN', token)
+ response = urllib.request.urlopen(request).read().decode('utf-8')
+
+
+def main():
+ parser = argparse.ArgumentParser(
+ description="Update gitlab committers according to COMMITTERS.rst"
+ )
+ parser.add_argument(
+ "token", type=str,
+ help="Your private access token. See https://gitlab.com/profile/personal_access_tokens."
+ )
+ args = parser.parse_args()
+
+ committers = get_committers(args.token)
+ create_committers_file(committers)
+ commit_changes_if_needed(args.token)
+
+
+if __name__ == '__main__':
+ main()