Merge pull request #76 from gabrie30/refactor

create new packages for each scm provider
This commit is contained in:
Jay Gabriels 2020-07-11 07:56:40 -07:00 committed by GitHub
commit 9f14d10fbc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 188 additions and 159 deletions

View File

@ -12,6 +12,10 @@ import (
"github.com/gabrie30/ghorg/colorlog"
"github.com/gabrie30/ghorg/configs"
"github.com/gabrie30/ghorg/internal/bitbucket"
"github.com/gabrie30/ghorg/internal/github"
"github.com/gabrie30/ghorg/internal/gitlab"
"github.com/gabrie30/ghorg/internal/repo"
"github.com/korovkin/limiter"
"github.com/spf13/cobra"
)
@ -34,6 +38,7 @@ var (
args []string
cloneErrors []string
cloneInfos []string
targetCloneSource string
)
func init() {
@ -56,133 +61,128 @@ func init() {
}
// Repo represents an SCM repo
type Repo struct {
Name string
Path string
URL string
CloneURL string
}
var cloneCmd = &cobra.Command{
Use: "clone",
Short: "Clone user or org repos from GitHub, GitLab, or Bitbucket",
Long: `Clone user or org repos from GitHub, GitLab, or Bitbucket. See $HOME/.config/ghorg/conf.yaml for defaults, its likely you will need to update some of these values of use the flags to overwrite them. Values are set first by a default value, then based off what is set in $HOME/.config/ghorg/conf.yaml, finally the cli flags, which have the highest level of precedence.`,
Run: func(cmd *cobra.Command, argz []string) {
Long: `Clone user or org repos from GitHub, GitLab, or Bitbucket. See $HOME/ghorg/conf.yaml for defaults, its likely you will need to update some of these values of use the flags to overwrite them. Values are set first by a default value, then based off what is set in $HOME/ghorg/conf.yaml, finally the cli flags, which have the highest level of precedence.`,
Run: cloneFunc,
}
if cmd.Flags().Changed("color") {
colorToggle := cmd.Flag("color").Value.String()
if colorToggle == "on" {
os.Setenv("GHORG_COLOR", colorToggle)
} else {
os.Setenv("GHORG_COLOR", "off")
}
func cloneFunc(cmd *cobra.Command, argz []string) {
if cmd.Flags().Changed("color") {
colorToggle := cmd.Flag("color").Value.String()
if colorToggle == "on" {
os.Setenv("GHORG_COLOR", colorToggle)
} else {
os.Setenv("GHORG_COLOR", "off")
}
if len(argz) < 1 {
colorlog.PrintError("You must provide an org or user to clone")
os.Exit(1)
}
if len(argz) < 1 {
colorlog.PrintError("You must provide an org or user to clone")
os.Exit(1)
}
if cmd.Flags().Changed("path") {
absolutePath := ensureTrailingSlash(cmd.Flag("path").Value.String())
os.Setenv("GHORG_ABSOLUTE_PATH_TO_CLONE_TO", absolutePath)
}
if cmd.Flags().Changed("protocol") {
protocol := cmd.Flag("protocol").Value.String()
os.Setenv("GHORG_CLONE_PROTOCOL", protocol)
}
if cmd.Flags().Changed("branch") {
os.Setenv("GHORG_BRANCH", cmd.Flag("branch").Value.String())
}
if cmd.Flags().Changed("bitbucket-username") {
os.Setenv("GHORG_BITBUCKET_USERNAME", cmd.Flag("bitbucket-username").Value.String())
}
if cmd.Flags().Changed("namespace") {
os.Setenv("GHORG_GITLAB_DEFAULT_NAMESPACE", cmd.Flag("namespace").Value.String())
}
if cmd.Flags().Changed("clone-type") {
cloneType := strings.ToLower(cmd.Flag("clone-type").Value.String())
os.Setenv("GHORG_CLONE_TYPE", cloneType)
}
if cmd.Flags().Changed("scm") {
scmType := strings.ToLower(cmd.Flag("scm").Value.String())
os.Setenv("GHORG_SCM_TYPE", scmType)
}
if cmd.Flags().Changed("base-url") {
url := cmd.Flag("base-url").Value.String()
os.Setenv("GHORG_SCM_BASE_URL", url)
}
if cmd.Flags().Changed("concurrency") {
g := cmd.Flag("concurrency").Value.String()
os.Setenv("GHORG_CONCURRENCY", g)
}
if cmd.Flags().Changed("skip-archived") {
os.Setenv("GHORG_SKIP_ARCHIVED", "true")
}
if cmd.Flags().Changed("preserve-dir") {
os.Setenv("GHORG_PRESERVE_DIRECTORY_STRUCTURE", "true")
}
if cmd.Flags().Changed("backup") {
os.Setenv("GHORG_BACKUP", "true")
}
configs.GetOrSetToken()
if cmd.Flags().Changed("token") {
if os.Getenv("GHORG_SCM_TYPE") == "github" {
os.Setenv("GHORG_GITHUB_TOKEN", cmd.Flag("token").Value.String())
} else if os.Getenv("GHORG_SCM_TYPE") == "gitlab" {
os.Setenv("GHORG_GITLAB_TOKEN", cmd.Flag("token").Value.String())
} else if os.Getenv("GHORG_SCM_TYPE") == "bitbucket" {
os.Setenv("GHORG_BITBUCKET_APP_PASSWORD", cmd.Flag("token").Value.String())
}
}
if cmd.Flags().Changed("path") {
absolutePath := ensureTrailingSlash(cmd.Flag("path").Value.String())
os.Setenv("GHORG_ABSOLUTE_PATH_TO_CLONE_TO", absolutePath)
}
err := configs.VerifyTokenSet()
if err != nil {
colorlog.PrintError(err)
os.Exit(1)
}
if cmd.Flags().Changed("protocol") {
protocol := cmd.Flag("protocol").Value.String()
os.Setenv("GHORG_CLONE_PROTOCOL", protocol)
}
err = configs.VerifyConfigsSetCorrectly()
if err != nil {
colorlog.PrintError(err)
os.Exit(1)
}
if cmd.Flags().Changed("branch") {
os.Setenv("GHORG_BRANCH", cmd.Flag("branch").Value.String())
}
parseParentFolder(argz)
args = argz
targetCloneSource = argz[0]
if cmd.Flags().Changed("bitbucket-username") {
os.Setenv("GHORG_BITBUCKET_USERNAME", cmd.Flag("bitbucket-username").Value.String())
}
if cmd.Flags().Changed("namespace") {
os.Setenv("GHORG_GITLAB_DEFAULT_NAMESPACE", cmd.Flag("namespace").Value.String())
}
if cmd.Flags().Changed("clone-type") {
cloneType := strings.ToLower(cmd.Flag("clone-type").Value.String())
os.Setenv("GHORG_CLONE_TYPE", cloneType)
}
if cmd.Flags().Changed("scm") {
scmType := strings.ToLower(cmd.Flag("scm").Value.String())
os.Setenv("GHORG_SCM_TYPE", scmType)
}
if cmd.Flags().Changed("base-url") {
url := cmd.Flag("base-url").Value.String()
os.Setenv("GHORG_SCM_BASE_URL", url)
}
if cmd.Flags().Changed("concurrency") {
g := cmd.Flag("concurrency").Value.String()
os.Setenv("GHORG_CONCURRENCY", g)
}
if cmd.Flags().Changed("skip-archived") {
os.Setenv("GHORG_SKIP_ARCHIVED", "true")
}
if cmd.Flags().Changed("preserve-dir") {
os.Setenv("GHORG_PRESERVE_DIRECTORY_STRUCTURE", "true")
}
if cmd.Flags().Changed("backup") {
os.Setenv("GHORG_BACKUP", "true")
}
configs.GetOrSetToken()
if cmd.Flags().Changed("token") {
if os.Getenv("GHORG_SCM_TYPE") == "github" {
os.Setenv("GHORG_GITHUB_TOKEN", cmd.Flag("token").Value.String())
} else if os.Getenv("GHORG_SCM_TYPE") == "gitlab" {
os.Setenv("GHORG_GITLAB_TOKEN", cmd.Flag("token").Value.String())
} else if os.Getenv("GHORG_SCM_TYPE") == "bitbucket" {
os.Setenv("GHORG_BITBUCKET_APP_PASSWORD", cmd.Flag("token").Value.String())
}
}
err := configs.VerifyTokenSet()
if err != nil {
colorlog.PrintError(err)
os.Exit(1)
}
err = configs.VerifyConfigsSetCorrectly()
if err != nil {
colorlog.PrintError(err)
os.Exit(1)
}
parseParentFolder(argz)
args = argz
CloneAllRepos()
},
CloneAllRepos()
}
// TODO: Figure out how to use go channels for this
func getAllOrgCloneUrls() ([]Repo, error) {
func getAllOrgCloneUrls() ([]repo.Data, error) {
asciiTime()
PrintConfigs()
var repos []Repo
var repos []repo.Data
var err error
switch os.Getenv("GHORG_SCM_TYPE") {
case "github":
repos, err = getGitHubOrgCloneUrls()
repos, err = github.GetOrgRepos(targetCloneSource)
case "gitlab":
repos, err = getGitLabOrgCloneUrls()
repos, err = gitlab.GetOrgRepos(targetCloneSource)
case "bitbucket":
repos, err = getBitBucketOrgCloneUrls()
repos, err = bitbucket.GetOrgRepos(targetCloneSource)
default:
colorlog.PrintError("GHORG_SCM_TYPE not set or unsupported, also make sure its all lowercase")
os.Exit(1)
@ -192,18 +192,18 @@ func getAllOrgCloneUrls() ([]Repo, error) {
}
// TODO: Figure out how to use go channels for this
func getAllUserCloneUrls() ([]Repo, error) {
func getAllUserCloneUrls() ([]repo.Data, error) {
asciiTime()
PrintConfigs()
var repos []Repo
var repos []repo.Data
var err error
switch os.Getenv("GHORG_SCM_TYPE") {
case "github":
repos, err = getGitHubUserCloneUrls()
repos, err = github.GetUserRepos(targetCloneSource)
case "gitlab":
repos, err = getGitLabUserCloneUrls()
repos, err = gitlab.GetUserRepos(targetCloneSource)
case "bitbucket":
repos, err = getBitBucketUserCloneUrls()
repos, err = bitbucket.GetUserRepos(targetCloneSource)
default:
colorlog.PrintError("GHORG_SCM_TYPE not set or unsupported, also make sure its all lowercase")
os.Exit(1)
@ -213,7 +213,7 @@ func getAllUserCloneUrls() ([]Repo, error) {
}
func createDirIfNotExist() {
if _, err := os.Stat(os.Getenv("GHORG_ABSOLUTE_PATH_TO_CLONE_TO") + parentFolder + "_ghorg"); os.IsNotExist(err) {
if _, err := os.Stat(os.Getenv("GHORG_ABSOLUTE_PATH_TO_CLONE_TO") + parentFolder + "_ghorg"); os.IsNotExist(err) {
err = os.MkdirAll(os.Getenv("GHORG_ABSOLUTE_PATH_TO_CLONE_TO"), 0700)
if err != nil {
panic(err)
@ -277,7 +277,7 @@ func readGhorgIgnore() ([]string, error) {
func CloneAllRepos() {
// resc, errc, infoc := make(chan string), make(chan error), make(chan error)
var cloneTargets []Repo
var cloneTargets []repo.Data
var err error
if os.Getenv("GHORG_CLONE_TYPE") == "org" {
@ -296,7 +296,7 @@ func CloneAllRepos() {
}
if len(cloneTargets) == 0 {
colorlog.PrintInfo("No repos found for " + os.Getenv("GHORG_SCM_TYPE") + " " + os.Getenv("GHORG_CLONE_TYPE") + ": " + args[0] + ", check spelling and verify clone-type (user/org) is set correctly e.g. -c=user")
colorlog.PrintInfo("No repos found for " + os.Getenv("GHORG_SCM_TYPE") + " " + os.Getenv("GHORG_CLONE_TYPE") + ": " + targetCloneSource + ", check spelling and verify clone-type (user/org) is set correctly e.g. -c=user")
os.Exit(0)
}
@ -313,7 +313,7 @@ func CloneAllRepos() {
colorlog.PrintInfo("Using ghorgignore, filtering repos down...")
filteredCloneTargets := []Repo{}
filteredCloneTargets := []repo.Data{}
var flag bool
for _, cloned := range cloneTargets {
flag = false
@ -332,7 +332,7 @@ func CloneAllRepos() {
}
colorlog.PrintInfo(strconv.Itoa(len(cloneTargets)) + " repos found in " + args[0])
colorlog.PrintInfo(strconv.Itoa(len(cloneTargets)) + " repos found in " + targetCloneSource)
fmt.Println()
createDirIfNotExist()

View File

@ -1,19 +1,21 @@
package cmd
package bitbucket
import (
"github.com/gabrie30/ghorg/internal/repo"
bitbucket "github.com/ktrysmt/go-bitbucket"
"os"
)
func getBitBucketOrgCloneUrls() ([]Repo, error) {
// GetOrgRepos gets org repos
func GetOrgRepos(targetOrg string) ([]repo.Data, error) {
client := bitbucket.NewBasicAuth(os.Getenv("GHORG_BITBUCKET_USERNAME"), os.Getenv("GHORG_BITBUCKET_APP_PASSWORD"))
cloneData := []Repo{}
cloneData := []repo.Data{}
resp, err := client.Teams.Repositories(args[0])
resp, err := client.Teams.Repositories(targetOrg)
if err != nil {
return []Repo{}, err
return []repo.Data{}, err
}
values := resp.(map[string]interface{})["values"].([]interface{})
if err != nil {
@ -25,7 +27,7 @@ func getBitBucketOrgCloneUrls() ([]Repo, error) {
for _, l := range links {
link := l.(map[string]interface{})["href"]
linkType := l.(map[string]interface{})["name"]
r := Repo{}
r := repo.Data{}
if os.Getenv("GHORG_CLONE_PROTOCOL") == "ssh" && linkType == "ssh" {
r.URL = link.(string)
r.CloneURL = link.(string)
@ -41,14 +43,15 @@ func getBitBucketOrgCloneUrls() ([]Repo, error) {
return cloneData, nil
}
func getBitBucketUserCloneUrls() ([]Repo, error) {
// GetUserRepos gets user repos from bitbucket
func GetUserRepos(targetUser string) ([]repo.Data, error) {
client := bitbucket.NewBasicAuth(os.Getenv("GHORG_BITBUCKET_USERNAME"), os.Getenv("GHORG_BITBUCKET_APP_PASSWORD"))
cloneData := []Repo{}
cloneData := []repo.Data{}
resp, err := client.Users.Repositories(args[0])
resp, err := client.Users.Repositories(targetUser)
if err != nil {
return []Repo{}, err
return []repo.Data{}, err
}
values := resp.(map[string]interface{})["values"].([]interface{})
if err != nil {
@ -61,7 +64,7 @@ func getBitBucketUserCloneUrls() ([]Repo, error) {
link := l.(map[string]interface{})["href"]
linkType := l.(map[string]interface{})["name"]
r := Repo{}
r := repo.Data{}
if os.Getenv("GHORG_CLONE_PROTOCOL") == "ssh" && linkType == "ssh" {
r.URL = link.(string)
r.CloneURL = link.(string)

View File

@ -1,14 +1,17 @@
package cmd
package github
import (
"context"
"os"
"strings"
"github.com/gabrie30/ghorg/internal/repo"
"github.com/google/go-github/github"
"golang.org/x/oauth2"
)
func getGitHubOrgCloneUrls() ([]Repo, error) {
// GetOrgRepos gets org repos
func GetOrgRepos(targetOrg string) ([]repo.Data, error) {
ctx := context.Background()
ts := oauth2.StaticTokenSource(
@ -25,7 +28,7 @@ func getGitHubOrgCloneUrls() ([]Repo, error) {
// get all pages of results
var allRepos []*github.Repository
for {
repos, resp, err := client.Repositories.ListByOrg(context.Background(), args[0], opt)
repos, resp, err := client.Repositories.ListByOrg(context.Background(), targetOrg, opt)
if err != nil {
return nil, err
@ -36,23 +39,23 @@ func getGitHubOrgCloneUrls() ([]Repo, error) {
}
opt.Page = resp.NextPage
}
cloneData := []Repo{}
cloneData := []repo.Data{}
for _, repo := range allRepos {
r := Repo{}
for _, ghRepo := range allRepos {
r := repo.Data{}
if os.Getenv("GHORG_SKIP_ARCHIVED") == "true" {
if *repo.Archived == true {
if *ghRepo.Archived == true {
continue
}
}
if os.Getenv("GHORG_CLONE_PROTOCOL") == "https" {
r.CloneURL = addTokenToHTTPSCloneURL(*repo.CloneURL, os.Getenv("GHORG_GITHUB_TOKEN"))
r.URL = *repo.CloneURL
r.CloneURL = addTokenToHTTPSCloneURL(*ghRepo.CloneURL, os.Getenv("GHORG_GITHUB_TOKEN"))
r.URL = *ghRepo.CloneURL
cloneData = append(cloneData, r)
} else {
r.CloneURL = *repo.SSHURL
r.URL = *repo.SSHURL
r.CloneURL = *ghRepo.SSHURL
r.URL = *ghRepo.SSHURL
cloneData = append(cloneData, r)
}
}
@ -60,8 +63,8 @@ func getGitHubOrgCloneUrls() ([]Repo, error) {
return cloneData, nil
}
// TODO: refactor with getAllOrgCloneUrls
func getGitHubUserCloneUrls() ([]Repo, error) {
// GetUserRepos gets user repos
func GetUserRepos(targetUser string) ([]repo.Data, error) {
ctx := context.Background()
ts := oauth2.StaticTokenSource(
@ -78,7 +81,7 @@ func getGitHubUserCloneUrls() ([]Repo, error) {
// get all pages of results
var allRepos []*github.Repository
for {
repos, resp, err := client.Repositories.List(context.Background(), args[0], opt)
repos, resp, err := client.Repositories.List(context.Background(), targetUser, opt)
if err != nil {
return nil, err
@ -89,26 +92,31 @@ func getGitHubUserCloneUrls() ([]Repo, error) {
}
opt.Page = resp.NextPage
}
repoData := []Repo{}
repoData := []repo.Data{}
for _, repo := range allRepos {
for _, ghRepo := range allRepos {
if os.Getenv("GHORG_SKIP_ARCHIVED") == "true" {
if *repo.Archived == true {
if *ghRepo.Archived == true {
continue
}
}
r := Repo{}
r := repo.Data{}
if os.Getenv("GHORG_CLONE_PROTOCOL") == "https" {
r.CloneURL = addTokenToHTTPSCloneURL(*repo.CloneURL, os.Getenv("GHORG_GITHUB_TOKEN"))
r.URL = *repo.CloneURL
r.CloneURL = addTokenToHTTPSCloneURL(*ghRepo.CloneURL, os.Getenv("GHORG_GITHUB_TOKEN"))
r.URL = *ghRepo.CloneURL
repoData = append(repoData, r)
} else {
r.CloneURL = *repo.SSHURL
r.URL = *repo.SSHURL
r.CloneURL = *ghRepo.SSHURL
r.URL = *ghRepo.SSHURL
repoData = append(repoData, r)
}
}
return repoData, nil
}
func addTokenToHTTPSCloneURL(url string, token string) string {
splitURL := strings.Split(url, "https://")
return "https://" + token + "@" + splitURL[1]
}

View File

@ -1,4 +1,4 @@
package cmd
package gitlab
import (
"fmt"
@ -6,11 +6,14 @@ import (
"strings"
"github.com/gabrie30/ghorg/colorlog"
"github.com/gabrie30/ghorg/internal/repo"
gitlab "github.com/xanzy/go-gitlab"
)
func getGitLabOrgCloneUrls() ([]Repo, error) {
repoData := []Repo{}
// GetOrgRepos fetches repo data
func GetOrgRepos(targetOrg string) ([]repo.Data, error) {
repoData := []repo.Data{}
client, err := determineClient()
if err != nil {
@ -34,11 +37,11 @@ func getGitLabOrgCloneUrls() ([]Repo, error) {
for {
// Get the first page with projects.
ps, resp, err := client.Groups.ListGroupProjects(args[0], opt)
ps, resp, err := client.Groups.ListGroupProjects(targetOrg, opt)
if err != nil {
// TODO: check if 404, then we know group does not exist
return []Repo{}, err
return []repo.Data{}, err
}
// List all the projects we've found so far.
@ -58,7 +61,7 @@ func getGitLabOrgCloneUrls() ([]Repo, error) {
continue
}
}
r := Repo{}
r := repo.Data{}
r.Path = p.PathWithNamespace
if os.Getenv("GHORG_CLONE_PROTOCOL") == "https" {
@ -96,8 +99,8 @@ func determineClient() (*gitlab.Client, error) {
return gitlab.NewClient(token)
}
func getGitLabUserCloneUrls() ([]Repo, error) {
cloneData := []Repo{}
func GetUserRepos(targetUsername string) ([]repo.Data, error) {
cloneData := []repo.Data{}
client, err := determineClient()
@ -114,10 +117,10 @@ func getGitLabUserCloneUrls() ([]Repo, error) {
for {
// Get the first page with projects.
ps, resp, err := client.Projects.ListUserProjects(args[0], opt)
ps, resp, err := client.Projects.ListUserProjects(targetUsername, opt)
if err != nil {
// TODO: check if 404, then we know user does not exist
return []Repo{}, err
return []repo.Data{}, err
}
// List all the projects we've found so far.
@ -128,7 +131,7 @@ func getGitLabUserCloneUrls() ([]Repo, error) {
continue
}
}
r := Repo{}
r := repo.Data{}
r.Path = p.PathWithNamespace
if os.Getenv("GHORG_CLONE_PROTOCOL") == "https" {
r.CloneURL = addTokenToHTTPSCloneURL(p.HTTPURLToRepo, os.Getenv("GHORG_GITLAB_TOKEN"))
@ -152,3 +155,8 @@ func getGitLabUserCloneUrls() ([]Repo, error) {
return cloneData, nil
}
func addTokenToHTTPSCloneURL(url string, token string) string {
splitURL := strings.Split(url, "https://")
return "https://oauth2:" + token + "@" + splitURL[1]
}

10
internal/repo/repo.go Normal file
View File

@ -0,0 +1,10 @@
// Package repo holds data for a repo
package repo
// Data represents an SCM repo
type Data struct {
Name string
Path string
URL string
CloneURL string
}