diff --git a/repos/__main__.py b/repos/__main__.py new file mode 100644 index 0000000..0364bd7 --- /dev/null +++ b/repos/__main__.py @@ -0,0 +1,196 @@ +#!/usr/bin/env python3 + +import os +from pathlib import Path +import typer +import requests + +app = typer.Typer() + +ignored_folders = [ + '.git', + '.DS_Store', + '.obsidian', +] + + +def getReposFromPage(github_token: str, organization: str, page: int): + # Get all repos from a specific page for an organization from GitHub. + # https://docs.github.com/en/rest/reference/repos#list-organization-repositories + try: + url = f"https://api.github.com/orgs/{organization}/repos" + payload = {} + headers = { + 'Authorization': f'token {github_token}' + } + params = { + 'per_page': '100', + 'page': page, + 'sort': 'full_name' + } + + response = requests.request("GET", url, headers=headers, data=payload, params=params) + + if response.status_code == 200: + return response.json() + else: + raise Exception(f"status code {response.status_code}") + except Exception as e: + print(f"error: {e}") + return None + + +def getRepos(github_token: str, organization: str): + # Get all repos for an organization from GitHub by iterating over all pages. + # https://docs.github.com/en/rest/reference/repos#list-organization-repositories + try: + repos = [] + page = 1 + while True: + repos_page = getReposFromPage(github_token, organization, page) + if repos_page is not None: + repos.extend(repos_page) + if len(repos_page) < 100: + break + else: + page += 1 + else: + raise Exception("repos_page is None") + return repos + except Exception as e: + print(f"error: {e}") + return None + + +@app.command() +def list(github_token: str = typer.Argument("--token", envvar="GITHUB_TOKEN")): + try: + # List all repos for an organization from GitHub + # https://docs.github.com/en/rest/reference/repos#list-organization-repositories + + repos = getRepos(github_token, 'clcreative') + if repos is not None: + for repo in repos: + print(f"{repo['name']}, {repo['owner']['login']}, {repo['description']}") + else: + raise Exception("repos is None") + + # Sum all repos + print(f"---\nTotal: {len(repos)}") + + except Exception as e: + print(f"error: {e}") + + +@app.command() +def check(github_token: str = typer.Argument("--token", envvar="GITHUB_TOKEN"), list_orphaned: bool = False): + try: + # Get all repos for an organization from GitHub + # https://docs.github.com/en/rest/reference/repos#list-organization-repositories + + repos = getRepos(github_token, 'clcreative') + repos_need_changes = [] + repos_not_cloned = [] + + if repos is not None: + for repo in repos: + # set repo path on local workstation + repo_path = f'{ Path.home() }/projects/{repo["owner"]["login"]}/{repo["name"]}' + + # when repo is existing + if os.path.exists(repo_path): + try: + os.chdir(repo_path) + + # check if there are any changes + # changes = os.system('git diff --exit-code --quiet') + + # check if there are any changes to the repo, and if something needs to be pulled or committed + changes = os.system('git diff --exit-code --quiet') + + if changes != 0: + print(f" {repo['name']} - {repo['description']}") + repos_need_changes.append(repo) + else: + print(f" {repo['name']} - {repo['description']}") + + except Exception as e: + print(f"error: {e}") + else: + print(f" {repo['name']} - {repo['description']}") + repos_not_cloned.append(repo) + + # Check orphaned repos + repos_orphaned = [] + + # read all folders in projects folder + for item in os.listdir(f'{ Path.home() }/projects/clcreative'): + # check if item is in repos list, and if item is not in ignored folders + if item not in [repo['name'] for repo in repos] and item not in ignored_folders: + repos_orphaned.append(item) + + # List all orphaned repos + if list_orphaned: + print("---\nOrphaned:") + for repo in repos_orphaned: + print(f" {repo}") + + # Get Information about all Repos + print(f"---\nTotal: {len(repos)}, Need changes: {len(repos_need_changes)}, Not cloned: {len(repos_not_cloned)}, Orphaned: {len(repos_orphaned)}") + + except Exception as e: + print(f"error: {e}") + + +@app.command() +def clone(github_token: str = typer.Argument("--token", envvar="GITHUB_TOKEN")): + # Clone all missing repos for an organization from GitHub + + try: + repos = getRepos(github_token, 'clcreative') + if repos is not None: + for repo in repos: + # set repo path on local workstation + repo_path = f'{ Path.home() }/projects/{repo["owner"]["login"]}/{repo["name"]}' + + # when repo is not existing + if not os.path.exists(repo_path): + print(f" {repo['name']} - {repo['description']}") + os.system(f'git clone git@github.com:{repo["owner"]["login"]}/{repo["name"]}.git {repo_path}') + + except Exception as e: + print(f"error: {e}") + + +@app.command() +def clear(github_token: str = typer.Argument("--token", envvar="GITHUB_TOKEN")): + # Clear all orphaned repos from workstation + + repos_orphaned = [] + + try: + repos = getRepos(github_token, 'clcreative') + if repos is not None: + # read all folders in projects folder + for item in os.listdir(f'{ Path.home() }/projects/clcreative'): + # check if item is in repos list, and if item is not in ignored folders + if item not in [repo['name'] for repo in repos] and item not in ignored_folders: + repos_orphaned.append(item) + + # ask for confirmation + if len(repos_orphaned) > 0: + print("---\nOrphaned:") + for repo in repos_orphaned: + print(f" {repo}") + print(f"---\nTotal: {len(repos_orphaned)}") + if typer.confirm("Do you want to delete all orphaned repos?"): + for repo in repos_orphaned: + # delete folder using rm + os.system(f'rm -rf { Path.home() }/projects/clcreative/{repo}') + + except Exception as e: + print(f"error: {e}") + + +if __name__ == "__main__": + app() diff --git a/repos/__pycache__/__main__.cpython-311.pyc b/repos/__pycache__/__main__.cpython-311.pyc new file mode 100644 index 0000000..ec6609e Binary files /dev/null and b/repos/__pycache__/__main__.cpython-311.pyc differ diff --git a/repos/requirements.txt b/repos/requirements.txt new file mode 100644 index 0000000..a1f5a53 --- /dev/null +++ b/repos/requirements.txt @@ -0,0 +1,2 @@ +typer==0.7.0 +requests==2.28.2 \ No newline at end of file