blob: ec1d4d106f0d77d1cb6ba71fa04bec571b576bbb [file] [log] [blame]
// Copyright 2016 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// This binary fetches all repos of a user or organization and clones
// them. It is strongly recommended to get a personal API token from
// https://github.com/settings/tokens, save the token in a file, and
// point the --token option to it.
package main
import (
"flag"
"io/ioutil"
"log"
"os"
"path/filepath"
"strings"
"github.com/google/go-github/github"
"golang.org/x/oauth2"
"github.com/google/zoekt/gitindex"
)
func main() {
dest := flag.String("dest", "", "destination directory")
org := flag.String("org", "", "organization to mirror")
user := flag.String("user", "", "user to mirror")
token := flag.String("token",
filepath.Join(os.Getenv("HOME"), ".github-token"),
"file holding API token.")
forks := flag.Bool("forks", false, "also mirror forks.")
namePattern := flag.String("name", "", "only clone repos whose name matches the given regexp.")
excludePattern := flag.String("exclude", "", "don't mirror repos whose names match this regexp.")
flag.Parse()
if *dest == "" {
log.Fatal("must set --dest")
}
if *org == "" && *user == "" {
log.Fatal("must set --org or --user")
}
destDir := filepath.Join(*dest, "github.com")
if err := os.MkdirAll(destDir, 0755); err != nil {
log.Fatal(err)
}
client := github.NewClient(nil)
if *token != "" {
content, err := ioutil.ReadFile(*token)
if err != nil {
log.Fatal(err)
}
ts := oauth2.StaticTokenSource(
&oauth2.Token{
AccessToken: strings.TrimSpace(string(content)),
})
tc := oauth2.NewClient(oauth2.NoContext, ts)
client = github.NewClient(tc)
}
var repos []*github.Repository
var err error
if *org != "" {
repos, err = getOrgRepos(client, *org)
} else if *user != "" {
repos, err = getUserRepos(client, *user)
}
if err != nil {
log.Fatal(err)
}
if !*forks {
trimmed := repos[:0]
for _, r := range repos {
if r.Fork == nil || !*r.Fork {
trimmed = append(trimmed, r)
}
}
repos = trimmed
}
filter, err := gitindex.NewFilter(*namePattern, *excludePattern)
if err != nil {
log.Fatal(err)
}
{
trimmed := repos[:0]
for _, r := range repos {
if filter.Include(*r.Name) {
trimmed = append(trimmed, r)
}
}
repos = trimmed
}
if err := cloneRepos(destDir, repos); err != nil {
log.Fatal(err)
}
}
func getOrgRepos(client *github.Client, org string) ([]*github.Repository, error) {
var allRepos []*github.Repository
opt := &github.RepositoryListByOrgOptions{}
for {
repos, resp, err := client.Repositories.ListByOrg(org, opt)
if err != nil {
return nil, err
}
if len(repos) == 0 {
break
}
var names []string
for _, r := range repos {
names = append(names, *r.Name)
}
log.Println(strings.Join(names, " "))
opt.Page = resp.NextPage
allRepos = append(allRepos, repos...)
if resp.NextPage == 0 {
break
}
}
return allRepos, nil
}
func getUserRepos(client *github.Client, user string) ([]*github.Repository, error) {
var allRepos []*github.Repository
opt := &github.RepositoryListOptions{}
for {
repos, resp, err := client.Repositories.List(user, opt)
if err != nil {
return nil, err
}
if len(repos) == 0 {
break
}
var names []string
for _, r := range repos {
names = append(names, *r.Name)
}
log.Println(strings.Join(names, " "))
opt.Page = resp.NextPage
allRepos = append(allRepos, repos...)
if resp.NextPage == 0 {
break
}
}
return allRepos, nil
}
func cloneRepos(destDir string, repos []*github.Repository) error {
for _, r := range repos {
config := map[string]string{
"zoekt.web-url-type": "github",
"zoekt.web-url": *r.HTMLURL,
"zoekt.name": filepath.Join("github.com", *r.FullName),
// TODO - insert stars/forks for repo ranking too.
}
if err := gitindex.CloneRepo(destDir, *r.FullName, *r.CloneURL, config); err != nil {
return err
}
}
return nil
}