Example #1
0
def act(repo, dry_run, instance, settings):
    gitlab_cli = GitLabApi(instance, project_url=repo, settings=settings)
    project_owners = RepoOwners(git_cli=gitlab_cli)

    for mr in gitlab_cli.get_merge_requests(state=MRState.OPENED):
        mr_approval = MRApproval(gitlab_client=gitlab_cli,
                                 merge_request=mr,
                                 owners=project_owners,
                                 dry_run=dry_run)

        if mr_approval.top_commit_created_at is None:
            _LOG.info([
                f'Project:{gitlab_cli.project.id} '
                f'Merge Request:{mr.iid} '
                f'- skipping'
            ])
            continue

        approval_status = mr_approval.get_approval_status()
        if approval_status['approved']:
            if mr_approval.has_approval_label():
                _LOG.info([
                    f'Project:{gitlab_cli.project.id} '
                    f'Merge Request:{mr.iid} '
                    f'- already approved'
                ])
                continue
            _LOG.info([
                f'Project:{gitlab_cli.project.id} '
                f'Merge Request:{mr.iid} '
                f'- approving now'
            ])
            if not dry_run:
                gitlab_cli.add_label_to_merge_request(mr.iid, APPROVAL_LABEL)
            continue

        if not dry_run:
            if mr_approval.has_approval_label():
                _LOG.info([
                    f'Project:{gitlab_cli.project.id} '
                    f'Merge Request:{mr.iid} '
                    f'- removing approval'
                ])
                gitlab_cli.remove_label_from_merge_request(
                    mr.iid, APPROVAL_LABEL)

        if approval_status['report'] is not None:
            _LOG.info([
                f'Project:{gitlab_cli.project.id} '
                f'Merge Request:{mr.iid} '
                f'- publishing approval report'
            ])

            if not dry_run:
                gitlab_cli.remove_label_from_merge_request(
                    mr.iid, APPROVAL_LABEL)
                mr.notes.create({'body': approval_status['report']})
            continue

        _LOG.info([
            f'Project:{gitlab_cli.project.id} '
            f'Merge Request:{mr.iid} '
            f'- not fully approved'
        ])
Example #2
0
class GitlabForkCompliance:

    OK = 0x0000
    ERR_MASTER_BRANCH = 0x0001
    ERR_NOT_A_MEMBER = 0x0002
    ERR_NOT_A_MAINTAINER = 0x0004

    def __init__(self, project_id, mr_id, maintainers_group):
        self.exit_code = self.OK

        self.maintainers_group = maintainers_group

        self.instance = queries.get_gitlab_instance()
        self.settings = queries.get_app_interface_settings()

        self.gl_cli = GitLabApi(self.instance, project_id=project_id,
                                settings=self.settings)
        self.mr = self.gl_cli.get_merge_request(mr_id)

        self.src = GitLabApi(self.instance,
                             project_id=self.mr.source_project_id,
                             settings=self.settings)

    def run(self):
        self.exit_code |= self.check_branch()
        self.exit_code |= self.check_bot_access()
        if self.exit_code:
            sys.exit(self.exit_code)

        # At this point, we know that the bot is a maintainer, so
        # we check if all the maintainers are in the fork, adding those
        # who are not
        group = self.gl_cli.gl.groups.get(self.maintainers_group)
        maintainers = group.members.list()
        project_maintainers = self.src.get_project_maintainers()
        for member in maintainers:
            if member.username in project_maintainers:
                continue
            LOG.info([f'adding {member.username} as maintainer'])
            user_payload = {'user_id': member.id,
                            'access_level': MAINTAINER_ACCESS}
            member = self.src.project.members.create(user_payload)
            member.save()
        # Last but not least, we remove the blocked label, in case
        # it is set
        mr_labels = self.gl_cli.get_merge_request_labels(self.mr.iid)
        if BLOCKED_LABEL in mr_labels:
            self.gl_cli.remove_label_from_merge_request(self.mr.iid,
                                                        BLOCKED_LABEL)

        sys.exit(self.exit_code)

    def check_branch(self):
        # The Merge Request can use the 'master' source branch
        if self.mr.source_branch == 'master':
            self.handle_error('source branch can not be master', MSG_BRANCH)
            return self.ERR_MASTER_BRANCH

        return self.OK

    def check_bot_access(self):
        # The bot needs access to the fork project
        try:
            project_bot = self.src.project.members.get(self.gl_cli.user.id)
        except GitlabGetError:
            self.handle_error('access denied for user {bot}', MSG_ACCESS)
            return self.ERR_NOT_A_MEMBER

        # The bot has to be a maintainer of the fork project
        if not project_bot or project_bot.access_level != MAINTAINER_ACCESS:
            self.handle_error('{bot} is not a maintainer in the fork project',
                              MSG_ACCESS)
            return self.ERR_NOT_A_MAINTAINER

        return self.OK

    def handle_error(self, log_msg, mr_msg):
        LOG.error([log_msg.format(bot=self.gl_cli.user.username)])
        self.gl_cli.add_label_to_merge_request(self.mr.iid,
                                               BLOCKED_LABEL)
        comment = mr_msg.format(user=self.mr.author['username'],
                                bot=self.gl_cli.user.username,
                                project_name=self.gl_cli.project.name)
        self.mr.notes.create({'body': comment})
def run(dry_run):
    instance = queries.get_gitlab_instance()
    settings = queries.get_app_interface_settings()
    repos = queries.get_repos_gitlab_owner(server=instance['url'])

    for repo in repos:
        gitlab_cli = GitLabApi(instance, project_url=repo, settings=settings)
        project_owners = RepoOwners(git_cli=gitlab_cli)

        for mr in gitlab_cli.get_merge_requests(state=MRState.OPENED):
            mr_approval = MRApproval(gitlab_client=gitlab_cli,
                                     merge_request=mr,
                                     owners=project_owners,
                                     dry_run=dry_run)

            approval_status = mr_approval.get_approval_status()
            if approval_status['approved']:
                if mr_approval.has_approval_label():
                    _LOG.info([
                        f'Project:{gitlab_cli.project.id} '
                        f'Merge Request:{mr.iid} '
                        f'- already approved'
                    ])
                    continue
                _LOG.info([
                    f'Project:{gitlab_cli.project.id} '
                    f'Merge Request:{mr.iid} '
                    f'- approving now'
                ])
                if not dry_run:
                    gitlab_cli.add_label_to_merge_request(
                        mr.iid, APPROVAL_LABEL)
                continue

            if not dry_run:
                if mr_approval.has_approval_label():
                    _LOG.info([
                        f'Project:{gitlab_cli.project.id} '
                        f'Merge Request:{mr.iid} '
                        f'- removing approval'
                    ])
                    gitlab_cli.remove_label_from_merge_request(
                        mr.iid, APPROVAL_LABEL)

            if approval_status['report'] is not None:
                _LOG.info([
                    f'Project:{gitlab_cli.project.id} '
                    f'Merge Request:{mr.iid} '
                    f'- publishing approval report'
                ])

                if not dry_run:
                    gitlab_cli.remove_label_from_merge_request(
                        mr.iid, APPROVAL_LABEL)
                    mr.notes.create({'body': approval_status['report']})
                continue

            _LOG.info([
                f'Project:{gitlab_cli.project.id} '
                f'Merge Request:{mr.iid} '
                f'- not fully approved'
            ])