Skip to content

Commit

Permalink
labhub.py: add migrate_issue plugin
Browse files Browse the repository at this point in the history
Introduce migrate_issue plugin that adds ability
to migrate an issue from a source repo to target
repo, both owned by the org.

Issue title, issue  description and all comments
are copied with a few additional details appended to
the the description and comments. Source issue is
referenced in the target issue and is closed after
the migration is complete.

Closes #518
  • Loading branch information
aabhaas-vaish committed Apr 17, 2018
1 parent 7a9e09b commit e4528f1
Show file tree
Hide file tree
Showing 2 changed files with 191 additions and 0 deletions.
85 changes: 85 additions & 0 deletions plugins/labhub.py
Original file line number Diff line number Diff line change
Expand Up @@ -381,3 +381,88 @@ def pr_stats(self, msg, match):
state=type(self).community_state(pr_count)
)
yield reply

@re_botcmd(pattern=r'^migrate\s+https://(github|gitlab)\.com/([^/]+)/([^/]+)/+issues/(\d+)\s+https://(github|gitlab)\.com/([^/]+)/([^/]+)/*$', # Ignore LineLengthBear, PyCodeStyleBear
# Ignore LineLengthBear, PyCodeStyleBear
re_cmd_name_help='migrate <complete-source-issue-URL> <complete-target-repo-URL>',
flags=re.IGNORECASE)
def migrate_issue(self, msg, match):
"""
Migrate an issue from source repo
to target repo owned by the org
"""
orig_host = match.group(1)
org = match.group(2)
repo_name_orig = match.group(3)
issue_number = match.group(4)
user = msg.frm.nick
final_host = match.group(5)
org2 = match.group(6)
repo_name_final = match.group(7)

if org != self.GH_ORG_NAME and org != self.GL_ORG_NAME:
return 'Source repository not owned by our org.'

if org2 != self.GH_ORG_NAME and org2 != self.GL_ORG_NAME:
return 'Target repository not owned by our org.'

if repo_name_orig not in self.REPOS:
return 'Source repository does not exist.'

if repo_name_final not in self.REPOS:
return 'Target repository does not exist.'

if not self.TEAMS[self.GH_ORG_NAME + ' maintainers'].is_member(user):
return tenv().get_template(
'labhub/errors/not-maintainer.jinja2.md'
).render(
action='migrate issues',
target=user,
)

try:
old_issue = self.REPOS[repo_name_orig].get_issue(int(issue_number))
old_labels = old_issue.labels

except RuntimeError as err:
sterr, errno = err.args
if errno == 404:
return 'Issue does not exist!'
else:
raise RuntimeError(sterr, errno)

if str(old_issue.state) != 'open':
return 'Issue must be open in order to be migrated!'

url1 = 'https://{}.com/{}/{}/issues/{}'.format(
orig_host, org, repo_name_orig, issue_number)
new_issue_title = old_issue.title
issue_author = old_issue.author.username

# Ignore LineLengthBear, PyCodeStyleBear
ext_msg = '\n\nThis is a migrated issue originally opened by @{} as {} and was migrated by @{}'
new_issue_descrip = old_issue.description.rstrip() + ext_msg.format(
issue_author, url1, str(user))
new_issue = self.REPOS[repo_name_final].create_issue(
new_issue_title, new_issue_descrip)
new_issue.labels = old_labels

# Ignore LineLengthBear, PyCodeStyleBear
comment_ext = '\n\nOriginally commented by @{} on {} UTC and can be seen [here]({})'

for comment in old_issue.comments:
comm_text = comment.body.rstrip()
comm_url = url1 + '#issuecomment-' + str(comment.number)
new_body = comm_text + comment_ext.format(
comment.author.username, str(comment.updated), comm_url)
new_issue.add_comment(new_body)

url2 = 'https://{}.com/{}/{}/issues/{}'.format(
final_host, org, repo_name_final, new_issue.number)
migrate_comm = 'Issue has been migrated to this [repository]({}) by @{}'
old_issue.add_comment(migrate_comm.format(url2, str(user)))
old_labels.add('Invalid')
old_issue.labels = old_labels
old_issue.close()

