diff --git a/bitbucket/bitbucket.go b/bitbucket/bitbucket.go new file mode 100644 index 0000000..13e7340 --- /dev/null +++ b/bitbucket/bitbucket.go @@ -0,0 +1,41 @@ +package bitbucket + +import ( + "gickup/types" + "net/url" + + "github.com/ktrysmt/go-bitbucket" + "github.com/rs/zerolog/log" +) + +func Get(conf *types.Conf) []types.Repo { + repos := []types.Repo{} + for _, repo := range conf.Source.BitBucket { + client := bitbucket.NewBasicAuth(repo.Username, repo.Password) + if repo.Url == "" { + repo.Url = bitbucket.DEFAULT_BITBUCKET_API_BASE_URL + } else { + bitbucketUrl, err := url.Parse(repo.Url) + if err != nil { + log.Panic().Str("stage", "bitbucket").Str("url", repo.Url).Msg(err.Error()) + } + client.SetApiBaseURL(*bitbucketUrl) + } + log.Info().Str("stage", "bitbucket").Str("url", repo.Url).Msgf("grabbing repositories from %s", repo.User) + + repositories, err := client.Repositories.ListForAccount(&bitbucket.RepositoriesOptions{Owner: repo.User}) + if err != nil { + log.Panic().Str("stage", "bitbucket").Str("url", repo.Url).Msg(err.Error()) + } + + exclude := types.GetExcludedMap(repo.Exclude) + + for _, r := range repositories.Items { + if exclude[r.Name] { + continue + } + repos = append(repos, types.Repo{Name: r.Name, Url: r.Links["clone"].([]interface{})[0].(map[string]interface{})["href"].(string), SshUrl: r.Links["clone"].([]interface{})[1].(map[string]interface{})["href"].(string), Token: "", Defaultbranch: r.Mainbranch.Name, Origin: repo}) + } + } + return repos +} diff --git a/gitea/gitea.go b/gitea/gitea.go new file mode 100644 index 0000000..ddc16c7 --- /dev/null +++ b/gitea/gitea.go @@ -0,0 +1,90 @@ +package gitea + +import ( + "gickup/types" + + "code.gitea.io/sdk/gitea" + "github.com/rs/zerolog/log" +) + +func Backup(r types.Repo, d types.GenRepo, dry bool) { + if d.Url == "" { + d.Url = "https://gitea.com/" + } + log.Info().Str("stage", "gitea").Str("url", d.Url).Msgf("mirroring %s to %s", types.Blue(r.Name), d.Url) + giteaclient, err := gitea.NewClient(d.Url) + if err != nil { + log.Panic().Str("stage", "gitea").Str("url", d.Url).Msg(err.Error()) + } + giteaclient.SetBasicAuth(d.Token, "") + user, _, err := giteaclient.GetMyUserInfo() + if err != nil { + log.Panic().Str("stage", "gitea").Str("url", d.Url).Msg(err.Error()) + } + if !dry { + repo, _, err := giteaclient.GetRepo(user.UserName, r.Name) + if err != nil { + opts := gitea.MigrateRepoOption{RepoName: r.Name, RepoOwner: user.UserName, Mirror: true, CloneAddr: r.Url, AuthToken: r.Token} + if r.Token == "" { + opts = gitea.MigrateRepoOption{RepoName: r.Name, RepoOwner: user.UserName, Mirror: true, CloneAddr: r.Url, AuthUsername: r.Origin.User, AuthPassword: r.Origin.Password} + } + _, _, err := giteaclient.MigrateRepo(opts) + if err != nil { + log.Panic().Str("stage", "gitea").Str("url", d.Url).Msg(err.Error()) + } + log.Info().Str("stage", "gitea").Str("url", d.Url).Msgf("mirrored %s to %s", types.Blue(r.Name), d.Url) + } else { + if repo.Mirror { + log.Info().Str("stage", "gitea").Str("url", d.Url).Msgf("mirror of %s already exists, syncing instead", types.Blue(r.Name)) + _, err := giteaclient.MirrorSync(user.UserName, repo.Name) + if err != nil { + log.Panic().Str("stage", "gitea").Str("url", d.Url).Msg(err.Error()) + } + log.Info().Str("stage", "gitea").Str("url", d.Url).Msgf("successfully synced %s.", types.Blue(r.Name)) + } + } + } +} + +func Get(conf *types.Conf) []types.Repo { + repos := []types.Repo{} + for _, repo := range conf.Source.Gitea { + if repo.Url == "" { + repo.Url = "https://gitea.com" + } + log.Info().Str("stage", "gitea").Str("url", repo.Url).Msgf("grabbing repositories from %s", repo.User) + opt := gitea.ListReposOptions{} + opt.PageSize = 50 + i := 0 + gitearepos := []*gitea.Repository{} + for { + opt.Page = i + client, err := gitea.NewClient(repo.Url) + if err != nil { + log.Panic().Str("stage", "gitea").Str("url", repo.Url).Msg(err.Error()) + } + if repo.Token != "" { + client.SetBasicAuth(repo.Token, "") + } + repos, _, err := client.ListUserRepos(repo.User, opt) + if err != nil { + log.Panic().Str("stage", "gitea").Str("url", repo.Url).Msg(err.Error()) + } + if len(repos) == 0 { + break + } + gitearepos = append(gitearepos, repos...) + i++ + } + + exclude := types.GetExcludedMap(repo.Exclude) + + for _, r := range gitearepos { + if exclude[r.Name] { + continue + } + repos = append(repos, types.Repo{Name: r.Name, Url: r.CloneURL, SshUrl: r.SSHURL, Token: repo.Token, Defaultbranch: r.DefaultBranch, Origin: repo}) + } + } + return repos +} diff --git a/github/github.go b/github/github.go new file mode 100644 index 0000000..0d4a165 --- /dev/null +++ b/github/github.go @@ -0,0 +1,57 @@ +package github + +import ( + "context" + "gickup/types" + + "github.com/google/go-github/v41/github" + "github.com/rs/zerolog/log" + "golang.org/x/oauth2" +) + +func Get(conf *types.Conf) []types.Repo { + repos := []types.Repo{} + for _, repo := range conf.Source.Github { + log.Info().Str("stage", "github").Str("url", "https://github.com").Msgf("grabbing the repositories from %s", repo.User) + client := &github.Client{} + opt := &github.RepositoryListOptions{ListOptions: github.ListOptions{PerPage: 50}} + i := 1 + githubrepos := []*github.Repository{} + if repo.Token == "" { + client = github.NewClient(nil) + } else { + ts := oauth2.StaticTokenSource( + &oauth2.Token{AccessToken: repo.Token}, + ) + tc := oauth2.NewClient(context.TODO(), ts) + client = github.NewClient(tc) + } + for { + opt.Page = i + repos, _, err := client.Repositories.List(context.TODO(), repo.User, opt) + if err != nil { + log.Panic().Str("stage", "github").Str("url", "https://github.com").Msg(err.Error()) + } + if len(repos) == 0 { + break + } + githubrepos = append(githubrepos, repos...) + i++ + } + + exclude := types.GetExcludedMap(repo.Exclude) + excludeorgs := types.GetExcludedMap(repo.ExcludeOrgs) + + for _, r := range githubrepos { + if exclude[*r.Name] { + continue + } + if excludeorgs[r.GetOwner().GetLogin()] { + continue + } + + repos = append(repos, types.Repo{Name: r.GetName(), Url: r.GetCloneURL(), SshUrl: r.GetSSHURL(), Token: repo.Token, Defaultbranch: r.GetDefaultBranch(), Origin: repo}) + } + } + return repos +} diff --git a/gitlab/gitlab.go b/gitlab/gitlab.go new file mode 100644 index 0000000..8619b0d --- /dev/null +++ b/gitlab/gitlab.go @@ -0,0 +1,138 @@ +package gitlab + +import ( + "fmt" + "gickup/types" + "strings" + + "github.com/rs/zerolog/log" + "github.com/xanzy/go-gitlab" +) + +func Backup(r types.Repo, d types.GenRepo, dry bool) { + gitlabclient := &gitlab.Client{} + var err error + if d.Url == "" { + d.Url = "https://gitlab.com" + gitlabclient, err = gitlab.NewClient(d.Token) + } else { + gitlabclient, err = gitlab.NewClient(d.Token, gitlab.WithBaseURL(d.Url)) + } + log.Info().Str("stage", "gitlab").Str("url", d.Url).Msgf("mirroring %s to %s", types.Blue(r.Name), d.Url) + if err != nil { + log.Panic().Str("stage", "gitlab").Str("url", d.Url).Msg(err.Error()) + } + + True := true + opt := gitlab.ListProjectsOptions{Search: &r.Name, Owned: &True} + projects, _, err := gitlabclient.Projects.ListProjects(&opt) + if err != nil { + log.Panic().Str("stage", "gitlab").Str("url", d.Url).Msg(err.Error()) + } + + found := false + for _, p := range projects { + if p.Name == r.Name { + found = true + } + } + + if !dry { + if !found { + if r.Token != "" { + splittedurl := strings.Split(r.Url, "//") + r.Url = fmt.Sprintf("%s//%s@%s", splittedurl[0], r.Token, splittedurl[1]) + if r.Token == "" { + r.Url = fmt.Sprintf("%s//%s:%s@%s", splittedurl[0], r.Origin.User, r.Origin.Password, splittedurl[1]) + } + } + opts := &gitlab.CreateProjectOptions{Mirror: &True, ImportURL: &r.Url, Name: &r.Name} + _, _, err := gitlabclient.Projects.CreateProject(opts) + if err != nil { + log.Panic().Str("stage", "gitlab").Str("url", d.Url).Msg(err.Error()) + } + } + } +} + +func Get(conf *types.Conf) []types.Repo { + repos := []types.Repo{} + for _, repo := range conf.Source.Gitlab { + if repo.Url == "" { + repo.Url = "https://gitlab.com" + } + log.Info().Str("stage", "gitlab").Str("url", repo.Url).Msgf("grabbing repositories from %s", repo.User) + gitlabrepos := []*gitlab.Project{} + gitlabgrouprepos := []*gitlab.Project{} + client, err := gitlab.NewClient(repo.Token, gitlab.WithBaseURL(repo.Url)) + if err != nil { + log.Panic().Str("stage", "gitlab").Str("url", repo.Url).Msg(err.Error()) + } + opt := &gitlab.ListProjectsOptions{} + users, _, err := client.Users.ListUsers(&gitlab.ListUsersOptions{Username: &repo.User}) + if err != nil { + log.Panic().Str("stage", "gitlab").Str("url", repo.Url).Msg(err.Error()) + } + + opt.PerPage = 50 + i := 0 + for _, user := range users { + if user.Username == repo.User { + for { + projects, _, err := client.Projects.ListUserProjects(user.ID, opt) + if err != nil { + log.Panic().Str("stage", "gitlab").Str("url", repo.Url).Msg(err.Error()) + } + if len(projects) == 0 { + break + } + gitlabrepos = append(gitlabrepos, projects...) + i++ + opt.Page = i + } + } + } + + exclude := types.GetExcludedMap(repo.Exclude) + + for _, r := range gitlabrepos { + if exclude[r.Name] { + continue + } + repos = append(repos, types.Repo{Name: r.Name, Url: r.HTTPURLToRepo, SshUrl: r.SSHURLToRepo, Token: repo.Token, Defaultbranch: r.DefaultBranch, Origin: repo}) + } + groups, _, err := client.Groups.ListGroups(&gitlab.ListGroupsOptions{}) + if err != nil { + log.Panic().Str("stage", "gitlab").Str("url", repo.Url).Msg(err.Error()) + } + + visibilities := []gitlab.VisibilityValue{gitlab.PrivateVisibility, gitlab.PublicVisibility, gitlab.InternalVisibility} + + for _, visibility := range visibilities { + gopt := &gitlab.ListGroupProjectsOptions{Visibility: gitlab.Visibility(visibility)} + gopt.PerPage = 50 + i = 0 + for _, group := range groups { + for { + projects, _, err := client.Groups.ListGroupProjects(group.ID, gopt) + if err != nil { + log.Panic().Str("stage", "gitlab").Str("url", repo.Url).Msg(err.Error()) + } + if len(projects) == 0 { + break + } + gitlabgrouprepos = append(gitlabgrouprepos, projects...) + i++ + gopt.Page = i + } + } + for _, r := range gitlabgrouprepos { + if exclude[r.Name] { + continue + } + repos = append(repos, types.Repo{Name: r.Name, Url: r.HTTPURLToRepo, SshUrl: r.SSHURLToRepo, Token: repo.Token, Defaultbranch: r.DefaultBranch, Origin: repo}) + } + } + } + return repos +} diff --git a/go.mod b/go.mod index 0e88681..9ac1d64 100644 --- a/go.mod +++ b/go.mod @@ -10,16 +10,13 @@ require ( github.com/go-git/go-git/v5 v5.4.2 github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85 github.com/golang/protobuf v1.5.2 // indirect - github.com/google/go-github/v41 v41.0.0 // indirect - github.com/google/go-querystring v1.1.0 // indirect + github.com/google/go-github/v41 v41.0.0 github.com/gookit/color v1.5.0 github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-retryablehttp v0.7.0 // indirect github.com/hashicorp/go-version v1.3.0 // indirect - github.com/k0kubun/pp v3.0.1+incompatible // indirect github.com/kevinburke/ssh_config v1.1.0 // indirect github.com/ktrysmt/go-bitbucket v0.9.32 - github.com/mattn/go-colorable v0.1.12 // indirect github.com/melbahja/goph v1.3.0 github.com/mitchellh/mapstructure v1.4.3 // indirect github.com/rs/zerolog v1.26.0 @@ -31,7 +28,6 @@ require ( golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 golang.org/x/sys v0.0.0-20211124211545-fe61309f8881 // indirect golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 // indirect - google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.27.1 // indirect gopkg.in/yaml.v2 v2.4.0 ) diff --git a/go.sum b/go.sum index 1aa0b05..70ca76c 100644 --- a/go.sum +++ b/go.sum @@ -38,19 +38,14 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= -github.com/Microsoft/go-winio v0.5.0 h1:Elr9Wn+sGKPlkaBvwu4mTrxtmOp3F3yV9qhaHbXGjwU= github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= -github.com/ProtonMail/go-crypto v0.0.0-20210920160938-87db9fbc61c7 h1:DSqTh6nEes/uO8BlNcGk8PzZsxY2sN9ZL//veWBdTRI= -github.com/ProtonMail/go-crypto v0.0.0-20210920160938-87db9fbc61c7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= github.com/ProtonMail/go-crypto v0.0.0-20211112122917-428f8eabeeb3 h1:XcF0cTDJeiuZ5NU8w7WUDge0HRwwNRmxj/GGk6KSA6g= github.com/ProtonMail/go-crypto v0.0.0-20211112122917-428f8eabeeb3/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk= github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= -github.com/alecthomas/kong v0.2.17 h1:URDISCI96MIgcIlQyoCAlhOmrSw6pZScBNkctg8r0W0= -github.com/alecthomas/kong v0.2.17/go.mod h1:ka3VZ8GZNPXv9Ov+j4YNLkI8mTuhXyr/0ktSlqIydQQ= github.com/alecthomas/kong v0.2.19 h1:qBDfByO5XgWUXyNB4D6OOhGh5Z1eNOwWayDPQJFNWdc= github.com/alecthomas/kong v0.2.19/go.mod h1:ka3VZ8GZNPXv9Ov+j4YNLkI8mTuhXyr/0ktSlqIydQQ= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA= @@ -130,7 +125,6 @@ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= @@ -151,8 +145,6 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/gookit/color v1.4.2 h1:tXy44JFSFkKnELV6WaMo/lLfu/meqITX3iAV52do7lk= -github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ= github.com/gookit/color v1.5.0 h1:1Opow3+BWDwqor78DcJkJCIwnkviFi+rrOANki9BUFw= github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= @@ -176,11 +168,8 @@ github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 h1:uC1QfSlInpQF+M0ao65imhwqKnz3Q2z/d8PWZRMQvDM= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= github.com/k0kubun/pp v2.3.0+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg= -github.com/k0kubun/pp v3.0.1+incompatible h1:3tqvf7QgUnZ5tXO6pNAZlrvHgl6DvifjDrd9g2S9Z40= -github.com/k0kubun/pp v3.0.1+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg= github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kevinburke/ssh_config v1.1.0 h1:pH/t1WS9NzT8go394IqZeJTMHVm6Cr6ZJ6AQ+mdNo/o= github.com/kevinburke/ssh_config v1.1.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= @@ -195,27 +184,17 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/ktrysmt/go-bitbucket v0.9.27 h1:1VGdeRHpZZyiIIIBjLr62ATbtOOZCJyyaFZeEWHzmOU= -github.com/ktrysmt/go-bitbucket v0.9.27/go.mod h1:FWxy2UK7GlK5b0NSJGc5hPqnssVlkNnsChvyuOf/Xno= github.com/ktrysmt/go-bitbucket v0.9.32 h1:IVk0m0gdB4OzRRLgxFnqNsfWKPXNdrcvgdpp9BojTpI= github.com/ktrysmt/go-bitbucket v0.9.32/go.mod h1:FWxy2UK7GlK5b0NSJGc5hPqnssVlkNnsChvyuOf/Xno= github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A= github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs= -github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= -github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/melbahja/goph v1.3.0 h1:RAIS7eL2tew/UrNmBpY2NZMxw6fWtOxki9nkrzw8mZY= github.com/melbahja/goph v1.3.0/go.mod h1:04M6J+mKmwzAOWhO0ABTweHGU3cizOp90WdCoxrn9gQ= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v0.0.0-20180220230111-00c29f56e238/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.4.2 h1:6h7AQ0yhTcIsmFmnAwQls75jp2Gzs4iB8W7pjMO+rqo= -github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs= github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= @@ -229,8 +208,6 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/rs/zerolog v1.25.0 h1:Rj7XygbUHKUlDPcVdoLyR91fJBsduXj5fRxyqIQj/II= -github.com/rs/zerolog v1.25.0/go.mod h1:7KHcEGe0QZPOm2IE4Kpb5rTh6n1h2hIgS5OOnu1rUaI= github.com/rs/zerolog v1.26.0 h1:ORM4ibhEZeTeQlCojCK2kPz1ogAY4bGs4tD+SaAdGaE= github.com/rs/zerolog v1.26.0/go.mod h1:yBiM87lvSqX8h0Ww4sdzNSkVYZ8dL2xjZJG1lAuGZEo= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= @@ -245,8 +222,6 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/xanzy/go-gitlab v0.51.1 h1:wWKLalwx4omxFoHh3PLs9zDgAD4GXDP/uoxwMRCSiWM= -github.com/xanzy/go-gitlab v0.51.1/go.mod h1:Q+hQhV508bDPoBijv7YjK/Lvlb4PhVhJdKqXVQrUoAE= github.com/xanzy/go-gitlab v0.52.2 h1:gkgg1z4ON70sphibtD86Bfmt1qV3mZ0pU0CBBCFAEvQ= github.com/xanzy/go-gitlab v0.52.2/go.mod h1:Q+hQhV508bDPoBijv7YjK/Lvlb4PhVhJdKqXVQrUoAE= github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= @@ -257,7 +232,6 @@ github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1z github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= @@ -274,10 +248,7 @@ golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871 h1:/pEO3GD/ABYAjuakUS6xSEmmlyVS4kxBNkeA9tLJiTI= -golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20211202192323-5770296d904e h1:MUP6MR3rJ7Gk9LEia0LP2ytiH6MuCfs7qYz+47jGdD8= golang.org/x/crypto v0.0.0-20211202192323-5770296d904e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -341,11 +312,7 @@ golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211008194852-3b03d305991f h1:1scJEYZBaF48BaG6tYbtxmLcXqwYGSfGcMoStTqkkIw= -golang.org/x/net v0.0.0-20211008194852-3b03d305991f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211201190559-0a0e4e1bb54c h1:WtYZ93XtWSO5KlOMgPZu7hXY9WhMZpprvlm5VwvAl8c= golang.org/x/net v0.0.0-20211201190559-0a0e4e1bb54c/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= @@ -356,8 +323,6 @@ golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4Iltr golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1 h1:B333XXssMuKQeBwiNODx4TupZy7bf4sxFZnN2ZOcvUE= -golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg= golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -408,18 +373,12 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac h1:oN6lz7iLW/YC7un8pq+9bOLyXrprv2+DKfkJY+2LJJw= -golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211031064116-611d5d643895 h1:iaNpwpnrgL5jzWS0vCNnfa8HqzxveCFpFx3uC/X4Tps= golang.org/x/sys v0.0.0-20211031064116-611d5d643895/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881 h1:TyHqChC80pFkXWraUUf6RuB5IqFdQieMLwwCJokV2pc= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -433,8 +392,6 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs= -golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 h1:GZokNIeuVkl3aZHJchRrr13WCsols02MLUcz1U9is6M= golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -478,7 +435,6 @@ golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/gogs/gogs.go b/gogs/gogs.go new file mode 100644 index 0000000..b5bd664 --- /dev/null +++ b/gogs/gogs.go @@ -0,0 +1,62 @@ +package gogs + +import ( + "gickup/types" + + "github.com/gogs/go-gogs-client" + "github.com/rs/zerolog/log" +) + +func Backup(r types.Repo, d types.GenRepo, dry bool) { + log.Info().Str("stage", "gogs").Str("url", d.Url).Msgf("mirroring %s to %s", types.Blue(r.Name), d.Url) + gogsclient := gogs.NewClient(d.Url, d.Token) + + user, err := gogsclient.GetSelfInfo() + if err != nil { + log.Panic().Str("stage", "gogs").Str("url", d.Url).Msg(err.Error()) + } + if !dry { + repo, err := gogsclient.GetRepo(user.UserName, r.Name) + if err != nil { + opts := gogs.MigrateRepoOption{RepoName: r.Name, UID: int(user.ID), Mirror: true, CloneAddr: r.Url, AuthUsername: r.Token} + if r.Token == "" { + opts = gogs.MigrateRepoOption{RepoName: r.Name, UID: int(user.ID), Mirror: true, CloneAddr: r.Url, AuthUsername: r.Origin.User, AuthPassword: r.Origin.Password} + } + _, err := gogsclient.MigrateRepo(opts) + if err != nil { + log.Panic().Str("stage", "gogs").Str("url", d.Url).Msg(err.Error()) + } + } else { + if repo.Mirror { + log.Info().Str("stage", "gogs").Str("url", d.Url).Msgf("mirror of %s already exists, syncing instead", types.Blue(r.Name)) + err := gogsclient.MirrorSync(user.UserName, repo.Name) + if err != nil { + log.Panic().Str("stage", "gogs").Str("url", d.Url).Msg(err.Error()) + } + log.Info().Str("stage", "gogs").Str("url", d.Url).Msgf("successfully synced %s.", types.Blue(r.Name)) + } + } + } +} + +func Get(conf *types.Conf) []types.Repo { + repos := []types.Repo{} + for _, repo := range conf.Source.Gogs { + log.Info().Str("stage", "gogs").Str("url", repo.Url).Msgf("grabbing repositories from %s", repo.User) + client := gogs.NewClient(repo.Url, repo.Token) + gogsrepos, err := client.ListUserRepos(repo.User) + if err != nil { + log.Panic().Str("stage", "gogs").Str("url", repo.Url).Msg(err.Error()) + } + + exclude := types.GetExcludedMap(repo.Exclude) + + for _, r := range gogsrepos { + if exclude[r.Name] { + continue + } + repos = append(repos, types.Repo{Name: r.Name, Url: r.CloneURL, SshUrl: r.SSHURL, Token: repo.Token, Defaultbranch: r.DefaultBranch, Origin: repo}) + } + } + return repos +} diff --git a/local/local.go b/local/local.go new file mode 100644 index 0000000..836c4d9 --- /dev/null +++ b/local/local.go @@ -0,0 +1,170 @@ +package local + +import ( + "gickup/types" + "net" + "os" + "path" + "strings" + "time" + + "github.com/go-git/go-git/v5" + "github.com/go-git/go-git/v5/plumbing/transport" + "github.com/go-git/go-git/v5/plumbing/transport/http" + "github.com/go-git/go-git/v5/plumbing/transport/ssh" + "github.com/melbahja/goph" + "github.com/rs/zerolog/log" + gossh "golang.org/x/crypto/ssh" +) + +func Locally(repo types.Repo, l types.Local, dry bool) { + stat, err := os.Stat(l.Path) + if os.IsNotExist(err) && !dry { + err := os.MkdirAll(l.Path, 0777) + if err != nil { + log.Panic().Str("stage", "locally").Str("path", l.Path).Msg(err.Error()) + } + stat, _ = os.Stat(l.Path) + } + if stat != nil { + if stat.IsDir() { + os.Chdir(l.Path) + } + } + + tries := 5 + var auth transport.AuthMethod + if repo.Origin.SSH { + if repo.Origin.SSHKey == "" { + home := os.Getenv("HOME") + repo.Origin.SSHKey = path.Join(home, ".ssh", "id_rsa") + } + auth, err = ssh.NewPublicKeysFromFile("git", repo.Origin.SSHKey, "") + if err != nil { + panic(err) + } + } else if repo.Token != "" { + auth = &http.BasicAuth{ + Username: "xyz", + Password: repo.Token, + } + } else if repo.Origin.Username != "" && repo.Origin.Password != "" { + auth = &http.BasicAuth{ + Username: repo.Origin.Username, + Password: repo.Origin.Password, + } + } + for x := 1; x <= tries; x++ { + stat, err := os.Stat(repo.Name) + if os.IsNotExist(err) { + log.Info().Str("stage", "locally").Str("path", l.Path).Msgf("cloning %s", types.Green(repo.Name)) + + if !dry { + url := repo.Url + if repo.Origin.SSH { + url = repo.SshUrl + site := types.Site{} + err := site.GetValues(url) + if err != nil { + log.Panic().Str("stage", "locally").Msg(err.Error()) + } + auth, err := goph.Key(repo.Origin.SSHKey, "") + if err != nil { + log.Panic().Str("stage", "locally").Msg(err.Error()) + } + _, err = goph.NewConn(&goph.Config{ + User: site.User, + Addr: site.Url, + Port: uint(site.Port), + Auth: auth, + Callback: VerifyHost, + }) + if err != nil { + log.Panic().Str("stage", "locally").Msg(err.Error()) + } + } + + _, err = git.PlainClone(repo.Name, false, &git.CloneOptions{ + URL: url, + Auth: auth, + SingleBranch: false, + }) + + if err != nil { + if x == tries { + log.Panic().Str("stage", "locally").Str("path", l.Path).Msg(err.Error()) + } else { + if strings.Contains(err.Error(), "remote repository is empty") { + log.Warn().Str("stage", "locally").Str("path", l.Path).Msg(err.Error()) + break + } + log.Warn().Str("stage", "locally").Str("path", l.Path).Msgf("retry %s from %s", types.Red(x), types.Red(tries)) + time.Sleep(5 * time.Second) + continue + } + } + } + } else { + if stat.IsDir() { + log.Info().Str("stage", "locally").Str("path", l.Path).Msgf("opening %s locally", types.Green(repo.Name)) + r, err := git.PlainOpen(repo.Name) + if err != nil { + log.Panic().Str("stage", "locally").Str("path", l.Path).Msg(err.Error()) + } + w, err := r.Worktree() + if err != nil { + log.Panic().Str("stage", "locally").Str("path", l.Path).Msg(err.Error()) + } + + log.Info().Str("stage", "locally").Str("path", l.Path).Msgf("pulling %s", types.Green(repo.Name)) + if !dry { + err = w.Pull(&git.PullOptions{Auth: auth, RemoteName: "origin", SingleBranch: false}) + if err != nil { + if strings.Contains(err.Error(), "already up-to-date") { + log.Info().Str("stage", "locally").Str("path", l.Path).Msg(err.Error()) + } else { + if x == tries { + log.Panic().Str("stage", "locally").Str("path", l.Path).Msg(err.Error()) + } else { + os.RemoveAll(repo.Name) + log.Warn().Str("stage", "locally").Str("path", l.Path).Msgf("retry %s from %s", types.Red(x), types.Red(tries)) + time.Sleep(5 * time.Second) + continue + } + } + } + } + } else { + log.Warn().Str("stage", "locally").Str("path", l.Path).Msgf("%s is a file", types.Red(repo.Name)) + } + } + x = 5 + } +} + +func VerifyHost(host string, remote net.Addr, key gossh.PublicKey) error { + // Got from the example from https://github.com/melbahja/goph/blob/master/examples/goph/main.go + // + // If you want to connect to new hosts. + // here your should check new connections public keys + // if the key not trusted you shuld return an error + // + + // hostFound: is host in known hosts file. + // err: error if key not in known hosts file OR host in known hosts file but key changed! + hostFound, err := goph.CheckKnownHost(host, remote, key, "") + // Host in known hosts but key mismatch! + // Maybe because of MAN IN THE MIDDLE ATTACK! + /* + if hostFound && err != nil { + return err + } + */ + // handshake because public key already exists. + if hostFound && err == nil { + return nil + } + + // Add the new host to known hosts file. + return goph.AddKnownHost(host, remote, key, "") +} diff --git a/main.go b/main.go index 962497c..5bbf93d 100644 --- a/main.go +++ b/main.go @@ -1,34 +1,22 @@ package main import ( - "context" "fmt" "io/ioutil" - "net" - "net/url" "os" - "path" "path/filepath" - "strconv" - "strings" - "time" - "code.gitea.io/sdk/gitea" + "gickup/bitbucket" + "gickup/gitea" + "gickup/github" + "gickup/gitlab" + "gickup/gogs" + "gickup/local" + "gickup/types" + "github.com/alecthomas/kong" - "github.com/go-git/go-git/v5" - "github.com/go-git/go-git/v5/plumbing/transport" - "github.com/go-git/go-git/v5/plumbing/transport/http" - "github.com/go-git/go-git/v5/plumbing/transport/ssh" - "github.com/gogs/go-gogs-client" - "github.com/google/go-github/v41/github" - "github.com/gookit/color" - "github.com/ktrysmt/go-bitbucket" - "github.com/melbahja/goph" "github.com/rs/zerolog" "github.com/rs/zerolog/log" - "github.com/xanzy/go-gitlab" - gossh "golang.org/x/crypto/ssh" - "golang.org/x/oauth2" "gopkg.in/yaml.v2" ) @@ -48,75 +36,23 @@ func (v dryrunFlag) BeforeApply() error { } var cli struct { - Configfile string `arg required name:"conf" help:"path to the configfile." type:"existingfile"` + Configfile string `arg requitypes.Red name:"conf" help:"path to the configfile." type:"existingfile"` Version versionFlag Dry dryrunFlag `flag name:"dryrun" help:"make a dry-run."` } var ( - red = color.FgRed.Render - green = color.FgGreen.Render - blue = color.FgBlue.Render - dry = false + dry = false ) -func (s *Site) GetValues(url string) { - if strings.HasPrefix(url, "ssh://") { - url = strings.Split(url, "ssh://")[1] - userurl := strings.Split(url, "@") - s.User = userurl[0] - urlport := strings.Split(userurl[1], ":") - s.Url = urlport[0] - portstring := strings.Split(urlport[1], "/")[0] - port, err := strconv.Atoi(portstring) - if err != nil { - log.Panic().Str("stage", "GetValus").Msg(err.Error()) - } - s.Port = port - } else { - userurl := strings.Split(url, "@") - s.User = userurl[0] - urlport := strings.Split(userurl[1], ":") - s.Url = urlport[0] - s.Port = 22 - } -} - -func VerifyHost(host string, remote net.Addr, key gossh.PublicKey) error { - // Got from the example from https://github.com/melbahja/goph/blob/master/examples/goph/main.go - // - // If you want to connect to new hosts. - // here your should check new connections public keys - // if the key not trusted you shuld return an error - // - - // hostFound: is host in known hosts file. - // err: error if key not in known hosts file OR host in known hosts file but key changed! - hostFound, err := goph.CheckKnownHost(host, remote, key, "") - // Host in known hosts but key mismatch! - // Maybe because of MAN IN THE MIDDLE ATTACK! - /* - if hostFound && err != nil { - return err - } - */ - // handshake because public key already exists. - if hostFound && err == nil { - return nil - } - - // Add the new host to known hosts file. - return goph.AddKnownHost(host, remote, key, "") -} - -func ReadConfigfile(configfile string) *Conf { +func ReadConfigfile(configfile string) *types.Conf { cfgdata, err := ioutil.ReadFile(configfile) if err != nil { - log.Panic().Str("stage", "readconfig").Str("file", configfile).Msgf("Cannot open config file from %s", red(configfile)) + log.Panic().Str("stage", "readconfig").Str("file", configfile).Msgf("Cannot open config file from %s", types.Red(configfile)) } - t := Conf{} + t := types.Conf{} err = yaml.Unmarshal([]byte(cfgdata), &t) @@ -127,254 +63,7 @@ func ReadConfigfile(configfile string) *Conf { return &t } -func GetExcludedMap(excludes []string) map[string]bool { - excludemap := make(map[string]bool) - for _, exclude := range excludes { - excludemap[exclude] = true - } - return excludemap -} - -func Locally(repo Repo, l Local) { - stat, err := os.Stat(l.Path) - if os.IsNotExist(err) && !dry { - err := os.MkdirAll(l.Path, 0777) - if err != nil { - log.Panic().Str("stage", "locally").Str("path", l.Path).Msg(err.Error()) - } - stat, _ = os.Stat(l.Path) - } - if stat != nil { - if stat.IsDir() { - os.Chdir(l.Path) - } - } - - tries := 5 - var auth transport.AuthMethod - if repo.Origin.SSH { - if repo.Origin.SSHKey == "" { - home := os.Getenv("HOME") - repo.Origin.SSHKey = path.Join(home, ".ssh", "id_rsa") - } - auth, err = ssh.NewPublicKeysFromFile("git", repo.Origin.SSHKey, "") - if err != nil { - panic(err) - } - } else if repo.Token != "" { - auth = &http.BasicAuth{ - Username: "xyz", - Password: repo.Token, - } - } else if repo.Origin.Username != "" && repo.Origin.Password != "" { - auth = &http.BasicAuth{ - Username: repo.Origin.Username, - Password: repo.Origin.Password, - } - } - for x := 1; x <= tries; x++ { - stat, err := os.Stat(repo.Name) - if os.IsNotExist(err) { - log.Info().Str("stage", "locally").Str("path", l.Path).Msgf("cloning %s", green(repo.Name)) - - if !dry { - url := repo.Url - if repo.Origin.SSH { - url = repo.SshUrl - site := Site{} - site.GetValues(url) - auth, err := goph.Key(repo.Origin.SSHKey, "") - if err != nil { - log.Panic().Str("stage", "locally").Msg(err.Error()) - } - _, err = goph.NewConn(&goph.Config{ - User: site.User, - Addr: site.Url, - Port: uint(site.Port), - Auth: auth, - Callback: VerifyHost, - }) - if err != nil { - log.Panic().Str("stage", "locally").Msg(err.Error()) - } - } - - _, err = git.PlainClone(repo.Name, false, &git.CloneOptions{ - URL: url, - Auth: auth, - SingleBranch: false, - }) - - if err != nil { - if x == tries { - log.Panic().Str("stage", "locally").Str("path", l.Path).Msg(err.Error()) - } else { - if strings.Contains(err.Error(), "remote repository is empty") { - log.Warn().Str("stage", "locally").Str("path", l.Path).Msg(err.Error()) - break - } - log.Warn().Str("stage", "locally").Str("path", l.Path).Msgf("retry %s from %s", red(x), red(tries)) - time.Sleep(5 * time.Second) - continue - } - } - } - } else { - if stat.IsDir() { - log.Info().Str("stage", "locally").Str("path", l.Path).Msgf("opening %s locally", green(repo.Name)) - r, err := git.PlainOpen(repo.Name) - if err != nil { - log.Panic().Str("stage", "locally").Str("path", l.Path).Msg(err.Error()) - } - w, err := r.Worktree() - if err != nil { - log.Panic().Str("stage", "locally").Str("path", l.Path).Msg(err.Error()) - } - - log.Info().Str("stage", "locally").Str("path", l.Path).Msgf("pulling %s", green(repo.Name)) - if !dry { - err = w.Pull(&git.PullOptions{Auth: auth, RemoteName: "origin", SingleBranch: false}) - if err != nil { - if strings.Contains(err.Error(), "already up-to-date") { - log.Info().Str("stage", "locally").Str("path", l.Path).Msg(err.Error()) - } else { - if x == tries { - log.Panic().Str("stage", "locally").Str("path", l.Path).Msg(err.Error()) - } else { - os.RemoveAll(repo.Name) - log.Warn().Str("stage", "locally").Str("path", l.Path).Msgf("retry %s from %s", red(x), red(tries)) - time.Sleep(5 * time.Second) - continue - } - } - } - } - } else { - log.Warn().Str("stage", "locally").Str("path", l.Path).Msgf("%s is a file", red(repo.Name)) - } - } - x = 5 - } -} - -func BackupGitea(r Repo, d GenRepo) { - if d.Url == "" { - d.Url = "https://gitea.com/" - } - log.Info().Str("stage", "gitea").Str("url", d.Url).Msgf("mirroring %s to %s", blue(r.Name), d.Url) - giteaclient, err := gitea.NewClient(d.Url) - if err != nil { - log.Panic().Str("stage", "gitea").Str("url", d.Url).Msg(err.Error()) - } - giteaclient.SetBasicAuth(d.Token, "") - user, _, err := giteaclient.GetMyUserInfo() - if err != nil { - log.Panic().Str("stage", "gitea").Str("url", d.Url).Msg(err.Error()) - } - if !dry { - repo, _, err := giteaclient.GetRepo(user.UserName, r.Name) - if err != nil { - opts := gitea.MigrateRepoOption{RepoName: r.Name, RepoOwner: user.UserName, Mirror: true, CloneAddr: r.Url, AuthToken: r.Token} - if r.Token == "" { - opts = gitea.MigrateRepoOption{RepoName: r.Name, RepoOwner: user.UserName, Mirror: true, CloneAddr: r.Url, AuthUsername: r.Origin.User, AuthPassword: r.Origin.Password} - } - _, _, err := giteaclient.MigrateRepo(opts) - if err != nil { - log.Panic().Str("stage", "gitea").Str("url", d.Url).Msg(err.Error()) - } - log.Info().Str("stage", "gitea").Str("url", d.Url).Msgf("mirrored %s to %s", blue(r.Name), d.Url) - } else { - if repo.Mirror { - log.Info().Str("stage", "gitea").Str("url", d.Url).Msgf("mirror of %s already exists, syncing instead", blue(r.Name)) - _, err := giteaclient.MirrorSync(user.UserName, repo.Name) - if err != nil { - log.Panic().Str("stage", "gitea").Str("url", d.Url).Msg(err.Error()) - } - log.Info().Str("stage", "gitea").Str("url", d.Url).Msgf("successfully synced %s.", blue(r.Name)) - } - } - } -} - -func BackupGogs(r Repo, d GenRepo) { - log.Info().Str("stage", "gogs").Str("url", d.Url).Msgf("mirroring %s to %s", blue(r.Name), d.Url) - gogsclient := gogs.NewClient(d.Url, d.Token) - - user, err := gogsclient.GetSelfInfo() - if err != nil { - log.Panic().Str("stage", "gogs").Str("url", d.Url).Msg(err.Error()) - } - if !dry { - repo, err := gogsclient.GetRepo(user.UserName, r.Name) - if err != nil { - opts := gogs.MigrateRepoOption{RepoName: r.Name, UID: int(user.ID), Mirror: true, CloneAddr: r.Url, AuthUsername: r.Token} - if r.Token == "" { - opts = gogs.MigrateRepoOption{RepoName: r.Name, UID: int(user.ID), Mirror: true, CloneAddr: r.Url, AuthUsername: r.Origin.User, AuthPassword: r.Origin.Password} - } - _, err := gogsclient.MigrateRepo(opts) - if err != nil { - log.Panic().Str("stage", "gogs").Str("url", d.Url).Msg(err.Error()) - } - } else { - if repo.Mirror { - log.Info().Str("stage", "gogs").Str("url", d.Url).Msgf("mirror of %s already exists, syncing instead", blue(r.Name)) - err := gogsclient.MirrorSync(user.UserName, repo.Name) - if err != nil { - log.Panic().Str("stage", "gogs").Str("url", d.Url).Msg(err.Error()) - } - log.Info().Str("stage", "gogs").Str("url", d.Url).Msgf("successfully synced %s.", blue(r.Name)) - } - } - } -} - -func BackupGitlab(r Repo, d GenRepo) { - gitlabclient := &gitlab.Client{} - var err error - if d.Url == "" { - d.Url = "https://gitlab.com" - gitlabclient, err = gitlab.NewClient(d.Token) - } else { - gitlabclient, err = gitlab.NewClient(d.Token, gitlab.WithBaseURL(d.Url)) - } - log.Info().Str("stage", "gitlab").Str("url", d.Url).Msgf("mirroring %s to %s", blue(r.Name), d.Url) - if err != nil { - log.Panic().Str("stage", "gitlab").Str("url", d.Url).Msg(err.Error()) - } - - True := true - opt := gitlab.ListProjectsOptions{Search: &r.Name, Owned: &True} - projects, _, err := gitlabclient.Projects.ListProjects(&opt) - if err != nil { - log.Panic().Str("stage", "gitlab").Str("url", d.Url).Msg(err.Error()) - } - - found := false - for _, p := range projects { - if p.Name == r.Name { - found = true - } - } - - if !dry { - if !found { - if r.Token != "" { - splittedurl := strings.Split(r.Url, "//") - r.Url = fmt.Sprintf("%s//%s@%s", splittedurl[0], r.Token, splittedurl[1]) - if r.Token == "" { - r.Url = fmt.Sprintf("%s//%s:%s@%s", splittedurl[0], r.Origin.User, r.Origin.Password, splittedurl[1]) - } - } - opts := &gitlab.CreateProjectOptions{Mirror: &True, ImportURL: &r.Url, Name: &r.Name} - _, _, err := gitlabclient.Projects.CreateProject(opts) - if err != nil { - log.Panic().Str("stage", "gitlab").Str("url", d.Url).Msg(err.Error()) - } - } - } -} - -func Backup(repos []Repo, conf *Conf) { +func Backup(repos []types.Repo, conf *types.Conf) { checkedpath := false for _, r := range repos { log.Info().Str("stage", "backup").Msgf("starting backup for %s", r.Url) @@ -387,246 +76,20 @@ func Backup(repos []Repo, conf *Conf) { conf.Destination.Local[i].Path = path checkedpath = true } - Locally(r, d) + local.Locally(r, d, dry) } for _, d := range conf.Destination.Gitea { - BackupGitea(r, d) + gitea.Backup(r, d, dry) } for _, d := range conf.Destination.Gogs { - BackupGogs(r, d) + gogs.Backup(r, d, dry) } for _, d := range conf.Destination.Gitlab { - BackupGitlab(r, d) + gitlab.Backup(r, d, dry) } } } -func getGithub(conf *Conf) []Repo { - repos := []Repo{} - for _, repo := range conf.Source.Github { - log.Info().Str("stage", "github").Str("url", "https://github.com").Msgf("grabbing the repositories from %s", repo.User) - client := &github.Client{} - opt := &github.RepositoryListOptions{ListOptions: github.ListOptions{PerPage: 50}} - i := 1 - githubrepos := []*github.Repository{} - if repo.Token == "" { - client = github.NewClient(nil) - } else { - ts := oauth2.StaticTokenSource( - &oauth2.Token{AccessToken: repo.Token}, - ) - tc := oauth2.NewClient(context.TODO(), ts) - client = github.NewClient(tc) - } - for { - opt.Page = i - repos, _, err := client.Repositories.List(context.TODO(), repo.User, opt) - if err != nil { - log.Panic().Str("stage", "github").Str("url", "https://github.com").Msg(err.Error()) - } - if len(repos) == 0 { - break - } - githubrepos = append(githubrepos, repos...) - i++ - } - - exclude := GetExcludedMap(repo.Exclude) - excludeorgs := GetExcludedMap(repo.ExcludeOrgs) - - for _, r := range githubrepos { - if exclude[*r.Name] { - continue - } - if excludeorgs[r.GetOwner().GetLogin()] { - continue - } - - repos = append(repos, Repo{Name: r.GetName(), Url: r.GetCloneURL(), SshUrl: r.GetSSHURL(), Token: repo.Token, Defaultbranch: r.GetDefaultBranch(), Origin: repo}) - } - } - return repos -} - -func getGitea(conf *Conf) []Repo { - repos := []Repo{} - for _, repo := range conf.Source.Gitea { - if repo.Url == "" { - repo.Url = "https://gitea.com" - } - log.Info().Str("stage", "gitea").Str("url", repo.Url).Msgf("grabbing repositories from %s", repo.User) - opt := gitea.ListReposOptions{} - opt.PageSize = 50 - i := 0 - gitearepos := []*gitea.Repository{} - for { - opt.Page = i - client, err := gitea.NewClient(repo.Url) - if err != nil { - log.Panic().Str("stage", "gitea").Str("url", repo.Url).Msg(err.Error()) - } - if repo.Token != "" { - client.SetBasicAuth(repo.Token, "") - } - repos, _, err := client.ListUserRepos(repo.User, opt) - if err != nil { - log.Panic().Str("stage", "gitea").Str("url", repo.Url).Msg(err.Error()) - } - if len(repos) == 0 { - break - } - gitearepos = append(gitearepos, repos...) - i++ - } - - exclude := GetExcludedMap(repo.Exclude) - - for _, r := range gitearepos { - if exclude[r.Name] { - continue - } - repos = append(repos, Repo{Name: r.Name, Url: r.CloneURL, SshUrl: r.SSHURL, Token: repo.Token, Defaultbranch: r.DefaultBranch, Origin: repo}) - } - } - return repos -} - -func getGogs(conf *Conf) []Repo { - repos := []Repo{} - for _, repo := range conf.Source.Gogs { - log.Info().Str("stage", "gogs").Str("url", repo.Url).Msgf("grabbing repositories from %s", repo.User) - client := gogs.NewClient(repo.Url, repo.Token) - gogsrepos, err := client.ListUserRepos(repo.User) - if err != nil { - log.Panic().Str("stage", "gogs").Str("url", repo.Url).Msg(err.Error()) - } - - exclude := GetExcludedMap(repo.Exclude) - - for _, r := range gogsrepos { - if exclude[r.Name] { - continue - } - repos = append(repos, Repo{Name: r.Name, Url: r.CloneURL, SshUrl: r.SSHURL, Token: repo.Token, Defaultbranch: r.DefaultBranch, Origin: repo}) - } - } - return repos -} - -func getGitlab(conf *Conf) []Repo { - repos := []Repo{} - for _, repo := range conf.Source.Gitlab { - if repo.Url == "" { - repo.Url = "https://gitlab.com" - } - log.Info().Str("stage", "gitlab").Str("url", repo.Url).Msgf("grabbing repositories from %s", repo.User) - gitlabrepos := []*gitlab.Project{} - gitlabgrouprepos := []*gitlab.Project{} - client, err := gitlab.NewClient(repo.Token, gitlab.WithBaseURL(repo.Url)) - if err != nil { - log.Panic().Str("stage", "gitlab").Str("url", repo.Url).Msg(err.Error()) - } - opt := &gitlab.ListProjectsOptions{} - users, _, err := client.Users.ListUsers(&gitlab.ListUsersOptions{Username: &repo.User}) - if err != nil { - log.Panic().Str("stage", "gitlab").Str("url", repo.Url).Msg(err.Error()) - } - - opt.PerPage = 50 - i := 0 - for _, user := range users { - if user.Username == repo.User { - for { - projects, _, err := client.Projects.ListUserProjects(user.ID, opt) - if err != nil { - log.Panic().Str("stage", "gitlab").Str("url", repo.Url).Msg(err.Error()) - } - if len(projects) == 0 { - break - } - gitlabrepos = append(gitlabrepos, projects...) - i++ - opt.Page = i - } - } - } - - exclude := GetExcludedMap(repo.Exclude) - - for _, r := range gitlabrepos { - if exclude[r.Name] { - continue - } - repos = append(repos, Repo{Name: r.Name, Url: r.HTTPURLToRepo, SshUrl: r.SSHURLToRepo, Token: repo.Token, Defaultbranch: r.DefaultBranch, Origin: repo}) - } - groups, _, err := client.Groups.ListGroups(&gitlab.ListGroupsOptions{}) - if err != nil { - log.Panic().Str("stage", "gitlab").Str("url", repo.Url).Msg(err.Error()) - } - - visibilities := []gitlab.VisibilityValue{gitlab.PrivateVisibility, gitlab.PublicVisibility, gitlab.InternalVisibility} - - for _, visibility := range visibilities { - gopt := &gitlab.ListGroupProjectsOptions{Visibility: gitlab.Visibility(visibility)} - gopt.PerPage = 50 - i = 0 - for _, group := range groups { - for { - projects, _, err := client.Groups.ListGroupProjects(group.ID, gopt) - if err != nil { - log.Panic().Str("stage", "gitlab").Str("url", repo.Url).Msg(err.Error()) - } - if len(projects) == 0 { - break - } - gitlabgrouprepos = append(gitlabgrouprepos, projects...) - i++ - gopt.Page = i - } - } - for _, r := range gitlabgrouprepos { - if exclude[r.Name] { - continue - } - repos = append(repos, Repo{Name: r.Name, Url: r.HTTPURLToRepo, SshUrl: r.SSHURLToRepo, Token: repo.Token, Defaultbranch: r.DefaultBranch, Origin: repo}) - } - } - } - return repos -} - -func getBitbucket(conf *Conf) []Repo { - repos := []Repo{} - for _, repo := range conf.Source.BitBucket { - client := bitbucket.NewBasicAuth(repo.Username, repo.Password) - if repo.Url == "" { - repo.Url = bitbucket.DEFAULT_BITBUCKET_API_BASE_URL - } else { - bitbucketUrl, err := url.Parse(repo.Url) - if err != nil { - log.Panic().Str("stage", "bitbucket").Str("url", repo.Url).Msg(err.Error()) - } - client.SetApiBaseURL(*bitbucketUrl) - } - log.Info().Str("stage", "bitbucket").Str("url", repo.Url).Msgf("grabbing repositories from %s", repo.User) - - repositories, err := client.Repositories.ListForAccount(&bitbucket.RepositoriesOptions{Owner: repo.User}) - if err != nil { - log.Panic().Str("stage", "bitbucket").Str("url", repo.Url).Msg(err.Error()) - } - - exclude := GetExcludedMap(repo.Exclude) - - for _, r := range repositories.Items { - if exclude[r.Name] { - continue - } - repos = append(repos, Repo{Name: r.Name, Url: r.Links["clone"].([]interface{})[0].(map[string]interface{})["href"].(string), SshUrl: r.Links["clone"].([]interface{})[1].(map[string]interface{})["href"].(string), Token: "", Defaultbranch: r.Mainbranch.Name, Origin: repo}) - } - } - return repos -} - func main() { zerolog.TimeFieldFormat = zerolog.TimeFormatUnix log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}) @@ -634,29 +97,29 @@ func main() { kong.Parse(&cli, kong.Name("gickup"), kong.Description("a tool to backup all your favorite repos")) if dry { - log.Info().Str("dry", "true").Msgf("this is a %s", blue("dry run")) + log.Info().Str("dry", "true").Msgf("this is a %s", types.Blue("dry run")) } - log.Info().Str("file", cli.Configfile).Msgf("Reading %s", green(cli.Configfile)) + log.Info().Str("file", cli.Configfile).Msgf("Reading %s", types.Green(cli.Configfile)) conf := ReadConfigfile(cli.Configfile) // Github - repos := getGithub(conf) + repos := github.Get(conf) Backup(repos, conf) // Gitea - repos = getGitea(conf) + repos = gitea.Get(conf) Backup(repos, conf) // Gogs - repos = getGogs(conf) + repos = gogs.Get(conf) Backup(repos, conf) // Gitlab - repos = getGitlab(conf) + repos = gitlab.Get(conf) Backup(repos, conf) //Bitbucket - repos = getBitbucket(conf) + repos = bitbucket.Get(conf) Backup(repos, conf) } diff --git a/types.go b/types/types.go similarity index 57% rename from types.go rename to types/types.go index 20924ec..d8c338a 100644 --- a/types.go +++ b/types/types.go @@ -1,4 +1,11 @@ -package main +package types + +import ( + "strconv" + "strings" + + "github.com/gookit/color" +) // Destination type Destination struct { @@ -58,3 +65,40 @@ type Site struct { User string Port int } + +func (s *Site) GetValues(url string) error { + if strings.HasPrefix(url, "ssh://") { + url = strings.Split(url, "ssh://")[1] + userurl := strings.Split(url, "@") + s.User = userurl[0] + urlport := strings.Split(userurl[1], ":") + s.Url = urlport[0] + portstring := strings.Split(urlport[1], "/")[0] + port, err := strconv.Atoi(portstring) + if err != nil { + return err + } + s.Port = port + } else { + userurl := strings.Split(url, "@") + s.User = userurl[0] + urlport := strings.Split(userurl[1], ":") + s.Url = urlport[0] + s.Port = 22 + } + return nil +} + +var ( + Red = color.FgRed.Render + Green = color.FgGreen.Render + Blue = color.FgBlue.Render +) + +func GetExcludedMap(excludes []string) map[string]bool { + excludemap := make(map[string]bool) + for _, exclude := range excludes { + excludemap[exclude] = true + } + return excludemap +}