brintos

brintos / llvm-project-archived public Read only

0
0
Text · 9.9 KiB · 52b9fd3 Raw
322 lines · python
1#!/usr/bin/env python32# ===-- commit-access-review.py  --------------------------------------------===#3#4# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.5# See https://llvm.org/LICENSE.txt for license information.6# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception7#8# ===------------------------------------------------------------------------===#9#10# ===------------------------------------------------------------------------===#11 12import datetime13import github14import re15import requests16import time17import sys18import re19 20 21class User:22    THRESHOLD = 523 24    def __init__(self, name, triage_list):25        self.name = name26        self.authored = 027        self.merged = 028        self.reviewed = 029        self.triage_list = triage_list30 31    def add_authored(self, val=1):32        self.authored += val33        if self.meets_threshold():34            print(self.name, "meets the threshold with authored commits")35            del self.triage_list[self.name]36 37    def set_authored(self, val):38        self.authored = 039        self.add_authored(val)40 41    def add_merged(self, val=1):42        self.merged += val43        if self.meets_threshold():44            print(self.name, "meets the threshold with merged commits")45            del self.triage_list[self.name]46 47    def add_reviewed(self, val=1):48        self.reviewed += val49        if self.meets_threshold():50            print(self.name, "meets the threshold with reviewed commits")51            del self.triage_list[self.name]52 53    def get_total(self):54        return self.authored + self.merged + self.reviewed55 56    def meets_threshold(self):57        return self.get_total() >= self.THRESHOLD58 59    def __repr__(self):60        return "{} : a: {} m: {} r: {}".format(61            self.name, self.authored, self.merged, self.reviewed62        )63 64 65def check_manual_requests(66    gh: github.Github, start_date: datetime.datetime67) -> list[str]:68    """69    Return a list of users who have been asked since ``start_date`` if they70    want to keep their commit access or if they have applied for commit71    access since ``start_date``72    """73 74    query = """75        query ($query: String!, $after: String) {76          search(query: $query, type: ISSUE, first: 100, after: $after) {77            nodes {78              ... on Issue {79                author {80                  login81                }82                body83              }84            }85            pageInfo {86              hasNextPage87              endCursor88            }89          }90        }91        """92    formatted_start_date = start_date.strftime("%Y-%m-%dT%H:%M:%S")93    variables = {94        "query": f"type:issue created:>{formatted_start_date} org:llvm repo:llvm-project label:infra:commit-access,infra:commit-access-request"95    }96 97    has_next_page = True98    users = []99    while has_next_page:100        res_header, res_data = gh._Github__requester.graphql_query(101            query=query, variables=variables102        )103        data = res_data["data"]104        for issue in data["search"]["nodes"]:105            users.extend([user[1:] for user in re.findall("@[^ ,\n]+", issue["body"])])106            if issue["author"]:107                users.append(issue["author"]["login"])108        has_next_page = data["search"]["pageInfo"]["hasNextPage"]109        if has_next_page:110            variables["after"] = data["search"]["pageInfo"]["endCursor"]111    return users112 113 114def get_num_commits(gh: github.Github, user: str, start_date: datetime.datetime) -> int:115    """116    Get number of commits that ``user`` has been made since ``start_date`.117    """118    variables = {119        "owner": "llvm",120        "user": user,121        "start_date": start_date.strftime("%Y-%m-%dT%H:%M:%S"),122    }123 124    user_query = """125        query ($user: String!) {126          user(login: $user) {127            id128          }129        }130    """131 132    res_header, res_data = gh._Github__requester.graphql_query(133        query=user_query, variables=variables134    )135    data = res_data["data"]136    variables["user_id"] = data["user"]["id"]137 138    query = """139        query ($owner: String!, $user_id: ID!, $start_date: GitTimestamp!){140          organization(login: $owner) {141            teams(query: "llvm-committers" first:1) {142              nodes {143                repositories {144                  nodes {145                    ref(qualifiedName: "main") {146                      target {147                        ... on Commit {148                          history(since: $start_date, author: {id: $user_id }) {149                            totalCount150                          }151                        }152                      }153                    }154                  }155                }156              }157            }158          }159        }160     """161    count = 0162    res_header, res_data = gh._Github__requester.graphql_query(163        query=query, variables=variables164    )165    data = res_data["data"]166    for repo in data["organization"]["teams"]["nodes"][0]["repositories"]["nodes"]:167        count += int(repo["ref"]["target"]["history"]["totalCount"])168        if count >= User.THRESHOLD:169            break170    return count171 172 173def get_review_count(174    gh: github.Github, user: str, start_date: datetime.datetime175) -> int:176    """177    Return the number of reviews that ``user`` has done since ``start_date``.178    """179    query = """180        query ($query: String!) {181          search(query: $query, type: ISSUE, first: 5) {182            issueCount183          }184        }185        """186    formatted_start_date = start_date.strftime("%Y-%m-%dT%H:%M:%S")187    variables = {188        "owner": "llvm",189        "repo": "llvm-project",190        "user": user,191        "query": f"type:pr commenter:{user} -author:{user} merged:>{formatted_start_date} org:llvm",192    }193 194    res_header, res_data = gh._Github__requester.graphql_query(195        query=query, variables=variables196    )197    data = res_data["data"]198    return int(data["search"]["issueCount"])199 200 201def count_prs(gh: github.Github, triage_list: dict, start_date: datetime.datetime):202    """203    Fetch all the merged PRs for the project since ``start_date`` and update204    ``triage_list`` with the number of PRs merged for each user.205    """206 207    query = """208        query ($query: String!, $after: String) {209          search(query: $query, type: ISSUE, first: 100, after: $after) {210            issueCount,211            nodes {212              ... on PullRequest {213                 author {214                   login215                 }216                 mergedBy {217                   login218                 }219              }220            }221            pageInfo {222              hasNextPage223              endCursor224            }225          }226        }227    """228    date_begin = start_date229    date_end = None230    while date_begin < datetime.datetime.now():231        date_end = date_begin + datetime.timedelta(days=7)232        formatted_date_begin = date_begin.strftime("%Y-%m-%dT%H:%M:%S")233        formatted_date_end = date_end.strftime("%Y-%m-%dT%H:%M:%S")234        variables = {235            "query": f"type:pr is:merged merged:{formatted_date_begin}..{formatted_date_end} org:llvm",236        }237        has_next_page = True238        while has_next_page:239            print(variables)240            res_header, res_data = gh._Github__requester.graphql_query(241                query=query, variables=variables242            )243            data = res_data["data"]244            for pr in data["search"]["nodes"]:245                # Users can be None if the user has been deleted.246                if not pr["author"]:247                    continue248                author = pr["author"]["login"]249                if author in triage_list:250                    triage_list[author].add_authored()251 252                if not pr["mergedBy"]:253                    continue254                merger = pr["mergedBy"]["login"]255                if author == merger:256                    continue257                if merger not in triage_list:258                    continue259                triage_list[merger].add_merged()260 261            has_next_page = data["search"]["pageInfo"]["hasNextPage"]262            if has_next_page:263                variables["after"] = data["search"]["pageInfo"]["endCursor"]264        date_begin = date_end265 266 267def main():268    token = sys.argv[1]269    gh = github.Github(login_or_token=token)270    org = gh.get_organization("llvm")271    repo = org.get_repo("llvm-project")272    one_year_ago = datetime.datetime.now() - datetime.timedelta(days=365)273    triage_list = {}274    for collaborator in repo.get_collaborators(permission="push"):275        triage_list[collaborator.login] = User(collaborator.login, triage_list)276 277    print("Start:", len(triage_list), "triagers")278    # Step 0 Check if users have requested commit access in the last year.279    for user in check_manual_requests(gh, one_year_ago):280        if user in triage_list:281            print(user, "requested commit access in the last year.")282            del triage_list[user]283    print("After Request Check:", len(triage_list), "triagers")284 285    # Step 1 count all PRs authored or merged286    count_prs(gh, triage_list, one_year_ago)287 288    print("After PRs:", len(triage_list), "triagers")289 290    if len(triage_list) == 0:291        sys.exit(0)292 293    # Step 2 check for reviews294    for user in list(triage_list.keys()):295        review_count = get_review_count(gh, user, one_year_ago)296        triage_list[user].add_reviewed(review_count)297 298    print("After Reviews:", len(triage_list), "triagers")299 300    if len(triage_list) == 0:301        sys.exit(0)302 303    # Step 3 check for number of commits304    for user in list(triage_list.keys()):305        num_commits = get_num_commits(gh, user, one_year_ago)306        # Override the total number of commits to not double count commits and307        # authored PRs.308        triage_list[user].set_authored(num_commits)309 310    print("After Commits:", len(triage_list), "triagers")311 312    print("Complete:", len(triage_list), "triagers")313 314    with open("triagers.log", "w") as triagers_log:315        for user in triage_list:316            print(triage_list[user].__repr__())317            triagers_log.write(user + "\n")318 319 320if __name__ == "__main__":321    main()322