Example #1
0
 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()
Example #2
0
 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()
Example #3
0
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))
Example #4
0
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})
Example #5
0
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
Example #6
0
 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()
Example #7
0
    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
Example #8
0
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)
Example #9
0
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()
Example #10
0
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)
Example #11
0
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()
Example #12
0
    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()
Example #13
0
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)
Example #14
0
 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))
Example #15
0
 def test__str__(self):
     repo = Repository(full_name=self.full_name,
                       provider=self.provider,
                       user=self.user)
     assert str(repo) == self.full_name