From cf6e9a94c1a0f0c9cd1c0eb9945713f61d44bcbd Mon Sep 17 00:00:00 2001 From: Ian H Pittwood Date: Thu, 27 Feb 2020 10:26:15 -0600 Subject: [PATCH] Implement Relates-To and Closes tagging --- gerrit_to_github_issues/engine.py | 84 ++++++++++++++---------- gerrit_to_github_issues/github_issues.py | 39 +++++++++-- 2 files changed, 82 insertions(+), 41 deletions(-) diff --git a/gerrit_to_github_issues/engine.py b/gerrit_to_github_issues/engine.py index dbdeb22..10bce0f 100644 --- a/gerrit_to_github_issues/engine.py +++ b/gerrit_to_github_issues/engine.py @@ -30,41 +30,53 @@ def update(gerrit_url: str, gerrit_project_name: str, github_project_name: str, def process_change(change: dict, repo: Repository, gerrit_url: str): - issue_number = github_issues.parse_issue_number(change['commitMessage']) - if not issue_number: + issue_numbers_dict = github_issues.parse_issue_number(change['commitMessage']) + if not issue_numbers_dict: LOG.warning(f'No issue tag found for change #{change["number"]}') return - try: - issue = repo.get_issue(issue_number) - except github.GithubException: - LOG.warning(f'Issue #{issue_number} not found for project') - return - comment_msg = '' - change_url = gerrit.make_gerrit_url(gerrit_url, change['number']) - link_exists = github_issues.check_issue_for_matching_comments(issue, change_url) - if issue.state == 'closed' and not link_exists: - issue.edit(state='open') - comment_msg += 'Issue reopened due to new activity on Gerrit.\n\n' - if 'WIP' in change['commitMessage'] or 'DNM' in change['commitMessage']: - LOG.debug(f'add `wip` to #{issue_number}') - issue.add_to_labels('wip') - try: - LOG.debug(f'rm `ready for review` to #{issue_number}') - issue.remove_from_labels('ready for review') - except github.GithubException: - LOG.debug(f'`ready for review` tag does not exist on issue #{issue_number}') - else: - LOG.debug(f'add `ready for review` to #{issue_number}') - issue.add_to_labels('ready for review') - try: - LOG.debug(f'rm `wip` to #{issue_number}') - issue.remove_from_labels('wip') - except github.GithubException: - LOG.debug(f'`wip` tag does not exist on issue #{issue_number}') - if not link_exists: - comment_msg += f'New Related Change: {change_url}\n' \ - f'Authored By: {change["owner"]["name"]} ({change["owner"]["email"]})' - if comment_msg: - issue.create_comment(comment_msg) - LOG.debug(f'Comment to post on #{issue_number}: {comment_msg}') - LOG.info(f'Comment posted to issue #{issue_number}') + for key, issues_list in issue_numbers_dict.items(): + for issue_number in issues_list: + try: + issue = repo.get_issue(issue_number) + except github.GithubException: + LOG.warning(f'Issue #{issue_number} not found for project') + return + comment_msg = '' + change_url = gerrit.make_gerrit_url(gerrit_url, change['number']) + link_exists = github_issues.check_issue_for_matching_comments(issue, change_url) + if issue.state == 'closed' and not link_exists: + LOG.debug(f'Issue #{issue_number} was closed, reopening...') + #issue.edit(state='open') + #issue.create_comment('Issue reopened due to new activity on Gerrit.\n\n') + labels = issue.get_labels() + if 'WIP' in change['commitMessage'] or 'DNM' in change['commitMessage']: + if 'wip' not in labels: + LOG.debug(f'add `wip` to #{issue_number}') + #issue.add_to_labels('wip') + if 'ready for review' in labels: + try: + LOG.debug(f'rm `ready for review` to #{issue_number}') + #issue.remove_from_labels('ready for review') + except github.GithubException: + LOG.debug(f'`ready for review` tag does not exist on issue #{issue_number}') + else: + if 'ready for review' not in labels: + LOG.debug(f'add `ready for review` to #{issue_number}') + #issue.add_to_labels('ready for review') + if 'wip' in labels: + try: + LOG.debug(f'rm `wip` to #{issue_number}') + #issue.remove_from_labels('wip') + except github.GithubException: + LOG.debug(f'`wip` tag does not exist on issue #{issue_number}') + if not link_exists: + comment_msg += '### New Related Change\n\n' \ + f'**Link:** {change_url}\n' \ + f'**Subject:** {change["subject"]}\n' \ + f'**Authored By:** {change["owner"]["name"]} ({change["owner"]["email"]})' + if key == 'closes': + comment_msg += '\n\nThis change will close this issue when merged.' + if comment_msg: + LOG.debug(f'Comment to post on #{issue_number}: {comment_msg}') + #issue.create_comment(comment_msg) + LOG.info(f'Comment posted to issue #{issue_number}') diff --git a/gerrit_to_github_issues/github_issues.py b/gerrit_to_github_issues/github_issues.py index cd5f927..f99af79 100644 --- a/gerrit_to_github_issues/github_issues.py +++ b/gerrit_to_github_issues/github_issues.py @@ -9,6 +9,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +import logging import re import github @@ -17,12 +18,40 @@ from github.Repository import Repository import errors +LOG = logging.getLogger(__name__) -def parse_issue_number(commit_msg: str) -> int: - match = re.search(r'(?<=\[#)(.*?)(?=\])', commit_msg) - if not match: - return None - return int(match.group(0)) + +def construct_issue_list(match_list: list) -> list: + new_list = [] + for issue in match_list: + try: + new_list.append(int(issue)) + except ValueError: + LOG.warning(f'Value {issue} could not be converted to `int` type') + return new_list + + +def parse_issue_number(commit_msg: str) -> dict: + # Searches for Relates-To or Closes tags first to match and return + LOG.debug(f'Parsing commit message: {commit_msg}') + related = re.findall(r'(?<=Relates-To: #)(.*?)(?=\n)', commit_msg) + LOG.debug(f'Captured related issues: {related}') + closes = re.findall(r'(?<=Closes: #)(.*?)(?=\n)', commit_msg) + LOG.debug(f'Captured closes issues: {closes}') + if related or closes: + return { + 'related': construct_issue_list(related), + 'closes': construct_issue_list(closes) + } + # If no Relates-To or Closes tags are defined, find legacy [#X] style tags + LOG.debug('Falling back to legacy tags') + legacy_matches = re.findall(r'(?<=\[#)(.*?)(?=\])', commit_msg) + LOG.debug(f'Captured legacy issues: {legacy_matches}') + if not legacy_matches: + return {} + return { + 'related': construct_issue_list(legacy_matches) + } def get_repo(repo_name: str, github_user: str, github_pw: str, github_token: str) -> Repository: