| #!/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() |