From 31ecd7a5de2cdd1dc6c78712b08c1bc1bf9a04df Mon Sep 17 00:00:00 2001 From: random-access7 Date: Tue, 3 Apr 2018 01:05:55 +0530 Subject: [PATCH] labhub.py: add migrate_issue plugin Includes a migrate issue plugin that migrates an issue from one repo to another subject to conditions on the command like only maintainers can perform migration, issue must exist and issue must not be closed already. The plugin copies the issue title, issue description but appends the URL of the old issue and handle of the user that migrated the issue, to the description of the new issue. All comments are copied and written along with other details like author, date/time and URL of the old comment. Also includes tests to check functionality. Closes https://github.com/coala/corobo/issues/518 --- plugins/labhub.py | 97 ++++++++++++++++++++++++++++++++++++++++++++ tests/labhub_test.py | 88 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 185 insertions(+) diff --git a/plugins/labhub.py b/plugins/labhub.py index 7616ba06..5ee9fa41 100644 --- a/plugins/labhub.py +++ b/plugins/labhub.py @@ -381,3 +381,100 @@ def pr_stats(self, msg, match): state=type(self).community_state(pr_count) ) yield reply + + @re_botcmd(pattern=r'^migrate\s+https://github.com/([^/]+)/([^/]+)/+issues/(\d+)\s+https://github.com/([^/]+)/([^/]+)/*$', # Ignore LineLengthBear, PyCodeStyleBear + # Ignore LineLengthBear, PyCodeStyleBear + re_cmd_name_help='migrate ', + flags=re.IGNORECASE) + def migrate_issue(self, msg, match): + """ + Migrate an issue from one repo + to another repo of coala + """ + org = match.group(1) + repo_name_orig = match.group(2) + issue_number = match.group(3) + user = msg.frm.nick + org2 = match.group(4) + repo_name_final = match.group(5) + + try: + assert org == self.GH_ORG_NAME or org == self.GL_ORG_NAME + except AssertionError: + yield 'First repository not owned by our org.' + return + + try: + assert org2 == self.GH_ORG_NAME or org2 == self.GL_ORG_NAME + except AssertionError: + yield 'Second repository not owned by our org.' + return + + if (repo_name_orig in self.REPOS) and (repo_name_final in self.REPOS): + pass + else: + yield 'Repository does not exist!' + return + + if self.TEAMS[self.GH_ORG_NAME + ' maintainers'].is_member(user): + pass + else: + yield tenv().get_template( + 'labhub/errors/not-maintainer.jinja2.md' + ).render( + action='migrate issues', + target=user, + ) + return + + try: + old_issue = self.REPOS[repo_name_orig].get_issue(int(issue_number)) + old_labels = old_issue.labels + + except RuntimeError: + yield 'Issue does not exist!' + return + + if str(old_issue.state) == 'closed': + yield 'Issue cannot be migrated as it has been closed already.' + return + else: + pass + + url1 = 'https://github.com/{}/{}/issues/{}' + new_issue_title = old_issue.title + new_issue_description = old_issue.description.rstrip() + extra_msg = '\n\nMigrated issue from '+url1.format( + org, repo_name_orig, issue_number) + \ + ' by @' + str(user) + + new_issue = self.REPOS[repo_name_final].create_issue( + new_issue_title, new_issue_description+extra_msg) + new_issue.labels = old_labels + + old_comm = old_issue.comments + + for i in range(len(old_comm)): + author = old_comm[i].author + comm_text = old_comm[i].body.rstrip() + updated = old_comm[i].updated + comm_url = url1.format(org, repo_name_orig, issue_number) + \ + '#issuecomment-' + str(old_comm[i].number) + new_body = comm_text + '\n\nOriginally written by @' + \ + author + ' on ' + \ + str(updated) + ' UTC' + \ + ' and you can view it [here!](' + comm_url + ')' + new_issue.add_comment(new_body) + + url2 = 'https://github.com/{}/{}/issues/{}'.format( + org, repo_name_final, new_issue.number) + + migrated_comm = 'Issue has been migrated to another [repository](' + \ + url2 + ') by @' + str(user) + old_issue.add_comment(migrated_comm) + old_labels.add('Invalid') + old_issue.labels = old_labels + old_issue.close() + + yield 'New issue created: {}'.format(url2) + return diff --git a/tests/labhub_test.py b/tests/labhub_test.py index 86de6d34..a80493d6 100644 --- a/tests/labhub_test.py +++ b/tests/labhub_test.py @@ -25,6 +25,7 @@ def setUp(self): self.mock_org = create_autospec(github3.orgs.Organization) self.mock_gh = create_autospec(github3.GitHub) + self.mock_team = create_autospec(github3.orgs.Team) self.mock_team.name = PropertyMock() self.mock_team.name = 'mocked team' @@ -343,3 +344,90 @@ def test_invite_me(self): 'Command \"hey\" / \"hey there\" not found.') with self.assertRaises(queue.Empty): testbot.pop_message() + + def test_migrate_issue(self): + plugins.labhub.GitHub = create_autospec(IGitt.GitHub.GitHub.GitHub) + plugins.labhub.GitLab = create_autospec(IGitt.GitLab.GitLab.GitLab) + labhub, testbot = plugin_testbot(plugins.labhub.LabHub, logging.ERROR) + labhub.activate() + + labhub.REPOS = { + 'a': self.mock_repo, + 'b': self.mock_repo + } + + mock_maint_team = create_autospec(github3.orgs.Team) + mock_maint_team.is_member.return_value = False + + labhub.TEAMS = { + 'coala maintainers': mock_maint_team, + 'coala developers': self.mock_team, + 'coala newcomers': self.mock_team + } + cmd = '!migrate https://github.com/{}/{}/issues/{} https://github.com/{}/{}/' + + # Not a maintainer + testbot.assertCommand(cmd.format('coala', 'a', '21','coala','b'), + 'you are not a maintainer!') + # Unknown first org + testbot.assertCommand(cmd.format('coa', 'a', '23','coala','b'), + 'First repository not owned by our org') + # Unknown second org + testbot.assertCommand(cmd.format('coala', 'a', '23','coa','b'), + 'Second repository not owned by our org') + # Repo does not exist + testbot.assertCommand(cmd.format('coala', 'c', '23','coala','b'), + 'Repository does not exist') + # No issue exists + mock_maint_team.is_member.return_value = True + self.mock_repo.get_issue = Mock(side_effect=RuntimeError) + testbot.assertCommand(cmd.format('coala', 'a', '21','coala','b'), + 'Issue does not exist!') + # Issue closed + mock_maint_team.is_member.return_value = True + mock_iss = create_autospec(IGitt.GitHub.GitHub.GitHubIssue) + self.mock_repo.get_issue = Mock(return_value=mock_iss) + mock_iss.labels = PropertyMock() + mock_iss.state = PropertyMock() + mock_iss.state = 'closed' + testbot.assertCommand(cmd.format('coala', 'a', '21','coala','b'), + 'has been closed already') + # Migrate issue + mock_maint_team.is_member.return_value = True + mock_iss = create_autospec(IGitt.GitHub.GitHub.GitHubIssue) + issue2 = create_autospec(IGitt.GitHub.GitHub.GitHubIssue) + + self.mock_repo.get_issue = Mock(return_value=mock_iss) + label_prop = PropertyMock(return_value={'enhancement','bug'}) + type(mock_iss).labels = label_prop + mock_iss.title = PropertyMock() + mock_iss.labels = 'Issue title' + mock_iss.description = PropertyMock() + mock_iss.description = 'Issue description' + mock_iss.state = PropertyMock() + mock_iss.state = 'open' + + self.mock_repo.create_issue = Mock(return_value=issue2) + issue2.labels = PropertyMock() + + mock_comment = create_autospec(IGitt.GitHub.GitHub.GitHubComment) + mock_comment2 = create_autospec(IGitt.GitHub.GitHub.GitHubComment) + + mock_iss.comments = PropertyMock() + mock_iss.comments = list() + mock_iss.comments.append(mock_comment) + mock_comment.author = PropertyMock() + mock_comment.author = 'random-access7' + mock_comment.body = PropertyMock() + mock_comment.body = 'Comment bobdy' + mock_comment.number = PropertyMock() + mock_comment.number = 1743 + mock_comment.updated = PropertyMock() + mock_comment.updated = '07/04/2018' + + issue2.add_comment = Mock(return_value=mock_comment2) + mock_iss.add_comment = Mock(return_value=mock_comment2) + mock_iss.close = Mock(return_value=True) + + testbot.assertCommand(cmd.format('coala', 'a', '21','coala','b'), + 'issue created:')