dotfiles/scripts/getgit.py
2025-02-08 20:13:56 -06:00

121 lines
3.9 KiB
Python

#!/usr/bin/env python3
"""
getgit.py - Pull or clone all of your Gitea user repositories.
Usage:
./getgit.py --server https://gitea.example.com --token YOUR_API_TOKEN
or, after setting the environment variable GITEA_TOKEN:
export GITEA_TOKEN=YOUR_API_TOKEN
./getgit.py --server https://gitea.example.com
By default the repositories will be placed under ~/repos.
If a repository directory already exists, a 'git pull' will be run;
otherwise, the repository will be cloned using its SSH URL.
"""
import os
import sys
import argparse
import subprocess
import requests
def get_repos(server, token, per_page=100):
"""Fetch all repositories for the authenticated user from Gitea."""
repos = []
page = 1
headers = {
"Authorization": f"token {token}",
"Accept": "application/json"
}
while True:
url = f"{server.rstrip('/')}/api/v1/user/repos?page={page}&limit={per_page}"
response = requests.get(url, headers=headers)
if response.status_code != 200:
print(f"Error fetching repositories (HTTP {response.status_code}): {response.text}")
sys.exit(1)
data = response.json()
if not data:
break
repos.extend(data)
if len(data) < per_page:
break
page += 1
return repos
def clone_or_pull_repo(repo, dest_dir):
"""
Given a repository object (as returned from the Gitea API), check if it exists locally.
If it exists, run 'git pull'; if not, run 'git clone' using the SSH URL.
"""
repo_name = repo.get('name')
ssh_url = repo.get('ssh_url')
if not repo_name or not ssh_url:
print(f"Skipping a repo entry with missing information: {repo}")
return
repo_path = os.path.join(dest_dir, repo_name)
if os.path.exists(repo_path):
print(f"Updating repository '{repo_name}'...")
try:
subprocess.run(["git", "-C", repo_path, "pull"], check=True)
except subprocess.CalledProcessError:
print(f"Warning: Failed to update repository '{repo_name}'.")
else:
print(f"Cloning repository '{repo_name}'...")
try:
subprocess.run(["git", "clone", ssh_url, repo_path], check=True)
except subprocess.CalledProcessError:
print(f"Warning: Failed to clone repository '{repo_name}'.")
def main():
parser = argparse.ArgumentParser(
description="Clone or update all Gitea user repositories."
)
parser.add_argument(
"--server",
type=str,
default="https://gitea.example.com",
help="Base URL of your Gitea server (e.g. https://gitea.example.com)"
)
parser.add_argument(
"--dest",
type=str,
default=os.path.expanduser("~/repos"),
help="Local destination directory to clone repositories (default: ~/repos)"
)
parser.add_argument(
"--token",
type=str,
help="Your Gitea API token. Alternatively, set the GITEA_TOKEN environment variable."
)
parser.add_argument(
"--per-page",
type=int,
default=100,
help="Number of repositories to fetch per API page (default: 100)"
)
args = parser.parse_args()
# Retrieve the API token from argument or environment variable.
token = args.token or os.environ.get("GITEA_TOKEN")
if not token:
print("Error: No API token provided. Supply it via --token or set GITEA_TOKEN.")
sys.exit(1)
# Ensure the destination directory exists.
dest_dir = os.path.expanduser(args.dest)
os.makedirs(dest_dir, exist_ok=True)
print("Fetching repository list from Gitea...")
repos = get_repos(args.server, token, args.per_page)
if not repos:
print("No repositories found.")
sys.exit(0)
print(f"Found {len(repos)} repositories. Processing each one...")
for repo in repos:
clone_or_pull_repo(repo, dest_dir)
if __name__ == "__main__":
main()