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:')