def test_user(self): repo = Repository(full_name=self.full_name, provider=self.provider, identifier=0) # Don't allow saving if not linked to a user with pytest.raises(ValidationError): repo.save()
def test_defaults(self): repo = Repository(user=self.user) # checking default values assert not repo.active assert repo.full_name is None assert repo.provider is None # Don't allow none objects to be saved with pytest.raises(IntegrityError): repo.save()
def apply_command_on_merge_request( pr: MergeRequest, comment: Comment, enable_rebase: bool = False, enable_merge: bool = False, enable_fastforward: bool = False, merge_admin_only: bool = True, fastforward_admin_only: bool = True, ): """ Performs a merge, fastforward or rebase of a merge request when an authorized user posts a command mentioning the keywords ``merge``, ``fastforward``/``ff`` or ``rebase`` respectively. e.g. ``@gitmate-bot rebase`` rebases the pull request with master. """ username = Repository.from_igitt_repo(pr.repository).user.username cmd, cmd_past = get_matched_command(comment.body, username) enabled_cmd = { 'rebase': enable_rebase, 'merge': enable_merge, 'fastforward': enable_fastforward }.get(cmd) if enabled_cmd: if not verify_command_access(comment, merge_admin_only, fastforward_admin_only, cmd): pr.add_comment( f'Hey @{comment.author.username}, you do not have the access ' f'to perform the {cmd} action with [GitMate.io]' '(https://gitmate.io). Please ask a maintainer to give you ' 'access. :warning:') return pr.add_comment( f'Hey! I\'m [GitMate.io](https://gitmate.io)! This pull request is' f' being {cmd_past} automatically. Please **DO NOT** push while ' f'{cmd} is in progress or your changes would be lost permanently ' ':warning:') head_clone_url = pr.source_repository.clone_url base_clone_url = pr.target_repository.clone_url output = run_in_container(settings.REBASER_IMAGE, 'python', 'run.py', cmd, head_clone_url, base_clone_url, pr.head_branch_name, pr.base_branch_name) output = json.loads(output) if output['status'] == 'success': pr.add_comment( f'Automated {cmd} with [GitMate.io](https://gitmate.io) was ' 'successful! :tada:') elif 'error' in output: # hiding oauth token for safeguarding user privacy error = output['error'].replace(head_clone_url, '<hidden_oauth_token>') error = error.replace(base_clone_url, '<hidden_oauth_token>') pr.add_comment(f'Automated {cmd} failed! Please {cmd} your pull ' 'request manually via the command line.\n\n' 'Reason:\n```\n{}\n```'.format(error))
def store_head_commit_sha(pr: MergeRequest): """ Stores the list of merge requests along with their heads and updates it on synchronizing again. """ MergeRequestModel.objects.update_or_create( repo=Repository.from_igitt_repo(pr.repository), number=pr.number, defaults={'head_sha': pr.head.sha})
def remove_merge_requests(pr: MergeRequest): """ Remove closed and merged MRs from database. """ repo = Repository.from_igitt_repo(pr.repository) try: MergeRequestModel.objects.get(repo=repo, number=pr.number).delete() except MergeRequestModel.DoesNotExist: # pragma: no cover # Merge request doesn't exist in db. Maybe it wasn't synchronized after # gitmate was enabled. pass
def test_validation(self): repo = Repository(user=self.user) # don't allow empty strings repo.full_name = '' repo.provider = '' with pytest.raises(ValidationError): repo.full_clean()
def test_not_implemented_igitt_repo_creation(self): self.auth = UserSocialAuth( user=self.user, provider='unknownprovider') self.auth.set_extra_data({ 'access_token': 'stupidshit' }) self.auth.save() with self.assertRaises(NotImplementedError): repo = Repository( user=self.user, provider='unknownprovider', full_name='some_repo', active=False ).igitt_repo
def gitmate_ack(pr: MergeRequest, comment: Comment, ack_strs: str = 'ack, reack', unack_strs: str = 'unack'): """ A responder to ack and unack commits """ body = comment.body.lower() commits = pr.commits perm_level = pr.repository.get_permission_level(comment.author) comment_slices = map_comment_parts_to_keywords(ack_strs, unack_strs, body) has_commit_sha = any( sha_compiled.search(string) for _list in comment_slices.values() for string in _list) # return right away if the comment isn't related to ack / unack command if not any(comment_slices) or not has_commit_sha: return elif perm_level.value < AccessLevel.CAN_WRITE.value: msg = ('Sorry @{}, you do not have the necessary permission ' 'levels to perform the action.'.format(comment.author.username)) pr.add_comment(msg) return db_pr, created = MergeRequestModel.objects.get_or_create( repo=Repository.from_igitt_repo(pr.repository), number=pr.number, defaults={'acks': dict()}) if created: # GitMate was integrated to the repo after syncing the pull request add_review_status(pr) db_pr.refresh_from_db() for commit in commits: for substring in comment_slices['unack']: if commit.sha[:6] in substring: db_pr.acks[_get_commit_hash(commit)] = _status_to_dict( unack(commit)) for substring in comment_slices['ack']: if commit.sha[:6] in substring: db_pr.acks[_get_commit_hash(commit)] = _status_to_dict( ack(commit)) db_pr.save() pr.head.set_status(db_pr.ack_state)
def remove_installed_repositories(installation: IGittInstallation, sender: IGittUser, repos: [IGittRepository]): """ Removes repositories from an existing installation. """ # add user to installation administrators db_user = get_user_if_exists(sender) if db_user: db_inst = Installation.from_igitt_installation(installation) db_inst.admins.add(db_user) db_inst.save() for repo in repos: db_repo = Repository.from_igitt_repo(repo) db_repo.installation = None db_repo.save()
def add_review_status(pr: MergeRequest): """ A responder to add pending/acknowledged/rejected status on commits on MergeRequest SYNCHRONIZED and OPENED events based on their generated commit hashes (generated from the unified diff and commit message). """ db_pr, _ = MergeRequestModel.objects.get_or_create( repo=Repository.from_igitt_repo(pr.repository), number=pr.number) head = pr.head hashes = [] for commit in pr.commits: commit_hash = _get_commit_hash(commit) hashes.append(commit_hash) # This commit was head of the PR before, deletion of the PR state is # not possible so we make it green to clean it up. if commit.sha == db_pr.last_head != head.sha: commit.set_status( CommitStatus(Status.SUCCESS, 'Outdated. Check ' + head.sha[:7] + ' instead.', 'review/gitmate/manual/pr', 'https://gitmate.io')) # copying status from unmodified commits in the same merge request if commit_hash in db_pr.acks: commit.set_status(_dict_to_status(db_pr.acks[commit_hash])) else: db_pr.acks[commit_hash] = _status_to_dict(pending(commit)) for chash in dict(db_pr.acks).keys(): if not chash in hashes: del db_pr.acks[chash] db_pr.last_head = head.sha db_pr.save() pr.head.set_status(db_pr.ack_state)
def sync_updated_pr_with_issue(pr: MergeRequest, sync_assignees: bool='Synchronize Assignees'): issues = pr.closes_issues repo = Repository.from_igitt_repo(pr.repository) pr_obj = MergeRequestModel.objects.get_or_create( repo=repo, number=pr.number)[0] data = defaultdict(dict) with lock_igitt_object('label mr', pr): labels = pr.labels for issue in issues: labels = issue.labels | labels pr.labels = labels if sync_assignees: with lock_igitt_object('assign mr', pr): assignees = pr.assignees for issue in issues: assignees |= issue.assignees data[str(issue.number)]['assignees'] = True pr.assignees = assignees pr_obj.closes_issues = data pr_obj.save()
def setUp(self): # Reconfigure gitmate for tests reinit_plugin('testplugin') self.factory = APIRequestFactory() self.user = User.objects.create_user( username='******', email='*****@*****.**', first_name='John', last_name='Appleseed') self.auth = UserSocialAuth(user=self.user, provider=Providers.GITHUB.value, uid=1) self.auth.set_extra_data({ 'access_token': os.environ['GITHUB_TEST_TOKEN'], 'id': 16681030, }) self.auth.save() self.gl_auth = UserSocialAuth(user=self.user, provider=Providers.GITLAB.value, uid=2) self.gl_auth.set_extra_data({ 'access_token': os.environ['GITLAB_TEST_TOKEN'], 'id': 1369631, }) self.gl_auth.save() self.gh_app_auth = UserSocialAuth(user=self.user, provider=Providers.GITHUB_APP.value, uid=1) self.gh_app_auth.set_extra_data({ 'access_token': os.environ['GITHUB_TEST_TOKEN'], 'id': 16681030 }) self.gh_app_auth.save() self.repo = Repository(user=self.user, identifier=49558751, full_name=os.environ['GITHUB_TEST_REPO'], provider=Providers.GITHUB.value, active=self.active) self.repo.save() # Needs an ID before adding relationship self.org = Organization( name=os.environ['GITHUB_TEST_REPO'].split('/', maxsplit=1)[0], primary_user=self.user, provider='github', ) self.org.save() # Needs an ID before adding relationship self.gh_inst = Installation(provider='github', identifier=1) self.gh_inst.save() # Needs an ID before adding relationship self.gh_inst.admins.add(self.user) self.gh_inst.save() self.gh_app_repo = Repository(identifier=49558751, full_name=os.environ['GITHUB_TEST_REPO'], provider=Providers.GITHUB_APP.value, active=self.active, installation=self.gh_inst) self.gh_app_repo.save() # Needs an ID before adding relationship self.repo.admins.add(self.user) self.org.admins.add(self.user) self.repo.save() self.org.save() self.gl_repo = Repository(user=self.user, identifier=3439658, full_name=os.environ['GITLAB_TEST_REPO'], provider=Providers.GITLAB.value, active=self.active) self.gl_repo.save() self.gl_repo.admins.add(self.user) self.gl_repo.save()
class GitmateTestCase(RecordedTestCase): """ A base class for setting up a dummy user, request factory and a repo for the user. """ active = False upmate = True def setUp(self): # Reconfigure gitmate for tests reinit_plugin('testplugin') self.factory = APIRequestFactory() self.user = User.objects.create_user( username='******', email='*****@*****.**', first_name='John', last_name='Appleseed') self.auth = UserSocialAuth(user=self.user, provider=Providers.GITHUB.value, uid=1) self.auth.set_extra_data({ 'access_token': os.environ['GITHUB_TEST_TOKEN'], 'id': 16681030, }) self.auth.save() self.gl_auth = UserSocialAuth(user=self.user, provider=Providers.GITLAB.value, uid=2) self.gl_auth.set_extra_data({ 'access_token': os.environ['GITLAB_TEST_TOKEN'], 'id': 1369631, }) self.gl_auth.save() self.gh_app_auth = UserSocialAuth(user=self.user, provider=Providers.GITHUB_APP.value, uid=1) self.gh_app_auth.set_extra_data({ 'access_token': os.environ['GITHUB_TEST_TOKEN'], 'id': 16681030 }) self.gh_app_auth.save() self.repo = Repository(user=self.user, identifier=49558751, full_name=os.environ['GITHUB_TEST_REPO'], provider=Providers.GITHUB.value, active=self.active) self.repo.save() # Needs an ID before adding relationship self.org = Organization( name=os.environ['GITHUB_TEST_REPO'].split('/', maxsplit=1)[0], primary_user=self.user, provider='github', ) self.org.save() # Needs an ID before adding relationship self.gh_inst = Installation(provider='github', identifier=1) self.gh_inst.save() # Needs an ID before adding relationship self.gh_inst.admins.add(self.user) self.gh_inst.save() self.gh_app_repo = Repository(identifier=49558751, full_name=os.environ['GITHUB_TEST_REPO'], provider=Providers.GITHUB_APP.value, active=self.active, installation=self.gh_inst) self.gh_app_repo.save() # Needs an ID before adding relationship self.repo.admins.add(self.user) self.org.admins.add(self.user) self.repo.save() self.org.save() self.gl_repo = Repository(user=self.user, identifier=3439658, full_name=os.environ['GITLAB_TEST_REPO'], provider=Providers.GITLAB.value, active=self.active) self.gl_repo.save() self.gl_repo.admins.add(self.user) self.gl_repo.save() def setUpWithPlugin(self, name: str): self.plugin = name self.plugin_config = apps.get_app_config(f'gitmate_{self.plugin}') GitmateTestCase.setUp(self) self.repo.plugins.append(self.plugin) self.repo.active = True self.repo.save() self.gl_repo.plugins.append(self.plugin) self.gl_repo.active = True self.gl_repo.save() def simulate_scheduled_responder_call(self, event: str, repo: Repository): ResponderRegistrar.respond(event, repo.igitt_repo, repo=repo) def simulate_github_webhook_call(self, event: str, data: dict): request = self.factory.post(reverse('webhooks:github'), data, format='json') hashed = hmac.new(bytes(os.environ['WEBHOOK_SECRET'], 'utf-8'), request.body, sha1) signature = 'sha1=' + hashed.hexdigest() request.META.update({ 'HTTP_X_HUB_SIGNATURE': signature, 'HTTP_X_GITHUB_EVENT': event, }) return github_webhook_receiver(request) def simulate_gitlab_webhook_call(self, event: str, data: dict): request = self.factory.post(reverse('webhooks:gitlab'), data, format='json') request.META.update({ 'HTTP_X_GITLAB_TOKEN': os.environ['WEBHOOK_SECRET'], 'HTTP_X_GITLAB_EVENT': event }) return gitlab_webhook_receiver(request)
def find_mrs_with_issue(cls, issue: Issue): repo = Repository.from_igitt_repo(issue.repository) return cls.objects.filter(repo=repo, closes_issues__has_key=str(issue.number))
def test__str__(self): repo = Repository(full_name=self.full_name, provider=self.provider, user=self.user) assert str(repo) == self.full_name