Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

labhub.py: add migrate_issue plugin #521

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the regex won't work for gitlab sub groups, see regex for other commands to see how its done

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this regex is most likely be duplicated in every command, make a new constant with the regex to match github/gitlab url, and then append it to your command. Do this as part of another issue after this is complete 😉

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@meetmangukiya , I used the same regex as used in unassign_cmd and it uses the same regex. How should I go about this? 😕

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about we only capture the URL here, without using regex to parse it, and then split it with a helper function that can be re-used in each command which takes a git hoster URL ?

# 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()