From 1388d72ac76c2de326d064290b383314db1454e8 Mon Sep 17 00:00:00 2001 From: Alexandru Fazakas Date: Fri, 26 Jul 2019 16:28:08 +0100 Subject: contrib: Update COMMITTERS automatically Since we want the COMMITTERS list to be sync'd with the GitLab permissions, use a script for fetching all the information and building a table with it automatically. The script uses a template located in the contrib/ directory. Closes https://gitlab.com/BuildStream/buildstream/issues/1071 --- contrib/COMMITTERS.rst.j2 | 18 +++++++ contrib/update_committers.py | 111 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 129 insertions(+) create mode 100644 contrib/COMMITTERS.rst.j2 create mode 100755 contrib/update_committers.py (limited to 'contrib') 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() -- cgit v1.2.1