Skip to content

Commit

Permalink
labhub.py: add migrate_issue plugin
Browse files Browse the repository at this point in the history
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 coala#518
  • Loading branch information
aabhaas-vaish committed Apr 11, 2018
1 parent 583785c commit fc8945e
Show file tree
Hide file tree
Showing 2 changed files with 190 additions and 0 deletions.
100 changes: 100 additions & 0 deletions plugins/labhub.py
Original file line number Diff line number Diff line change
Expand Up @@ -381,3 +381,103 @@ 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-issue-URL-to-be-copied-from> <complete-repo-URL-to-be-copied-into>',
flags=re.IGNORECASE)
def migrate_issue(self, msg, match):
"""
Migrate an issue from one repo
to another repo of coala
"""
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)

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 not in self.REPOS:
yield 'First repository does not exist!'
return

if repo_name_final not in self.REPOS:
yield 'Second repository does not exist!'
return

if not self.TEAMS[self.GH_ORG_NAME + ' maintainers'].is_member(user):
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 as err:
sterr, errno = err.args
if errno == 404:
yield 'Issue does not exist!'
return
else:
raise RuntimeError(sterr, errno)

if str(old_issue.state) == 'closed':
yield 'Issue cannot be migrated as it has been closed already.'
return

url1 = 'https://{}.com/{}/{}/issues/{}'
new_issue_title = old_issue.title
new_issue_description = old_issue.description.rstrip()
issue_author = old_issue.author.username
extra_msg = '\n\nMigrated issue originally opened by @'+issue_author+ \
' from ' + url1.format(
orig_host, org, repo_name_orig, issue_number) + \
' Migration done by @' + str(user)
new_issue_description += extra_msg
new_issue = self.REPOS[repo_name_final].create_issue(
new_issue_title, new_issue_description)
new_issue.labels = old_labels

for comment in old_issue.comments:
comm_text = comment.body.rstrip()
comm_url = url1.format(
orig_host, org, repo_name_orig, issue_number) + \
'#issuecomment-' + str(comment.number)
new_body = comm_text + '\n\nOriginally written by @' + \
comment.author.username + ' on ' + \
str(comment.updated) + ' UTC' + \
' and you can view it [here!](' + comm_url + ')'
new_issue.add_comment(new_body)

url2 = 'https://{}.com/{}/{}/issues/{}'.format(
final_host, 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
90 changes: 90 additions & 0 deletions tests/labhub_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -343,3 +344,92 @@ 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('Error message',404))
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'
mock_iss.author.username = PropertyMock()
mock_iss.author.username = 'random-access7'

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.username = PropertyMock()
mock_comment.author.username = 'random-access7'
mock_comment.body = PropertyMock()
mock_comment.body = 'Comment body'
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:')

0 comments on commit fc8945e

Please sign in to comment.