|  | #!/usr/bin/env python3 | 
|  | """A script to set up COMMITTERS.rst according to gitlab committers.""" | 
|  |  | 
|  | import os | 
|  | import sys | 
|  | 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']) | 
|  | except subprocess.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): | 
|  | repository_root = find_repository_root() | 
|  | contrib_directory = os.path.join(repository_root, 'contrib') | 
|  | file_loader = FileSystemLoader(contrib_directory) | 
|  | env = Environment(loader=file_loader, autoescape=True) | 
|  | template = env.get_template('COMMITTERS.rst.j2') | 
|  | render_output = template.render(committers=committers, get_table_entry=get_table_entry) | 
|  | committers_file = os.path.join(repository_root, '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(), 'COMMITTERS.rst') | 
|  | git_diff = subprocess.call(['git', 'diff', '--quiet', committers_file]) | 
|  | if git_diff: | 
|  | commit_message = '\'Update COMMITTERS.rst\'' | 
|  | branch_name = 'update_committers' | 
|  | subprocess.call(['git', 'add', committers_file]) | 
|  | subprocess.call(['git', 'commit', '-m', commit_message]) | 
|  | try: | 
|  | subprocess.check_call(['git', 'push', '-u', 'origin', branch_name], stderr=subprocess.STDOUT) | 
|  | except subprocess.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) | 
|  | if response.getcode() != 200: | 
|  | print("Failed to open MR; please open an MR for branch {} manually".format(branch_name)) | 
|  |  | 
|  |  | 
|  | 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() |