all repos — reposync @ edbfd68c600720cf72d8889209af960411c5c1ba

Go program that clones all of my public repos from GitHub to my git server.

main.go (view raw)

  1package main
  2
  3import (
  4	"encoding/json"
  5	"fmt"
  6	"io"
  7	"log"
  8	"net/http"
  9	"os"
 10	"os/exec"
 11	"path/filepath"
 12
 13	"github.com/joho/godotenv"
 14)
 15
 16type Repo struct {
 17	Name        string `json:"name"`
 18	HTMLUrl     string `json:"html_url"`
 19	Description string `json:"description"`
 20}
 21
 22func getRepos(username, token string) ([]Repo, error) {
 23	url := fmt.Sprintf("https://api.github.com/users/%s/repos", username)
 24	req, err := http.NewRequest("GET", url, nil)
 25	if err != nil {
 26		return nil, err
 27	}
 28	req.SetBasicAuth(username, token)
 29
 30	client := &http.Client{}
 31	resp, err := client.Do(req)
 32	if err != nil {
 33		return nil, err
 34	}
 35	defer resp.Body.Close()
 36
 37	if resp.StatusCode != http.StatusOK {
 38		return nil, fmt.Errorf("failed to fetch repositories: %s", resp.Status)
 39	}
 40
 41	body, err := io.ReadAll(resp.Body)
 42	if err != nil {
 43		return nil, err
 44	}
 45
 46	var repos []Repo
 47	if err := json.Unmarshal(body, &repos); err != nil {
 48		return nil, err
 49	}
 50
 51	return repos, nil
 52}
 53
 54func runGitCommand(args ...string) error {
 55	cmd := exec.Command("git", args...)
 56	cmd.Stdout = os.Stdout
 57	cmd.Stderr = os.Stderr
 58	return cmd.Run()
 59}
 60
 61func writeDescription(repoPath, description string) error {
 62	descriptionPath := filepath.Join(repoPath, "description")
 63	return os.WriteFile(descriptionPath, []byte(description), 0644)
 64}
 65
 66func syncRepo(repo Repo, targetDir string) error {
 67	repoPath := filepath.Join(targetDir, repo.Name+".git")
 68	if _, err := os.Stat(repoPath); os.IsNotExist(err) {
 69		fmt.Printf("Cloning repository %s as bare...\n", repo.Name)
 70		if err := runGitCommand("clone", "--bare", repo.HTMLUrl, repoPath); err != nil {
 71			return err
 72		}
 73	} else {
 74		fmt.Printf("Pulling updates for repository %s...\n", repo.Name)
 75		cmd := exec.Command("git", "-C", repoPath, "pull")
 76		cmd.Stdout = os.Stdout
 77		cmd.Stderr = os.Stderr
 78		if err := cmd.Run(); err != nil {
 79			return err
 80		}
 81	}
 82
 83	// Write the repository description
 84	if repo.Description != "" {
 85		if err := writeDescription(repoPath, repo.Description); err != nil {
 86			return fmt.Errorf("failed to write description for %s: %v", repo.Name, err)
 87		}
 88	}
 89
 90	return nil
 91}
 92
 93func main() {
 94	err := godotenv.Load()
 95	if err != nil {
 96		println("Failed to load .env file: %v\n", err)
 97	}
 98
 99	ghUser := os.Getenv("GITHUB_USERNAME")
100	ghToken := os.Getenv("GITHUB_TOKEN")
101	repoDir := os.Getenv("REPO_DIR")
102
103	if ghUser == "" || ghToken == "" || repoDir == "" {
104		log.Fatalf("GitHub username and token must be provided\n")
105	}
106
107	repos, err := getRepos(ghUser, ghToken)
108	if err != nil {
109		log.Fatalf("Failed to fetch repositories: %v\n", err)
110	}
111
112	for _, repo := range repos {
113		if err := syncRepo(repo, repoDir); err != nil {
114			log.Printf("Failed to sync repository %s: %v\n", repo.Name, err)
115		}
116	}
117}