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' ])
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' ])