Skip to content

Commit

Permalink
labhub.py: add migrate_issue plugin
Browse files Browse the repository at this point in the history
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 description and comments. Source issue is
referenced in the target issue and closed after
the migration is completed.

Closes #518
  • Loading branch information
aabhaas-vaish committed May 16, 2018
1 parent a3a6cda commit 5e517e3
Show file tree
Hide file tree
Showing 2 changed files with 192 additions and 0 deletions.
84 changes: 84 additions & 0 deletions plugins/labhub.py
Original file line number Diff line number Diff line change
Expand Up @@ -381,3 +381,87 @@ 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
"""
source_host = match.group(1)
source_org = match.group(2)
source_repo = match.group(3)
issue_number = match.group(4)
target_host = match.group(5)
target_org = match.group(6)
target_repo = match.group(7)

user = msg.frm.nick

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

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

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

if target_repo 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:
source_issue = self.REPOS[source_repo].get_issue(int(issue_number))
source_labels = source_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(source_issue.state) != 'open':
return 'Issue must be open in order to be migrated!'

source_url = 'https://{}.com/{}/{}/issues/{}'.format(
source_host, source_org, source_repo, issue_number)

ext_msg = ('\n\nThis is a migrated issue originally opened by @{} as {}'
' and was migrated by @{}')
target_issue_desc = source_issue.description.rstrip() + ext_msg.format(
source_issue.author.username, source_url, str(user))
target_issue = self.REPOS[target_repo].create_issue(
source_issue.title, target_issue_desc)
target_issue.labels = source_labels

comment_ext = '\n\nOriginally commented by @{} on {} UTC'

for comment in source_issue.comments:
target_comm = comment.body.rstrip() + comment_ext.format(
comment.author.username, str(comment.updated))
target_issue.add_comment(target_comm)

target_url = 'https://{}.com/{}/{}/issues/{}'.format(
target_host, target_org, target_repo, target_issue.number)

migrate_comm = 'Issue has been migrated to this [repository]({}) by @{}'
source_issue.add_comment(migrate_comm.format(
target_url, str(user)))

source_labels.add('Invalid')
source_issue.labels = source_labels
source_issue.close()

return 'Issue has been successfully migrated: {}'.format(target_url)
108 changes: 108 additions & 0 deletions tests/labhub_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -344,3 +344,111 @@ 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/{}/{}/'
issue_check = 'Issue desc\n\nThis is a migrated issue originally opened by @{} as {} and was migrated by @{}'
comment_check = 'Comment body\n\nOriginally commented by @{} on {} UTC'

# 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 desc'
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.updated = PropertyMock()
mock_comment.updated = '07/04/2018'

testbot.assertCommand(cmd.format('coala', 'a', '21', 'coala', 'b'),
'successfully migrated:')

self.mock_repo.get_issue.assert_called_with(21)

self.mock_repo.create_issue.assert_called_with('Issue title',
issue_check.format('random-access7', 'https://github.com/coala/a/issues/21', 'None'))

mock_issue2.add_comment.assert_called_with(comment_check.format('random-access7',
'07/04/2018'))

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 5e517e3

Please sign in to comment.