From 6d9f2cb2dd4bdd9b7c8e3806a5082240e7e87673 Mon Sep 17 00:00:00 2001 From: Mike Crute Date: Tue, 27 Mar 2018 03:59:24 +0000 Subject: Initial import --- main.go | 190 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 190 insertions(+) create mode 100644 main.go diff --git a/main.go b/main.go new file mode 100644 index 0000000..5c962d3 --- /dev/null +++ b/main.go @@ -0,0 +1,190 @@ +package main + +import ( + "context" + "encoding/json" + "fmt" + "github.com/google/go-github/github" + "golang.org/x/crypto/ssh" + "golang.org/x/oauth2" + "io/ioutil" + "log" + "strings" +) + +const ( + ACCESS_TOKEN = "" + KEY_PASSWORD = "" + KEY_PATH = "" + SSH_USERNAME = "" + SSH_HOST = "" + REPO_PREFIX = "mcrute/github-mirror/" + GITHUB_USERNAME = "mcrute" +) + +func GetGitoliteRepos(c *ssh.Client, prefix string) (map[string]string, error) { + repos := make(map[string]string) + + session, err := c.NewSession() + if err != nil { + return nil, err + } + defer session.Close() + + output, err := session.Output(fmt.Sprintf("info -json %s", prefix)) + if err != nil { + return nil, err + } + + var d map[string]interface{} + if err = json.Unmarshal(output, &d); err != nil { + return nil, err + } + + for k, _ := range d["repos"].(map[string]interface{}) { + name := strings.TrimPrefix(k, REPO_PREFIX) + if name == ".*" { + continue + } + repos[name] = k + } + + return repos, nil +} + +func SyncGitoliteRepo(c *ssh.Client, name string) error { + session, err := c.NewSession() + if err != nil { + return err + } + defer session.Close() + + if err = session.Run(fmt.Sprintf("sync-mirror %s", name)); err != nil { + return err + } + + return nil +} + +func CreateGitoliteMirror(c *ssh.Client, name, remote string) error { + session, err := c.NewSession() + if err != nil { + return err + } + defer session.Close() + + if err = session.Run(fmt.Sprintf("create-mirror %s %s", remote, name)); err != nil { + return err + } + + return nil +} + +func SSHConnect(key, password, username, host string) (*ssh.Client, error) { + pem_bytes, err := ioutil.ReadFile(key) + if err != nil { + return nil, err + } + + signer, err := ssh.ParsePrivateKeyWithPassphrase(pem_bytes, []byte(password)) + if err != nil { + return nil, err + } + + client_config := &ssh.ClientConfig{ + User: username, + Auth: []ssh.AuthMethod{ssh.PublicKeys(signer)}, + HostKeyCallback: ssh.InsecureIgnoreHostKey(), + } + + ssh_client, err := ssh.Dial("tcp", host, client_config) + if err != nil { + return nil, err + } + + return ssh_client, nil +} + +func NewGithubClient(token string) *github.Client { + ctx := context.Background() + ts := oauth2.StaticTokenSource( + &oauth2.Token{AccessToken: token}, + ) + tc := oauth2.NewClient(ctx, ts) + return github.NewClient(tc) +} + +type GitHubRepo struct { + Name string + FullName string + IsFork bool + CloneURL string +} + +func ListGithubRepos(c *github.Client, username string) ([]GitHubRepo, error) { + ctx := context.Background() + gh_repos := make([]GitHubRepo, 0) + opts := &github.RepositoryListOptions{} + + for { + repos, resp, err := c.Repositories.List(ctx, username, opts) + if err != nil { + return nil, err + } + + for _, r := range repos { + gh_repos = append(gh_repos, GitHubRepo{ + Name: strings.TrimPrefix(*r.FullName, fmt.Sprintf("%s/", username)), + FullName: *r.FullName, + IsFork: *r.Fork, + CloneURL: *r.CloneURL, + }) + } + + if resp.NextPage == 0 { + break + } + + opts.Page = resp.NextPage + } + + return gh_repos, nil +} + +func main() { + gh_client := NewGithubClient(ACCESS_TOKEN) + + ssh_client, err := SSHConnect(KEY_PATH, KEY_PASSWORD, SSH_USERNAME, SSH_HOST) + if err != nil { + log.Fatal(err) + } + + server_repos, err := GetGitoliteRepos(ssh_client, REPO_PREFIX) + if err != nil { + log.Fatal(err) + } + + gh_repos, err := ListGithubRepos(gh_client, GITHUB_USERNAME) + if err != nil { + log.Fatal(err) + } + + for _, r := range gh_repos { + gitolite_name, have := server_repos[r.Name] + if have { + err = SyncGitoliteRepo(ssh_client, gitolite_name) + if err != nil { + log.Fatal("Failed to sync: %s", r.Name) + } else { + log.Printf("Synced: %s", r.Name) + } + } else if !have && !r.IsFork { + err = CreateGitoliteMirror(ssh_client, fmt.Sprintf("%s%s", REPO_PREFIX, r.Name), r.CloneURL) + if err != nil { + log.Fatal("Failed to mirror: %s", r.Name) + } else { + log.Printf("Mirrored: %s", r.Name) + } + } + } +} -- cgit v1.2.3