From 824cc7d59cf9900e7003e0e94c2d7e530d39d67d Mon Sep 17 00:00:00 2001 From: Thomas Bui <43018778+Thomas-Boi@users.noreply.github.com> Date: Fri, 28 Jan 2022 10:58:30 -0800 Subject: [PATCH] New Feature: Add an Issue `in-develop` labeler and functions to close these issues (#990) * Working on labeler * Add in_develop_labeler * Build bot can close issues based on labels * Test mode for build * Fixed errors in labeler_bot * Update build-bot * Update .github/scripts/icomoon_build.py Co-authored-by: David Leal * Remove test url Co-authored-by: David Leal Co-authored-by: Clemens Bastian <8781699+amacado@users.noreply.github.com> --- .github/scripts/build_assets/api_handler.py | 85 ++++++++++++++++++++- .github/scripts/build_assets/arg_getters.py | 15 ++++ .github/scripts/icomoon_build.py | 5 ++ .github/scripts/in_develop_labeler.py | 20 +++++ .github/workflows/build_icons.yml | 2 +- .github/workflows/in_develop_labeler.yml | 27 +++++++ 6 files changed, 151 insertions(+), 3 deletions(-) create mode 100644 .github/scripts/in_develop_labeler.py create mode 100644 .github/workflows/in_develop_labeler.yml diff --git a/.github/scripts/build_assets/api_handler.py b/.github/scripts/build_assets/api_handler.py index f75a20ccd..fef1db575 100644 --- a/.github/scripts/build_assets/api_handler.py +++ b/.github/scripts/build_assets/api_handler.py @@ -1,8 +1,12 @@ import requests import sys import re +from typing import List +# our base url which leads to devicon +base_url = "https://api.github.com/repos/devicons/devicon/" + def get_merged_pull_reqs_since_last_release(token): """ Get all the merged pull requests since the last release. @@ -38,7 +42,7 @@ def get_merged_pull_reqs(token, page): :param token, a GitHub API token. :param page, the page number. """ - queryPath = "https://api.github.com/repos/devicons/devicon/pulls" + url = base_url + "pulls" headers = { "Authorization": f"token {token}" } @@ -50,7 +54,7 @@ def get_merged_pull_reqs(token, page): } print(f"Querying the GitHub API for requests page #{page}") - response = requests.get(queryPath, headers=headers, params=params) + response = requests.get(url, headers=headers, params=params) if not response: print(f"Can't query the GitHub API. Status code is {response.status_code}. Message is {response.text}") sys.exit(1) @@ -99,3 +103,80 @@ def find_all_authors(pull_req_data, token): authors.add(commit["commit"]["author"]["name"]) print(f"This URL didn't have an `author` attribute: {pull_req_data['commits_url']}") return ", ".join(["@" + author for author in list(authors)]) + + +def label_issues(token: str, issues: List[str], labels: List[str]): + """ + Label the issues specified with the label specified. + :param token: the GitHub API token. + :param issues: the issue numbers (as str) that we are labelling. + :param labels: the labels that we are labelling. + """ + headers = { + "Authorization": f"token {token}", + "accept": "application/vnd.github.v3+json" + } + url = base_url + "issues/{}/labels" + for issue in issues: + body = { + "labels": labels + } + response = requests.post(url.format(issue), headers=headers, json=body) + if not response: + raise Exception(f"Can't label the Issue provided. Issue: {issue}, labels: {labels}, API response: " + response.text) + else: + print(f"Successfully labelled issue {issue}") + + +def close_issues(token: str, issues: List[str]): + """ + Close issues. + :param token: the GitHub API token. + :param issues: the issue numbers (as str) that we are labelling. + """ + headers = { + "Authorization": f"token {token}", + "accept": "application/vnd.github.v3+json" + } + url = base_url + "issues/{}" + body = { + "state": "closed" + } + for issue in issues: + response = requests.patch(url.format(issue), headers=headers, json=body) + if not response: + raise Exception(f"Can't close Issue provided. Issue: {issue}, API response: " + response.text) + else: + print(f"Successfully closed issue {issue}") + + +def get_issues_by_labels(token: str, labels: List[str]): + """ + Get a list of issues based on their labels. + :param token: the GitHub API token. + :param labels: the labels that we are labelling. + """ + url = base_url + "issues?per_page=100&labels={}&page={}" + headers = { + "Authorization": f"token {token}", + "accept": "application/vnd.github.v3+json" + } + issues = [] + done = False + page_num = 1 + while not done: + response = requests.get(url.format(",".join(labels), page_num), headers=headers) + if not response: + raise Exception(f"Can't access API. Can't get issues for labels: {labels}, API response: " + response.text) + else: + results = response.json() + if len(results) < 100: + done = True # we are done + else: + page_num += 1 # page is full => might need to check another page + + # GitHub API also returns PRs for issues queries => have to check + issues_only = [issue for issue in results if issue.get("pull_request") is None] + issues.extend(issues_only) + + return issues diff --git a/.github/scripts/build_assets/arg_getters.py b/.github/scripts/build_assets/arg_getters.py index 80b88d3c2..71f7d16b8 100644 --- a/.github/scripts/build_assets/arg_getters.py +++ b/.github/scripts/build_assets/arg_getters.py @@ -68,3 +68,18 @@ def get_release_message_args(): help="The GitHub token to access the GitHub REST API.", type=str) return parser.parse_args() + + +def get_in_develop_labeler_args(): + """ + Get the commandline arguments for in_develop_labeler.py. + """ + parser = ArgumentParser(description="Parse the PR body to find the issue(s) we are labelling.") + parser.add_argument("token", + help="The GitHub token to access the GitHub REST API.", + type=str) + + parser.add_argument("body", + help="The PR's initial comment by the author AKA the `body` attribute of the `pull_request` API object.", + type=str) + return parser.parse_args() diff --git a/.github/scripts/icomoon_build.py b/.github/scripts/icomoon_build.py index 1a362c697..ab45b33c4 100644 --- a/.github/scripts/icomoon_build.py +++ b/.github/scripts/icomoon_build.py @@ -49,6 +49,11 @@ def main(): print("Creating the release message by querying the GitHub API...") get_release_message(args.token) + print("Closing issues with the `in-develop` label.") + issues = api_handler.get_issues_by_labels(args.token, ["in-develop"]) + issue_nums = [issue_num["number"] for issue_num in issues] + api_handler.close_issues(args.token, issue_nums) + print("Task completed.") except TimeoutException as e: util.exit_with_err("Selenium Time Out Error: \n" + str(e)) diff --git a/.github/scripts/in_develop_labeler.py b/.github/scripts/in_develop_labeler.py new file mode 100644 index 000000000..c065b9667 --- /dev/null +++ b/.github/scripts/in_develop_labeler.py @@ -0,0 +1,20 @@ +import re +from build_assets import arg_getters, api_handler + +def main(): + args = arg_getters.get_in_develop_labeler_args() + try: + # find the issue closing line + issue_line = [line for line in args.body.split("\n") if line.startswith("**This PR closes")][0] + + print("Issue Line is " + issue_line) + issue_pattern = re.compile(r"\d+") + issues_numbers = issue_pattern.findall(issue_line) + print("Labelling issues: " + str(issues_numbers)) + api_handler.label_issues(args.token, issues_numbers, ["in-develop"]) + except IndexError: # if can't find the issue line + print("The PR body doesn't contain `**This PR closes` keywords. Ending workflow.") + return + +if __name__ == "__main__": + main() diff --git a/.github/workflows/build_icons.yml b/.github/workflows/build_icons.yml index 2d1d68e62..4747bd0bc 100644 --- a/.github/workflows/build_icons.yml +++ b/.github/workflows/build_icons.yml @@ -23,7 +23,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: > python ./.github/scripts/icomoon_build.py - ./.github/scripts/build_assets/geckodriver-v0.29.1-win64/geckodriver.exe ./icomoon.json + ./.github/scripts/build_assets/geckodriver-v0.30.0-win64/geckodriver.exe ./icomoon.json ./devicon.json ./icons ./ %GITHUB_TOKEN% --headless - name: Upload geckodriver.log for debugging purposes diff --git a/.github/workflows/in_develop_labeler.yml b/.github/workflows/in_develop_labeler.yml new file mode 100644 index 000000000..61b7b4335 --- /dev/null +++ b/.github/workflows/in_develop_labeler.yml @@ -0,0 +1,27 @@ +name: Label Issue In Develop +on: + pull_request: + types: [closed] +jobs: + label: + name: Label Issue In Develop + runs-on: ubuntu-18.04 + if: github.event.pull_request.merged == true + steps: + - uses: actions/checkout@v2 + + - name: Setup Python v3.8 + uses: actions/setup-python@v2 + with: + python-version: 3.8 + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r ./.github/scripts/requirements.txt + + - name: Run in_develop_labeler.py + env: + TOKEN: ${{ secrets.GITHUB_TOKEN }} + BODY: ${{ github.event.pull_request.body }} + run: python ./.github/scripts/in_develop_labeler.py $TOKEN "$BODY"