From 46439880a6fb79d7c75bc9a5484ac21336ce1526 Mon Sep 17 00:00:00 2001 From: nir-valtman Date: Tue, 28 Dec 2021 17:20:39 -0500 Subject: [PATCH] Codeowners teams bug fix and distinct PR approvers --- config.yaml | 15 +++++++++---- run.py | 53 ++++++++++++++++++++++++++++++++------------- src/codeowners.py | 5 +++-- src/pull_request.py | 2 +- src/teams.py | 2 +- 5 files changed, 54 insertions(+), 23 deletions(-) diff --git a/config.yaml b/config.yaml index adda7f3..ea7b19b 100644 --- a/config.yaml +++ b/config.yaml @@ -124,7 +124,6 @@ members: - Chamomile-push - Echinacea-pull - Calendula-push - days_since_last_commit: [] - member_id: 90470369 login: archiet-gg @@ -149,7 +148,7 @@ members: - repo: Lavender days: 14 branch: feature_L01 - create_pr: True + create_pr: False - repo: Chamomile days: 0 branch: feature_C01 @@ -180,7 +179,11 @@ members: create_pr: False - repo: Lavender days: 5 - branch: feature_L02 + branch: feature_L02a + create_pr: True + - repo: Lavender + days: 20 + branch: feature_L02b create_pr: True - member_id: 90473779 login: codeyf-gg @@ -202,7 +205,11 @@ members: create_pr: True - repo: Chamomile days: 0 - branch: feature_C03 + branch: feature_C03a + create_pr: True + - repo: Chamomile + days: 14 + branch: feature_C03b create_pr: True - repo: Calendula days: 95 diff --git a/run.py b/run.py index e8c6811..94eae4c 100755 --- a/run.py +++ b/run.py @@ -44,7 +44,6 @@ async def create_repos(config, org): await r.delete_existing_repos() for repo_name in tqdm(config.repo_names, desc='Repos'): await r.create(repo_name) - #logging.info(f'Created repository {repo_name} in org {org}.') logging.info(f'Cloning GitGoat and pushing to org {org}.') await r.clone_gitgoat() @@ -54,7 +53,6 @@ async def create_teams(config, org): for gp in repo['group_postfixes']: await t.create(f'{repo["repo"]}-{gp}', [f'{org}/{repo["repo"]}']) await t.add_repository_permission(f'{repo["repo"]}-{gp}', f'{org}/{repo["repo"]}', gp) - #logging.info(f'Created the team {repo["repo"]}-{gp} and added the permission {gp} to repo {org}/{repo["repo"]}') for member in config.members: if (f'{repo["repo"]}-{gp}' in member['member_of_groups']): await t.add_member(f'{repo["repo"]}-{gp}',member['login']) @@ -68,14 +66,12 @@ async def accept_invitations(config, org): for member in tqdm(config.members, desc='Members'): token = member['token'] if 'ghp_' in member['token'] else 'ghp_' + member['token'] await m.accept_invitation_to_org(token) - #logging.info(f'The user {member["login"]} accepted the invitation to join {org}') async def add_direct_permissions(config, org): dp = DirectPermission(org, config.filename) for member in tqdm(config.members, desc='Direct Permission'): if 'gitgoat_repo_permission' in member: await dp.add_repository_permission('GitGoat',member['login'],member['gitgoat_repo_permission']) - #logging.info(f'The permission{member["gitgoat_repo_permission"]} is granted to user {member["login"]} in repo GitGoat in org {org}') async def setup_actions(config, org): a = Actions(org, config.filename) @@ -101,7 +97,7 @@ async def configure_codeowners(config, org): r = Repository(org, config.filename) for repo_name in tqdm(config.repo_names, desc='CODEOWNERS'): repo = await r.clone(repo_name, 'GitGoat', Config.get_pat(), 'GitGoat@gitgoat.tools') - co = CodeOwners(repo_name, repo, config.filename) + co = CodeOwners(org,repo_name, repo, config.filename) filename = await co.generate_file() await co.push_file(filename) @@ -121,29 +117,53 @@ async def create_commits(config, org): r = Repository(org, config.filename) pr = PullRequest(org, config.filename) for member in config.members: + token = member['token'] if 'ghp_' in member['token'] else 'ghp_' + member['token'] for commit_details in tqdm(member['days_since_last_commit'], desc=f'Commits for {member["login"]}'): - token = member['token'] if 'ghp_' in member['token'] else 'ghp_' + member['token'] repo = await r.clone(commit_details['repo'], member['login'], token, member['email'], commit_details['branch']) c = Commit(repo) - c.generate_commits(50, commit_details['days']) + c.generate_commits(25, commit_details['days']) if commit_details['create_pr']: await pr.create_pull_request(token, commit_details['repo'], commit_details['branch']) - #logging.info(f'Created a PR by {member["login"]} from branch {commit_details["branch"]}') async def review_pull_requests(config, org): pr = PullRequest(org, config.filename) - for member in tqdm(config.members, desc=f'Members Reviewing PRs'): + pr_reviews_map = await get_pr_reviews_map(pr, config) + for member in tqdm(config.members, desc=f'Members Review PRs'): + token = member['token'] if 'ghp_' in member['token'] else 'ghp_' + member['token'] for repo in config.repo_names: - token = member['token'] if 'ghp_' in member['token'] else 'ghp_' + member['token'] - if is_member_allowed_to_review(config, member, repo): - prs = await pr.get_pull_requests(token,repo) + member_reviewed_prs_of_login = [] + prs = await pr.get_pull_requests(token,repo) + if is_member_codeowner(config, member, repo): for p in prs: - if prs[p] != member['login']: + if prs[p] != member['login'] and prs[p] not in member_reviewed_prs_of_login and not pr_reviews_map[repo][p]: await pr.review(token, repo, p) + pr_reviews_map[repo][p] = True + member_reviewed_prs_of_login.append(prs[p]) + elif can_members_review(config, repo): + for p in prs: + if prs[p] != member['login'] and not pr_reviews_map[repo][p]: + await pr.review(token, repo, p) + pr_reviews_map[repo][p] = True + member_reviewed_prs_of_login.append(prs[p]) + await review_pull_requests_by_owner(pr_reviews_map, pr) + +async def review_pull_requests_by_owner(pr_reviews_map, pr): + for repo in tqdm(pr_reviews_map, desc=f'Owner Reviews PRs'): + for pr_id in pr_reviews_map[repo]: + if not pr_reviews_map[repo][pr_id]: + await pr.review(Config.get_pat(), repo, pr_id) + +async def get_pr_reviews_map(pr, config): + pr_reviews_map = {} + for repo in config.repo_names: + pr_reviews_map[repo] = {} + for pr_id in await pr.get_pull_requests(Config.get_pat(),repo): + pr_reviews_map[repo][pr_id] = False + return pr_reviews_map async def merge_pull_requests(config, org): pr = PullRequest(org, config.filename) - for member in tqdm(config.members, desc=f'Members Merging PRs'): + for member in tqdm(config.members, desc=f'Members Merge PRs'): for repo in config.repo_names: token = member['token'] if 'ghp_' in member['token'] else 'ghp_' + member['token'] if is_member_allowed_to_merge(config, member, repo): @@ -153,9 +173,12 @@ async def merge_pull_requests(config, org): if not merged: logging.warning(f'Did NOT merge the PR id {p} in repository {repo} by {member["login"]}') -def is_member_allowed_to_review(config, member, repo): +def can_members_review(config, repo): if 'branch_protection_restirctions' in config.repo_configs[repo] and 'require_code_owner_reviews' in config.repo_configs[repo]['branch_protection_restirctions'] and not config.repo_configs[repo]['branch_protection_restirctions']['require_code_owner_reviews']: return True + return False + +def is_member_codeowner(config, member, repo): if 'codeowners' in config.repo_configs[repo] and 'owners' in config.repo_configs[repo]['codeowners']: for owner in config.repo_configs[repo]['codeowners']['owners']: for u in owner['users']: diff --git a/src/codeowners.py b/src/codeowners.py index cec45e3..aad1849 100644 --- a/src/codeowners.py +++ b/src/codeowners.py @@ -7,8 +7,9 @@ class CodeOwners: filename = 'CODEOWNERS' - def __init__(self, repo_name: str, repository: git.Repo, config_file = None): + def __init__(self, organization: str, repo_name: str, repository: git.Repo, config_file = None): self.config = Config() if config_file is None else Config(config_file) + self.org = organization self.repo = repository self.repo_name = repo_name self.repo.git.add(update=True) @@ -35,7 +36,7 @@ async def generate_file(self): for u in owner['users']: rules += f'@{u} ' for t in owner['teams']: - rules += f'@{self.repo_name}-{t} ' + rules += f'@{self.org}/{self.repo_name}-{t} ' rules += '\n' with open(filename, 'w') as f: f.write(rules) diff --git a/src/pull_request.py b/src/pull_request.py index 8d9e97b..6c06dc3 100644 --- a/src/pull_request.py +++ b/src/pull_request.py @@ -15,7 +15,7 @@ async def get_pull_requests(self, pat, repository): resp = await conn.get(endpoint) for pr in resp: if pr['state'] == 'open': - pr_ids[pr['number']] = pr['user']['login'] + pr_ids[str(pr['number'])] = pr['user']['login'] return pr_ids async def create_pull_request(self, pat, repository, head_branch): diff --git a/src/teams.py b/src/teams.py index cf0a903..53a4f86 100644 --- a/src/teams.py +++ b/src/teams.py @@ -13,7 +13,7 @@ async def create(self, name, repo_names = []): data = { 'name': name, 'repo_names': repo_names, - 'privacy': 'secret' + 'privacy': 'closed' } resp = await self.conn.post(self.endpoint, json_data=data) return resp