|
| 1 | +#!/usr/bin/env python3 |
| 2 | + |
| 3 | +"""This script is used to switch out one team for another across all the |
| 4 | +repositories in a given Github organisation. |
| 5 | +
|
| 6 | +Note: Only the source team's permissions are migrated (members are not added |
| 7 | +or removed) and the destination team must already exist |
| 8 | +""" |
| 9 | + |
| 10 | +import argparse |
| 11 | +import sys |
| 12 | +from github import Github |
| 13 | +from os import environ |
| 14 | + |
| 15 | + |
| 16 | +class GitHubTeamMover(): |
| 17 | + def __init__(self, org, dry_run): |
| 18 | + self.connect() |
| 19 | + self.get_org(org) |
| 20 | + self.dry_run = dry_run |
| 21 | + |
| 22 | + def connect(self): |
| 23 | + try: |
| 24 | + self.g = Github(environ["GITHUB_TOKEN"]) |
| 25 | + except KeyError: |
| 26 | + print("ERROR: Ensure GITHUB_TOKEN is present in environment") |
| 27 | + exit(1) |
| 28 | + |
| 29 | + def get_org(self, org): |
| 30 | + self.org = self.g.get_organization(org) |
| 31 | + |
| 32 | + def get_repos_team_present_in(self, team_slug): |
| 33 | + repos = [] |
| 34 | + for repo in self.org.get_repos(): |
| 35 | + if(team_slug in [t.slug for t in repo.get_teams()]): |
| 36 | + repos.append(repo) |
| 37 | + return repos |
| 38 | + |
| 39 | + def remove_from_repo(self, team_slug, repo): |
| 40 | + team = self.org.get_team_by_slug(team_slug) |
| 41 | + print("Removing", team.slug, "from", repo.name, "...", end=" ") |
| 42 | + if not self.dry_run: |
| 43 | + team.remove_from_repos(repo) |
| 44 | + print("ok!") |
| 45 | + else: |
| 46 | + print("no action (dry run)") |
| 47 | + |
| 48 | + def add_to_repo(self, team_slug, repo, role): |
| 49 | + team = self.org.get_team_by_slug(team_slug) |
| 50 | + print("Adding", team.slug, "to", repo.name, |
| 51 | + f"({role} access) ...", end=" ") |
| 52 | + if not self.dry_run: |
| 53 | + team.add_to_repos(repo) |
| 54 | + self.set_repo_role(team, repo, role) |
| 55 | + print("ok!") |
| 56 | + else: |
| 57 | + print("no action (dry run)") |
| 58 | + |
| 59 | + def get_repo_role(self, team_slug, repo): |
| 60 | + team = self.org.get_team_by_slug(team_slug) |
| 61 | + permission = team.get_repo_permission(repo) |
| 62 | + if permission.admin: |
| 63 | + role = "admin" |
| 64 | + elif permission.maintain: |
| 65 | + role = "maintain" |
| 66 | + elif permission.push: |
| 67 | + role = "push" # this is called 'write' in UI |
| 68 | + elif permission.triage: |
| 69 | + role = "triage" |
| 70 | + elif permission.pull: |
| 71 | + role = "read" |
| 72 | + else: |
| 73 | + print( |
| 74 | + f"Couldn't determine permission for {team_slug} on ", |
| 75 | + repo.full_name) |
| 76 | + sys.exit(1) |
| 77 | + return role |
| 78 | + |
| 79 | + def set_repo_role(self, team, repo, role): |
| 80 | + return team.update_team_repository(repo, role) |
| 81 | + |
| 82 | + def switch_teams(self, from_team, to_team): |
| 83 | + repos_present_in = self.get_repos_team_present_in(from_team) |
| 84 | + for repo in repos_present_in: |
| 85 | + role = self.get_repo_role(from_team, repo.full_name) |
| 86 | + self.add_to_repo(to_team, repo, role) |
| 87 | + self.remove_from_repo(from_team, repo) |
| 88 | + |
| 89 | + |
| 90 | +parser = argparse.ArgumentParser(description=__doc__, |
| 91 | + formatter_class=argparse.RawTextHelpFormatter) |
| 92 | +parser.add_argument('--org', action='store', required=True) |
| 93 | +parser.add_argument('--from-team-slug', action='store', required=True) |
| 94 | +parser.add_argument('--to-team-slug', action='store', required=True) |
| 95 | +parser.add_argument('--dry-run', action='store_true') |
| 96 | +args = parser.parse_args() |
| 97 | + |
| 98 | + |
| 99 | +team = GitHubTeamMover(args.org, args.dry_run) |
| 100 | +team.switch_teams(args.from_team_slug, args.to_team_slug) |
0 commit comments