From 2c563ff9f30ae51cc6699894143480e13c0aef6a Mon Sep 17 00:00:00 2001 From: Zed <601306339@qq.com> Date: Mon, 23 Aug 2021 16:04:40 +0800 Subject: [PATCH 01/65] release_iseus_status_auto_reply --- scripts/release_issue_status/main.py | 48 +++++--- .../release_issue_status/reply_generator.py | 110 ++++++++++++++++++ .../release_issue_status/update_issue_body.py | 97 +++++++++++++++ 3 files changed, 241 insertions(+), 14 deletions(-) create mode 100644 scripts/release_issue_status/reply_generator.py create mode 100644 scripts/release_issue_status/update_issue_body.py diff --git a/scripts/release_issue_status/main.py b/scripts/release_issue_status/main.py index 9f9e5a35145d..b0549a3ba305 100644 --- a/scripts/release_issue_status/main.py +++ b/scripts/release_issue_status/main.py @@ -5,10 +5,13 @@ from datetime import date, datetime import subprocess as sp from azure.storage.blob import BlobClient +import reply_generator as rg +from update_issue_body import update_issue_body +import traceback _NULL = ' ' _FILE_OUT = 'release_issue_status.csv' -_PYTHON_SDK_ADMINISTRATORS = {'msyyc', 'RAY-316', 'BigCat20196'} +_PYTHON_SDK_ADMINISTRATORS = ['msyyc', 'RAY-316', 'BigCat20196'] def my_print(cmd): @@ -85,9 +88,11 @@ def _extract_author_latest_comment(comments): def _whether_author_comment(comments): q = set(comment.user.login for comment in comments) - diff = q.difference(_PYTHON_SDK_ADMINISTRATORS) + for administrators in _PYTHON_SDK_ADMINISTRATORS: + q.discard(administrators) + + return True if len(q) > 0 else False - return len(diff) > 0 def _latest_comment_time(comments, delay_from_create_date): q = [(comment.updated_at.timestamp(), comment.user.login) @@ -96,6 +101,7 @@ def _latest_comment_time(comments, delay_from_create_date): return delay_from_create_date if not q else int((time.time() - q[-1][0]) / 3600 / 24) + def main(): # get latest issue status g = Github(os.getenv('TOKEN')) # please fill user_token @@ -135,30 +141,44 @@ def main(): # rule2: if latest comment is from author, need response asap # rule3: if comment num is 0, it is new issue, better to deal with it asap # rule4: if delay from latest update is over 7 days, better to deal with it soon. - # rule5: if delay from created date is over 30 days and owner never reply, close it. - # rule6: if delay from created date is over 15 days and owner never reply, remind owner to handle it. + # rule5: if delay from created date is over 30 days, better to close. + # rule6: if delay from created date is over 30 days and owner never reply, close it. + # rule7: if delay from created date is over 15 days and owner never reply, remind owner to handle it. for item in issue_status: if item.status == 'release': item.bot_advice = 'better to release asap.' elif item.author == item.author_latest_comment: item.bot_advice = 'new comment for author.' - elif item.comment_num == 0: + elif item.comment_num == 0 and 'Python' in item.labels: item.bot_advice = 'new issue and better to confirm quickly.' + if 'auto-link' not in item.labels: + if not update_issue_body(item.link): + item.bot_advice = 'failed to modify the body of the new issue. Please modify manually' + item.labels.append('attention') + item.labels.append('auto-link') + item.issue_object.set_labels(*item.labels) + try: + time.sleep(3) + reply = rg.ReplyGenerator(issue_object=item.issue_object) + reply.run() + except Exception as e: + print('Error from auto reply ========================') + print('Issue:{}'.format(item.issue_object.number)) + print(traceback.format_exc()) + print('==============================================') elif item.delay_from_latest_update >= 7: item.bot_advice = 'delay for a long time and better to handle now.' - + if item.days_from_latest_commit >= 30 and item.language == 'Python' and '30days attention' not in item.labels: item.labels.append('30days attention') item.issue_object.set_labels(*item.labels) - item.issue_object.create_comment(f'hi @{item.author}, the issue is closed since there is no reply for a long time. Please reopen it if necessary or create new one.') - item.issue_object.edit(state='close') + issue.issue_object.edit(state='close') elif item.days_from_latest_commit >= 15 and item.language == 'Python' and '15days attention' not in item.labels: - item.issue_object.create_comment(f'hi @{item.author}, this release-request has been delayed more than 15 days,' - ' please deal with it ASAP. We will close the issue if there is still no response after 15 days!') + item.issue_object.create_comment('hi @{} ,this release-request has been delayed more than 15 days,' + ' please deal with it ASAP'.format(item.author)) item.labels.append('15days attention') item.issue_object.set_labels(*item.labels) - - + # judge whether there is duplicated issue for same package if item.package != _NULL and duplicated_issue.get((item.language, item.package)) > 1: item.bot_advice = f'Warning:There is duplicated issue for {item.package}. ' + item.bot_advice @@ -182,4 +202,4 @@ def main(): if __name__ == '__main__': - main() + main() \ No newline at end of file diff --git a/scripts/release_issue_status/reply_generator.py b/scripts/release_issue_status/reply_generator.py new file mode 100644 index 000000000000..123a4928368b --- /dev/null +++ b/scripts/release_issue_status/reply_generator.py @@ -0,0 +1,110 @@ +from github import Github +import re +import os + + +class ReplyGenerator(object): + + def __init__(self, issue_object): + self.issue_object = issue_object + self.labels = [label.name for label in self.issue_object.labels] + self.comments = self.issue_object.get_comments() + self.body = self.issue_object.body + self.body_parse = {} + self._body_parse() + self.rest_specs_object = None + self.whether_change_readme = False + self._whether_change_readme() + self.whl_link = None + self.changelog_content = None + self.latest_pr_number = None + + def _whether_change_readme(self): + g = Github(os.getenv('TOKEN')) + repo = g.get_repo('Azure/azure-rest-api-specs') + self.rest_specs_object = repo + contents = repo.get_contents(self.body_parse['readme_path']) + contents = str(contents.decoded_content) + pattern_tag = re.compile(r'tag: package-[\w+-.]+') + package_tag = pattern_tag.search(contents).group() + package_tag = package_tag.split(':')[1].strip() + readme_python_contents = str(repo.get_contents(self.body_parse['readme_python_path']).decoded_content) + whether_multi_api = 'multi-api' in readme_python_contents + whether_same_tag = package_tag == self.body_parse['readme_tag'] + self.whether_change_readme = not (whether_same_tag and (not whether_multi_api or 'MultiAPI' in self.labels)) + + def _body_parse(self): + pattern_readme = re.compile(r'/specification/([\w-]+/)+readme.md') + pattern_resource_manager = re.compile(r'/specification/([\w-]+/)+resource-manager') + pattern_tag = re.compile(r'package-[\w+-.]+') + readme_path = pattern_readme.search(self.body).group() + readme_tag = pattern_tag.search(self.body).group() + resource_manager = pattern_resource_manager.search(self.body).group() + self.body_parse['readme_path'] = readme_path + self.body_parse['readme_python_path'] = readme_path[:readme_path.rfind('/')] + '/readme.python.md' + self.body_parse['readme_tag'] = readme_tag + self.body_parse['resource_manager'] = resource_manager + + def _get_latest_pr_from_readme(self): + commits = self.rest_specs_object.get_commits(path=self.body_parse['resource_manager']) + latest_commit = [commit for commit in commits][0] + latest_pr_brief = latest_commit.commit.message + latest_pr_number = re.findall('\(\#[0-9]+\)', latest_pr_brief) + latest_pr_number_int = [] + for number in latest_pr_number: + number = int(re.search('\d+', number).group()) + latest_pr_number_int.append(number) + latest_pr_number_int.sort() + + return latest_pr_number_int[-1] + + def _latest_pr_parse(self): + latest_pr = self.rest_specs_object.get_issue(self.latest_pr_number) + latest_pr_comments = latest_pr.get_comments() + b = [i for i in latest_pr_comments] + for comment in latest_pr_comments: + if '

Swagger Generation Artifacts

' in comment.body: + return self._swagger_generator_parse(comment.body) + + def _swagger_generator_parse(self, context): + track1_info_model = '' + try: + if ' azure-sdk-for-python' in context: + pattern_python_t1 = re.compile(' azure-sdk-for-python.+?', re.DOTALL) + python_t1 = re.search(pattern_python_t1, context).group() + prttern_python_track1 = re.compile('