Skip to content

Instantly share code, notes, and snippets.

@jarulsamy
Created October 11, 2022 16:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jarulsamy/00873eb1f60e452d3b91e11d2ed7db6b to your computer and use it in GitHub Desktop.
Save jarulsamy/00873eb1f60e452d3b91e11d2ed7db6b to your computer and use it in GitHub Desktop.
#!/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