From f965b29f6e57c41ae6fb70d6172f973addda422d Mon Sep 17 00:00:00 2001 From: Muhammad Farhan Khan Date: Fri, 26 Jul 2024 17:52:06 +0500 Subject: [PATCH] feat: Adds check to ensure no repo have any outside collaborator --- edx_repo_tools/repo_checks/repo_checks.py | 58 +++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/edx_repo_tools/repo_checks/repo_checks.py b/edx_repo_tools/repo_checks/repo_checks.py index 94968acb..fb94fd22 100644 --- a/edx_repo_tools/repo_checks/repo_checks.py +++ b/edx_repo_tools/repo_checks/repo_checks.py @@ -1119,6 +1119,63 @@ def fix(self, dry_run=False): return steps +class EnsureNoOutsideCollaborators(Check): + """ + Repository shouldn't have outside collaborators + """ + + def __init__(self, api: GhApi, org: str, repo: str): + super().__init__(api, org, repo) + self.users_list = [] + + def is_relevant(self) -> bool: + """ + All non security fork repos, public or private. + """ + return not is_security_private_fork(self.api, self.org_name, self.repo_name) + + def check(self) -> tuple[bool, str]: + """ + Verify whether or not the check is failing. + + This should not change anything and should not have a side-effect + other than populating `self` with any data that is needed later for + `fix` or `dry_run`. + + The string in the return tuple should be a human readable reason + that the check failed. + """ + self.users_list = list(all_paged_items( + self.api.repos.list_collaborators, owner=self.org_name, repo=self.repo_name, affiliation='outside' + )) + users = [f"{user.login}: {user.role_name}" for user in self.users_list] + if users: + return ( + False, + f"The repo has some outside collaborators:\n\t\t" + + "\n\t\t".join(users), + ) + return (True, "The repo doesn't have any outside collaborators.") + + def dry_run(self): + return self.fix(dry_run=True) + + def fix(self, dry_run=False): + steps = [] + for user in self.users_list: + if not dry_run: + self.api.repos.remove_collaborator( + owner=self.org_name, + repo=self.repo_name, + username=user.login, + ) + steps.append( + f"Removed outside collaborator {user.login}" + ) + + return steps + + CHECKS = [ RequiredCLACheck, RequireTriageTeamAccess, @@ -1127,6 +1184,7 @@ def fix(self, dry_run=False): EnsureNoAdminOrMaintainTeams, EnsureRepoSettings, EnsureNoDirectRepoAccessToUsers, + EnsureNoOutsideCollaborators, ] CHECKS_BY_NAME = {check_cls.__name__: check_cls for check_cls in CHECKS} CHECKS_BY_NAME_LOWER = {check_cls.__name__.lower(): check_cls for check_cls in CHECKS}