blob: 927c02ec05de1ea19bf8a8fd681dd38a230d1a1f [file] [log] [blame]
#!/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()