diff options
author | Mike Crute <mike@crute.us> | 2018-03-27 03:59:24 +0000 |
---|---|---|
committer | Mike Crute <mike@crute.us> | 2018-03-27 03:59:24 +0000 |
commit | 6d9f2cb2dd4bdd9b7c8e3806a5082240e7e87673 (patch) | |
tree | d41182b6245c4ef7356a533382d8cf694ae0edb3 | |
download | github_mirror-6d9f2cb2dd4bdd9b7c8e3806a5082240e7e87673.tar.bz2 github_mirror-6d9f2cb2dd4bdd9b7c8e3806a5082240e7e87673.tar.xz github_mirror-6d9f2cb2dd4bdd9b7c8e3806a5082240e7e87673.zip |
Initial import
-rw-r--r-- | main.go | 190 |
1 files changed, 190 insertions, 0 deletions
@@ -0,0 +1,190 @@ | |||
1 | package main | ||
2 | |||
3 | import ( | ||
4 | "context" | ||
5 | "encoding/json" | ||
6 | "fmt" | ||
7 | "github.com/google/go-github/github" | ||
8 | "golang.org/x/crypto/ssh" | ||
9 | "golang.org/x/oauth2" | ||
10 | "io/ioutil" | ||
11 | "log" | ||
12 | "strings" | ||
13 | ) | ||
14 | |||
15 | const ( | ||
16 | ACCESS_TOKEN = "" | ||
17 | KEY_PASSWORD = "" | ||
18 | KEY_PATH = "" | ||
19 | SSH_USERNAME = "" | ||
20 | SSH_HOST = "" | ||
21 | REPO_PREFIX = "mcrute/github-mirror/" | ||
22 | GITHUB_USERNAME = "mcrute" | ||
23 | ) | ||
24 | |||
25 | func GetGitoliteRepos(c *ssh.Client, prefix string) (map[string]string, error) { | ||
26 | repos := make(map[string]string) | ||
27 | |||
28 | session, err := c.NewSession() | ||
29 | if err != nil { | ||
30 | return nil, err | ||
31 | } | ||
32 | defer session.Close() | ||
33 | |||
34 | output, err := session.Output(fmt.Sprintf("info -json %s", prefix)) | ||
35 | if err != nil { | ||
36 | return nil, err | ||
37 | } | ||
38 | |||
39 | var d map[string]interface{} | ||
40 | if err = json.Unmarshal(output, &d); err != nil { | ||
41 | return nil, err | ||
42 | } | ||
43 | |||
44 | for k, _ := range d["repos"].(map[string]interface{}) { | ||
45 | name := strings.TrimPrefix(k, REPO_PREFIX) | ||
46 | if name == ".*" { | ||
47 | continue | ||
48 | } | ||
49 | repos[name] = k | ||
50 | } | ||
51 | |||
52 | return repos, nil | ||
53 | } | ||
54 | |||
55 | func SyncGitoliteRepo(c *ssh.Client, name string) error { | ||
56 | session, err := c.NewSession() | ||
57 | if err != nil { | ||
58 | return err | ||
59 | } | ||
60 | defer session.Close() | ||
61 | |||
62 | if err = session.Run(fmt.Sprintf("sync-mirror %s", name)); err != nil { | ||
63 | return err | ||
64 | } | ||
65 | |||
66 | return nil | ||
67 | } | ||
68 | |||
69 | func CreateGitoliteMirror(c *ssh.Client, name, remote string) error { | ||
70 | session, err := c.NewSession() | ||
71 | if err != nil { | ||
72 | return err | ||
73 | } | ||
74 | defer session.Close() | ||
75 | |||
76 | if err = session.Run(fmt.Sprintf("create-mirror %s %s", remote, name)); err != nil { | ||
77 | return err | ||
78 | } | ||
79 | |||
80 | return nil | ||
81 | } | ||
82 | |||
83 | func SSHConnect(key, password, username, host string) (*ssh.Client, error) { | ||
84 | pem_bytes, err := ioutil.ReadFile(key) | ||
85 | if err != nil { | ||
86 | return nil, err | ||
87 | } | ||
88 | |||
89 | signer, err := ssh.ParsePrivateKeyWithPassphrase(pem_bytes, []byte(password)) | ||
90 | if err != nil { | ||
91 | return nil, err | ||
92 | } | ||
93 | |||
94 | client_config := &ssh.ClientConfig{ | ||
95 | User: username, | ||
96 | Auth: []ssh.AuthMethod{ssh.PublicKeys(signer)}, | ||
97 | HostKeyCallback: ssh.InsecureIgnoreHostKey(), | ||
98 | } | ||
99 | |||
100 | ssh_client, err := ssh.Dial("tcp", host, client_config) | ||
101 | if err != nil { | ||
102 | return nil, err | ||
103 | } | ||
104 | |||
105 | return ssh_client, nil | ||
106 | } | ||
107 | |||
108 | func NewGithubClient(token string) *github.Client { | ||
109 | ctx := context.Background() | ||
110 | ts := oauth2.StaticTokenSource( | ||
111 | &oauth2.Token{AccessToken: token}, | ||
112 | ) | ||
113 | tc := oauth2.NewClient(ctx, ts) | ||
114 | return github.NewClient(tc) | ||
115 | } | ||
116 | |||
117 | type GitHubRepo struct { | ||
118 | Name string | ||
119 | FullName string | ||
120 | IsFork bool | ||
121 | CloneURL string | ||
122 | } | ||
123 | |||
124 | func ListGithubRepos(c *github.Client, username string) ([]GitHubRepo, error) { | ||
125 | ctx := context.Background() | ||
126 | gh_repos := make([]GitHubRepo, 0) | ||
127 | opts := &github.RepositoryListOptions{} | ||
128 | |||
129 | for { | ||
130 | repos, resp, err := c.Repositories.List(ctx, username, opts) | ||
131 | if err != nil { | ||
132 | return nil, err | ||
133 | } | ||
134 | |||
135 | for _, r := range repos { | ||
136 | gh_repos = append(gh_repos, GitHubRepo{ | ||
137 | Name: strings.TrimPrefix(*r.FullName, fmt.Sprintf("%s/", username)), | ||
138 | FullName: *r.FullName, | ||
139 | IsFork: *r.Fork, | ||
140 | CloneURL: *r.CloneURL, | ||
141 | }) | ||
142 | } | ||
143 | |||
144 | if resp.NextPage == 0 { | ||
145 | break | ||
146 | } | ||
147 | |||
148 | opts.Page = resp.NextPage | ||
149 | } | ||
150 | |||
151 | return gh_repos, nil | ||
152 | } | ||
153 | |||
154 | func main() { | ||
155 | gh_client := NewGithubClient(ACCESS_TOKEN) | ||
156 | |||
157 | ssh_client, err := SSHConnect(KEY_PATH, KEY_PASSWORD, SSH_USERNAME, SSH_HOST) | ||
158 | if err != nil { | ||
159 | log.Fatal(err) | ||
160 | } | ||
161 | |||
162 | server_repos, err := GetGitoliteRepos(ssh_client, REPO_PREFIX) | ||
163 | if err != nil { | ||
164 | log.Fatal(err) | ||
165 | } | ||
166 | |||
167 | gh_repos, err := ListGithubRepos(gh_client, GITHUB_USERNAME) | ||
168 | if err != nil { | ||
169 | log.Fatal(err) | ||
170 | } | ||
171 | |||
172 | for _, r := range gh_repos { | ||
173 | gitolite_name, have := server_repos[r.Name] | ||
174 | if have { | ||
175 | err = SyncGitoliteRepo(ssh_client, gitolite_name) | ||
176 | if err != nil { | ||
177 | log.Fatal("Failed to sync: %s", r.Name) | ||
178 | } else { | ||
179 | log.Printf("Synced: %s", r.Name) | ||
180 | } | ||
181 | } else if !have && !r.IsFork { | ||
182 | err = CreateGitoliteMirror(ssh_client, fmt.Sprintf("%s%s", REPO_PREFIX, r.Name), r.CloneURL) | ||
183 | if err != nil { | ||
184 | log.Fatal("Failed to mirror: %s", r.Name) | ||
185 | } else { | ||
186 | log.Printf("Mirrored: %s", r.Name) | ||
187 | } | ||
188 | } | ||
189 | } | ||
190 | } | ||