Skip to content

Commit

Permalink
New Feature: Add an Issue in-develop labeler and functions to close…
Browse files Browse the repository at this point in the history
… 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 <halfpacho@gmail.com>

* Remove test url

Co-authored-by: David Leal <halfpacho@gmail.com>
Co-authored-by: Clemens Bastian <8781699+amacado@users.noreply.github.com>
  • Loading branch information
3 people authored Jan 28, 2022
1 parent 7b06adf commit 824cc7d
Show file tree
Hide file tree
Showing 6 changed files with 151 additions and 3 deletions.
85 changes: 83 additions & 2 deletions .github/scripts/build_assets/api_handler.py
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -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}"
}
Expand All @@ -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)
Expand Down Expand Up @@ -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
15 changes: 15 additions & 0 deletions .github/scripts/build_assets/arg_getters.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
5 changes: 5 additions & 0 deletions .github/scripts/icomoon_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
20 changes: 20 additions & 0 deletions .github/scripts/in_develop_labeler.py
Original file line number Diff line number Diff line change
@@ -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()
2 changes: 1 addition & 1 deletion .github/workflows/build_icons.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
27 changes: 27 additions & 0 deletions .github/workflows/in_develop_labeler.yml
Original file line number Diff line number Diff line change
@@ -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"

0 comments on commit 824cc7d

Please sign in to comment.