Created
October 11, 2022 16:28
-
-
Save jarulsamy/00873eb1f60e452d3b91e11d2ed7db6b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
"""Get all the Github repositories for the currently authenticated gh-cli user.""" | |
import json | |
import os | |
import subprocess | |
from pathlib import Path | |
from typing import Union | |
import requests | |
import yaml | |
class cd: | |
"""Context to make switching directories safer.""" | |
def __init__(self, new_path: Union[str, Path]): | |
"""Make switching directories safer.""" | |
self.new_path = Path(new_path) | |
def __enter__(self): | |
"""Change directory to some other directory, saving where we came from.""" | |
self.saved_path = Path.cwd() | |
os.chdir(self.new_path) | |
def __exit__(self, exc_type, exc_val, traceback): | |
"""Return to directory prior to this context.""" | |
os.chdir(self.saved_path) | |
def get_gh_oauth() -> dict[str, str]: | |
""" | |
Steal oauth login information from the github-cli config file. | |
:returns: { | |
"github.com": | |
{ | |
"user": <AUTHED_USER>, | |
"oauth_token": <OAUTH_TOKEN>, | |
"git_protocol": <PROTOCOL>, | |
} | |
} | |
""" | |
config_path = Path.home() / ".config" / "gh" / "hosts.yml" | |
with open(config_path, "r") as fp: | |
data = yaml.load(fp, yaml.Loader) | |
return data["github.com"] | |
def get_gh_repos(oauth_token) -> list[dict]: | |
""" | |
Get a list of all the repositories for an authenticated Github user. | |
Relevant Github API documentation here: | |
https://docs.github.com/en/rest/repos/repos#list-repositories-for-the-authenticated-user | |
:param oauth_token: Token to login to Github with. | |
:returns: List of all repository information. | |
""" | |
repo_api_url = "https://api.github.com/user/repos" | |
params = {"per_page": 100, "page": 1} | |
headers = { | |
"Accept": "application/vnd.github+json", | |
"Authorization": f"Bearer {oauth_token}", | |
} | |
result = [] | |
last_resp_len = params["per_page"] + 1 | |
while last_resp_len >= params["per_page"]: | |
r = requests.get(repo_api_url, params=params, headers=headers) | |
payload = r.json() | |
result.extend(payload) | |
last_resp_len = len(payload) | |
params["page"] += 1 | |
return result | |
def git(*args): | |
return subprocess.run(["git", *args]) | |
def archive_repo(repo: dict, base_dir: Path = Path(".")) -> None: | |
name = repo["name"] | |
ssh_url = repo["ssh_url"] | |
owner = repo["owner"]["login"] | |
parent_dir = Path(base_dir, owner) | |
repo_dest = parent_dir / name | |
repo_bundle_dest = (parent_dir / (repo_dest.name + ".bundle")).absolute() | |
if repo_dest.exists(): | |
with cd(repo_dest): | |
git("remote", "update") | |
else: | |
git("clone", "--mirror", str(ssh_url), str(repo_dest)) | |
with cd(repo_dest): | |
git("bundle", "create", str(repo_bundle_dest.absolute()), "--all") | |
def archive_repos(repos: list[dict], base_dir: Path = Path("./backups")) -> None: | |
for i in repos: | |
archive_repo(i, base_dir) | |
if __name__ == "__main__": | |
manifest_file = Path("repo_list.json") | |
if manifest_file.exists(): | |
with open(manifest_file, "r") as fp: | |
manifest = json.load(fp) | |
else: | |
gh_oauth = get_gh_oauth() | |
manifest = get_gh_repos(gh_oauth["oauth_token"]) | |
archive_repos(manifest) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment