#!/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()