summaryrefslogtreecommitdiff
path: root/contrib/update_committers.py
blob: 927c02ec05de1ea19bf8a8fd681dd38a230d1a1f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#!/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()