return 'New issue created: {}'.format(url2)
106 changes: 106 additions & 0 deletions tests/labhub_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -343,3 +343,109 @@ 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'),
'Source repository not owned by our org')
# Unknown second org
testbot.assertCommand(cmd.format('coala', 'a', '23', 'coa', 'b'),
'Target repository not owned by our org')
# Repo does not exist
testbot.assertCommand(cmd.format('coala', 'c', '23', 'coala', 'b'),
'Source repository does not exist')
# Repo does not exist
testbot.assertCommand(cmd.format('coala', 'a', '23', 'coala', 'e'),
'Target repository does not exist')
# No issue exists
mock_maint_team.is_member.return_value = True
self.mock_repo.get_issue = Mock(side_effect=RuntimeError('Error message', 404))
testbot.assertCommand(cmd.format('coala', 'a', '21', 'coala', 'b'),
'Issue does not exist!')
# Runtime error
mock_maint_team.is_member.return_value = True
self.mock_repo.get_issue = Mock(side_effect=RuntimeError('Error message', 403))
testbot.assertCommand(cmd.format('coala', 'a', '21', 'coala', 'b'),
'Computer says')
# Issue closed
mock_maint_team.is_member.return_value = True
mock_issue = create_autospec(IGitt.GitHub.GitHub.GitHubIssue)
self.mock_repo.get_issue = Mock(return_value=mock_issue)
mock_issue.labels = PropertyMock()
mock_issue.state = PropertyMock()
mock_issue.state = 'closed'
testbot.assertCommand(cmd.format('coala', 'a', '21', 'coala', 'b'),
'Issue must be open')
# Migrate issue
mock_maint_team.is_member.return_value = True
mock_issue = create_autospec(IGitt.GitHub.GitHub.GitHubIssue)
mock_issue2 = create_autospec(IGitt.GitHub.GitHub.GitHubIssue)

self.mock_repo.get_issue = Mock(return_value=mock_issue)
label_prop = PropertyMock(return_value=set())
type(mock_issue).labels = label_prop
mock_issue.title = PropertyMock()
mock_issue.title = 'Issue title'
mock_issue.description = PropertyMock()
mock_issue.description = 'Issue description'
mock_issue.state = PropertyMock()
mock_issue.state = 'open'
mock_issue.author.username = PropertyMock()
mock_issue.author.username = 'random-access7'

self.mock_repo.create_issue = Mock(return_value=mock_issue2)
mock_issue2.labels = PropertyMock()
mock_issue2.number = PropertyMock()
mock_issue2.number = 45

mock_comment = create_autospec(IGitt.GitHub.GitHub.GitHubComment)
mock_comment2 = create_autospec(IGitt.GitHub.GitHub.GitHubComment)

mock_issue.comments = PropertyMock()
mock_issue.comments = list()
mock_issue.comments.append(mock_comment)
mock_comment.author.username = PropertyMock()
mock_comment.author.username = 'random-access7'
mock_comment.body = PropertyMock()
mock_comment.body = 'Comment body'
mock_comment.number = PropertyMock()
mock_comment.number = 172
mock_comment.updated = PropertyMock()
mock_comment.updated = '07/04/2018'

testbot.assertCommand(cmd.format('coala', 'a', '21', 'coala', 'b'),
'issue created:')

self.mock_repo.get_issue.assert_called_with(21)
self.mock_repo.create_issue.assert_called_with('Issue title',
'Issue description\n\nThis is a migrated issue originally opened by @random-access7 ' + \
'as https://github.com/coala/a/issues/21 and was migrated by @None')
mock_issue2.add_comment.assert_called_with(
'Comment body\n\nOriginally commented by @random-access7 on 07/04/2018 UTC and ' + \
'can be seen [here](https://github.com/coala/a/issues/21#issuecomment-172)')
mock_issue.add_comment.assert_called_with(
'Issue has been migrated to this [repository](https://github.com/coala/b/issues/45) by @None')
mock_issue.close.assert_called_with()

0 comments on commit e4528f1

Please sign in to comment.