Compare commits
9 Commits
Author | SHA1 | Date |
---|---|---|
|
1fd918e5e3 | |
|
b7b96ce1b7 | |
|
1a4a897b6e | |
|
249e90e00c | |
|
657f09c93d | |
|
09d8e17937 | |
|
4c254a2c23 | |
|
1d212795a6 | |
|
b37b0b1c97 |
|
@ -6,17 +6,25 @@
|
|||
### 功能特点
|
||||
- [简单] 可自动在对应平台创建同名仓库并上传
|
||||
- [可靠] 可选择是否本地覆盖远程(如果同名仓库在远端已存在)
|
||||
- [灵活] 可通过多种方式导入仓库源(目录、文本列表)
|
||||
- [方便] 可选择上传到 Gitee 组织或者新建组织上传
|
||||
- [灵活] 可通过多种方式导入仓库源(目录、文本列表、Github 源)
|
||||
- [快速] 并发5个同时操作(各平台均有限制,5个并发可保证成功率及速度)
|
||||
|
||||
### 注意事项
|
||||
- 工具会自动生成临时的一个 Remote `up2GitX-{timestamp}` 进行推送,不会影响现有的 Remote 配置
|
||||
- 工具会调用OAuth授权进行仓库的创建,后续可以在第三方应用管理取消授权
|
||||
- 工具在进行处理的过程中会对特殊情况进行交互,请仔细阅读并慎重选择
|
||||
- Github 的镜像操作会生成临时目录来存储这些临时的裸仓库,同步完成后,自行删除即可
|
||||
|
||||
### 设计流程
|
||||
|
||||

|
||||
|
||||
### 使用简介
|
||||
|
||||
[下载](https://gitee.com/kesin/up2GitX/releases) 对应平台的二进制包,可直接运行
|
||||
下载对应平台的二进制包,可直接运行
|
||||
- [up2-macos-v1.1.0.zip](https://gitee.com/kesin/up2GitX/attach_files/432062/download)
|
||||
- [up2-linux-v1.1.0.zip](https://gitee.com/kesin/up2GitX/attach_files/432063/download)
|
||||
|
||||
以 Gitee 为例 `./up2 gitee -h`
|
||||
|
||||
|
@ -32,31 +40,38 @@ Global Options:
|
|||
--no-color Disable color when outputting message
|
||||
-h, --help Display this help information
|
||||
|
||||
|
||||
Arguments:
|
||||
repoSource Tell me which repo dir or list your want to sync, is required
|
||||
token Provide platform token for skip api rate limit
|
||||
|
||||
Examples:
|
||||
Using dir: ./up2 gitee /Zoker/repos/
|
||||
Dir example
|
||||
$ ls -l /Zoker/repos/
|
||||
drwxr-xr-x 4 zoker 128B Jun 1 19:05 git-work-repo1
|
||||
drwxr-xr-x 4 zoker 128B Jun 1 19:02 taskover
|
||||
drwxr-xr-x 4 zoker 128B Jun 1 19:03 blogine
|
||||
drwxr-xr-x 3 zoker 96B Jun 1 12:15 git-bare-repo3
|
||||
...
|
||||
$ ls -l /Zoker/repos/
|
||||
drwxr-xr-x 4 zoker 128B Jun 1 19:05 git-work-repo1
|
||||
drwxr-xr-x 4 zoker 128B Jun 1 19:02 taskover
|
||||
drwxr-xr-x 4 zoker 128B Jun 1 19:03 blogine
|
||||
drwxr-xr-x 3 zoker 96B Jun 1 12:15 git-bare-repo3
|
||||
...
|
||||
|
||||
Using file: ./up2 gitee /Zoker/repos.list
|
||||
File example
|
||||
$ cat /Zoker/repos.list
|
||||
/tmp/repos/git-work-repo1
|
||||
/Zoker/workspace/git-work-repo2
|
||||
/other/path/to/git-bare-repo3
|
||||
...
|
||||
$ cat /Zoker/repos.list
|
||||
/tmp/repos/git-work-repo1
|
||||
/Zoker/workspace/git-work-repo2
|
||||
/other/path/to/git-bare-repo3
|
||||
...
|
||||
|
||||
Using Source: ./up2 gitee github:zoker YOUR_TOKEN_HERE(OPTIONAL)
|
||||
Support import from Github source, replace {zoker} with your expected Github path
|
||||
It better to provide your own access_token to avoid api rate limit, eg on Github: https://github.com/settings/tokens
|
||||
Alert: Only Github source and public project supported, other platform like Gitlab, Bitbucket will be added later
|
||||
```
|
||||
|
||||
### 使用示例
|
||||
|
||||
这里以上传到 Gitee 为例
|
||||
这里以上传到 Gitee 为例([镜像 Github 空间到 Gitee 示例](https://gitee.com/kesin/up2GitX/releases/v1.1.0))
|
||||
|
||||
#### 1、指定 RepoSource
|
||||
|
||||
|
@ -68,9 +83,9 @@ Examples:
|
|||
|
||||
#### 2、输入 Gitee 账号信息并选择同步到哪个 Namespace
|
||||
|
||||

|
||||

|
||||
|
||||
输入注册 Gitee 所用的邮箱以及密码,认证成功后会列出可用的 Namespace,在 Gitee 上,它可以是个人名下、组织名下或者企业名下。(目前仅支持上传到个人名下,后续接口功能到位会支持组织和企业)
|
||||
输入注册 Gitee 所用的邮箱以及密码,认证成功后会列出可用的 Namespace,你也可以选择新建一个组织。在 Gitee 上,它可以是个人名下、组织名下或者企业名下。(目前仅支持上传到个人和组织,后续接口功能到位会支持企业)
|
||||
|
||||
#### 3、确认将要生成的仓库链接并选择公开属性
|
||||
|
||||
|
|
1
go.mod
1
go.mod
|
@ -3,6 +3,7 @@ module up2GitX
|
|||
go 1.14
|
||||
|
||||
require (
|
||||
github.com/bitly/go-simplejson v0.5.0
|
||||
github.com/go-git/go-git/v5 v5.1.0
|
||||
github.com/gookit/color v1.2.5
|
||||
github.com/gookit/gcli/v2 v2.2.1
|
||||
|
|
2
go.sum
2
go.sum
|
@ -1,6 +1,8 @@
|
|||
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||
github.com/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkNEM+Y=
|
||||
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
|
||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
|
|
|
@ -3,6 +3,7 @@ package platform
|
|||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
@ -13,10 +14,11 @@ import (
|
|||
"github.com/gookit/gcli/v2/interact"
|
||||
"github.com/gookit/gcli/v2/progress"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/transport/http"
|
||||
"github.com/bitly/go-simplejson"
|
||||
)
|
||||
|
||||
type RepoResult struct {
|
||||
local string
|
||||
source string
|
||||
uri string
|
||||
status int
|
||||
error string
|
||||
|
@ -52,10 +54,18 @@ func GiteeCommand() *gcli.Command {
|
|||
/tmp/repos/git-work-repo1
|
||||
/Zoker/workspace/git-work-repo2
|
||||
/other/path/to/git-bare-repo3
|
||||
...`}
|
||||
...
|
||||
|
||||
<yellow>Using Source: </> <cyan>{$binName} {$cmd} github:zoker </><gray>YOUR_TOKEN_HERE(OPTIONAL)</>
|
||||
Support import from Github source, replace {zoker} with your expected Github path
|
||||
<blue>It better to provide your own access_token to avoid api rate limit, eg on Github: https://github.com/settings/tokens</>
|
||||
Alert: Only Github source and public project supported, other platform like Gitlab, Bitbucket will be added later
|
||||
|
||||
`}
|
||||
|
||||
// bind args with names
|
||||
gitee.AddArg("repoSource", "Tell me which repo dir or list your want to sync, is required", false)
|
||||
gitee.AddArg("token", "Provide platform token for skip api rate limit", false)
|
||||
|
||||
return gitee
|
||||
}
|
||||
|
@ -68,7 +78,7 @@ func syncGitee(c *gcli.Command, args []string) error {
|
|||
}
|
||||
|
||||
// check repodir and print projects to ensure
|
||||
repos := share.ReadyToAuth(args[0])
|
||||
repos, source, orgPath := share.ReadyToAuth(args, c.Name)
|
||||
if repos == nil {
|
||||
return nil
|
||||
}
|
||||
|
@ -90,7 +100,7 @@ func syncGitee(c *gcli.Command, args []string) error {
|
|||
color.Green.Printf("\nHello, %s! \n\n", userInfo["name"])
|
||||
|
||||
// get available namespace todo: enterprise and group
|
||||
allNamespace := getNamespace(userInfo)
|
||||
allNamespace := getNamespace(userInfo, accessToken)
|
||||
namespace := make([]string, len(allNamespace))
|
||||
for i, n := range allNamespace {
|
||||
namespace[i] = n[1]
|
||||
|
@ -98,8 +108,17 @@ func syncGitee(c *gcli.Command, args []string) error {
|
|||
selectedNumber := askNamespace(namespace)
|
||||
numberD, _ := strconv.Atoi(selectedNumber)
|
||||
|
||||
// select namespace and ask for ensure
|
||||
selectedNp := allNamespace[numberD]
|
||||
var selectedNp []string
|
||||
if numberD == 0 {
|
||||
// create a new group
|
||||
selectedNp = createGroup(accessToken)
|
||||
if selectedNp == nil {
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
// select namespace and ask for ensure
|
||||
selectedNp = allNamespace[numberD]
|
||||
}
|
||||
color.Notice.Printf("\nSelected %s(https://gitee.com/%s) as namespace, Type: %s \n" +
|
||||
"The following projects will be generated on Gitee: \n\n", selectedNp[0], selectedNp[1], selectedNp[2])
|
||||
|
||||
|
@ -142,8 +161,20 @@ func syncGitee(c *gcli.Command, args []string) error {
|
|||
}
|
||||
|
||||
// sync code
|
||||
fmt.Println("\n", "Syncing Projects to Gitee, Please Wait...")
|
||||
syncRes := multiSync(avaiRepo, auth, asExi)
|
||||
var tmpDir string
|
||||
if source == "local" {
|
||||
fmt.Println("\n", "Syncing Projects to Gitee, Please Wait...")
|
||||
} else {
|
||||
currentDir, _ := os.Getwd()
|
||||
tmpDir = fmt.Sprintf("%s/up2GitX-%s-%s", currentDir, source, orgPath)
|
||||
if err := os.MkdirAll(tmpDir, 0755); err !=nil {
|
||||
color.Red.Println(err.Error())
|
||||
return nil
|
||||
}
|
||||
fmt.Printf("\nA tmp repo dir `%s` created for tmp repositories, you can remove it after sync successed\n\n", tmpDir)
|
||||
fmt.Println("Cloning and Uploading Projects to Gitee, Please Wait...")
|
||||
}
|
||||
syncRes := multiSync(avaiRepo, auth, asExi, tmpDir)
|
||||
showSyncRes(syncRes)
|
||||
return nil
|
||||
}
|
||||
|
@ -154,18 +185,19 @@ func askForAccount() (string, bool, *http.BasicAuth) {
|
|||
if len(email) == 0 || len(password) == 0 {
|
||||
return "Email or Password must be provided!", false, nil
|
||||
} else {
|
||||
// replace your client_id and client_secret from Gitee
|
||||
params := fmt.Sprintf(`{
|
||||
"grant_type": "password",
|
||||
"username": "%s",
|
||||
"password": "%s",
|
||||
"client_id": "xxxx", // client id from Gitee
|
||||
"client_secret": "xxxx", // client secret from Gitee
|
||||
"client_id": "xxxx",
|
||||
"client_secret": "xxxx",
|
||||
"scope": "user_info projects groups enterprises"
|
||||
}`, email, password)
|
||||
|
||||
var paramsJson map[string]interface{}
|
||||
json.Unmarshal([]byte(params), ¶msJson)
|
||||
result, err := share.Post("https://gitee.com/oauth/token", paramsJson)
|
||||
result, err := share.PostForm("https://gitee.com/oauth/token", paramsJson)
|
||||
|
||||
if err != nil {
|
||||
return err.Error(), false, nil
|
||||
|
@ -208,16 +240,56 @@ func filterResult(result map[string]interface{}, key string) (string, bool) {
|
|||
}
|
||||
|
||||
// todo enable select group and enterprise
|
||||
func getNamespace(userInfo map [string]string) [][]string {
|
||||
namespace := make([][]string, 1)
|
||||
namespace[0] = make([]string, 4)
|
||||
namespace[0][0] = userInfo["name"]
|
||||
namespace[0][1] = userInfo["username"]
|
||||
namespace[0][2] = "Personal"
|
||||
namespace[0][3] = "0"
|
||||
func getNamespace(userInfo map [string]string, token string) [][]string {
|
||||
var namespace [][]string
|
||||
newOrg := []string{"Create a new Group", "Create a new Group", ""}
|
||||
owner := []string{userInfo["name"], userInfo["username"], "Personal"}
|
||||
namespace = append(namespace, newOrg, owner)
|
||||
|
||||
orgUrl := fmt.Sprintf("https://gitee.com/api/v5/user/orgs?access_token=%s&per_page=100", token)
|
||||
if result, _, err := share.GetByte(orgUrl); err == nil {
|
||||
orgRes, _ := simplejson.NewJson([]byte(result))
|
||||
serOrg, _ := orgRes.Array()
|
||||
for _, org := range serOrg {
|
||||
orgA := org.(map[string]interface{})
|
||||
orgName := orgA["login"].(string)
|
||||
namespace = append(namespace, []string{orgName, orgName, "Group"})
|
||||
}
|
||||
}
|
||||
|
||||
return namespace
|
||||
}
|
||||
|
||||
func createGroup(token string) []string {
|
||||
orgPath, _ := interact.ReadLine("\nPlease enter your new Group path(eg: zoker, zoker123, osc123): ")
|
||||
if len(orgPath) == 0 {
|
||||
color.Red.Println("Group path must be provided!")
|
||||
} else {
|
||||
orgUrl := "https://gitee.com/api/v5/users/organization"
|
||||
params := fmt.Sprintf(`{
|
||||
"access_token": "%s",
|
||||
"name": "%s",
|
||||
"org": "%s"
|
||||
}`, token, orgPath, orgPath)
|
||||
var paramsJson map[string]interface{}
|
||||
json.Unmarshal([]byte(params), ¶msJson)
|
||||
|
||||
if result, _, err := share.PostFormByte(orgUrl, paramsJson); err == nil {
|
||||
orgRes, _ := simplejson.NewJson([]byte(result))
|
||||
login, _ := orgRes.Get("login").String()
|
||||
if login == orgPath {
|
||||
color.Green.Printf("\nGroup https://gitee.com/%s has been successfully Created!\n", orgPath)
|
||||
return []string{orgPath, orgPath, "Group"}
|
||||
} else {
|
||||
color.Red.Println(orgRes)
|
||||
}
|
||||
} else {
|
||||
color.Red.Println(err.Error())
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func askNamespace(namespace []string) string {
|
||||
np := interact.SelectOne(
|
||||
"Please select which namespace you want to put this repositories: ",
|
||||
|
@ -282,7 +354,7 @@ func createProject(path string, public string, token string, np []string, repoRe
|
|||
uri = fmt.Sprintf("https://gitee.com/%s/%s.git", np[1], repoPath)
|
||||
}
|
||||
mutex.Lock()
|
||||
*repoRes = append(*repoRes, RepoResult{local: path, uri: uri, status: eType, error: errMsg})
|
||||
*repoRes = append(*repoRes, RepoResult{source: path, uri: uri, status: eType, error: errMsg})
|
||||
mutex.Unlock()
|
||||
}
|
||||
|
||||
|
@ -339,7 +411,7 @@ func printRepo(repoRes []RepoResult, status int) int {
|
|||
} else {
|
||||
result = item.uri
|
||||
}
|
||||
p = fmt.Sprintf("Dir: (%s)\n Status: %s\n Result: ", item.local, repoStatus)
|
||||
p = fmt.Sprintf("Source: (%s)\n Status: %s\n Result: ", item.source, repoStatus)
|
||||
colorRepo(status, p)
|
||||
colorResult(status, result)
|
||||
fmt.Printf(SPL)
|
||||
|
@ -362,7 +434,7 @@ func printSync(syncRes []RepoResult, status int) {
|
|||
} else {
|
||||
result = item.error
|
||||
}
|
||||
p = fmt.Sprintf("Dir: (%s)\n Gitee: %s\n Result: ", item.local, item.uri)
|
||||
p = fmt.Sprintf("Source: (%s)\n Gitee: %s\n Result: ", item.source, item.uri)
|
||||
colorRepo(EXIST, p)
|
||||
colorResult(item.status, result)
|
||||
fmt.Printf(SPL)
|
||||
|
@ -411,7 +483,7 @@ func colorResult(status int, p string) {
|
|||
}
|
||||
}
|
||||
|
||||
func multiSync(avaiRepo []RepoResult, auth *http.BasicAuth, force string) (syncRes []RepoResult) {
|
||||
func multiSync(avaiRepo []RepoResult, auth *http.BasicAuth, force string, tmpDir string) (syncRes []RepoResult) {
|
||||
step := progress.Bar(len(avaiRepo))
|
||||
var wg sync.WaitGroup
|
||||
var mutex = &sync.Mutex{}
|
||||
|
@ -421,7 +493,7 @@ func multiSync(avaiRepo []RepoResult, auth *http.BasicAuth, force string) (syncR
|
|||
wg.Add(len(avaiRepo))
|
||||
|
||||
for w := 1; w <= WORKER; w++ {
|
||||
go multiSyncWorker(avais, auth, force, &syncRes, &wg, mutex, step)
|
||||
go multiSyncWorker(avais, auth, force, &syncRes, &wg, mutex, step, tmpDir)
|
||||
}
|
||||
|
||||
for _, p := range avaiRepo {
|
||||
|
@ -436,9 +508,9 @@ func multiSync(avaiRepo []RepoResult, auth *http.BasicAuth, force string) (syncR
|
|||
return syncRes
|
||||
}
|
||||
|
||||
func multiSyncWorker(avais chan RepoResult, auth *http.BasicAuth, force string, syncRes *[]RepoResult, wg *sync.WaitGroup, mutex *sync.Mutex, step *progress.Progress) {
|
||||
func multiSyncWorker(avais chan RepoResult, auth *http.BasicAuth, force string, syncRes *[]RepoResult, wg *sync.WaitGroup, mutex *sync.Mutex, step *progress.Progress, tmpDir string) {
|
||||
for item := range avais {
|
||||
err := share.SyncRepo(auth, item.local, item.uri, force)
|
||||
err := share.SyncRepo(auth, item.source, item.uri, force, tmpDir)
|
||||
if err != nil {
|
||||
item.status = ERROR
|
||||
item.error = err.Error()
|
||||
|
|
225
share/tools.go
225
share/tools.go
|
@ -7,9 +7,9 @@ import (
|
|||
"io"
|
||||
"io/ioutil"
|
||||
NetHttp "net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
@ -19,6 +19,7 @@ import (
|
|||
"github.com/go-git/go-git/v5"
|
||||
"github.com/go-git/go-git/v5/config"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/transport/http"
|
||||
"github.com/bitly/go-simplejson"
|
||||
)
|
||||
|
||||
type RepoLocal struct {
|
||||
|
@ -34,7 +35,7 @@ const (
|
|||
|
||||
func InvalidAlert(platform string) {
|
||||
fmt.Printf("Tell me which repos source your want to sync, Usage: ")
|
||||
color.Yellow.Printf("up2 %s /Users/Zoker/repos/ or up2 %s /Users/Zoker/repo.txt\n", platform, platform)
|
||||
color.Yellow.Printf("up2 %s /Users/Zoker/repos/ or up2 %s /Users/Zoker/repo.txt or up2 %s github:kesin\n", platform, platform, platform)
|
||||
fmt.Printf("See 'up2 %s -h' for more details\n", platform)
|
||||
}
|
||||
|
||||
|
@ -111,13 +112,12 @@ func isGitRepo(repoPath string) (isGit bool) {
|
|||
}
|
||||
}
|
||||
|
||||
func printRepos(repos []string) {
|
||||
color.Yellow.Println(len(repos), "repositories detected, please check below: ", "\n")
|
||||
func printRepos(repos []RepoLocal) {
|
||||
color.Yellow.Printf("\n%d repositories detected, please check below:\n\n", len(repos))
|
||||
|
||||
alertFlag := false
|
||||
reposLocal := getRepoLocal(repos)
|
||||
|
||||
for i, repo := range reposLocal {
|
||||
for i, repo := range repos {
|
||||
i = i + 1
|
||||
p := fmt.Sprintf("%d. %s", i, repo.path)
|
||||
fmt.Printf(p)
|
||||
|
@ -153,6 +153,43 @@ func getRepoLocal(repos []string) (reposLocal []RepoLocal) {
|
|||
return reposLocal
|
||||
}
|
||||
|
||||
func getRepoApi(repoDir, tokenFix string) (reposLocal []RepoLocal, repos []string) {
|
||||
var orgType string
|
||||
var repoArray []map[string]interface{}
|
||||
orgPath := repoDir[7:]
|
||||
platform := repoDir[:6]
|
||||
|
||||
// check path type
|
||||
orgType = getGithubType(orgPath, tokenFix)
|
||||
if orgType == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
color.Green.Printf("Selected %s, type is %s, Start to fetch repository list...\n", orgPath, orgType)
|
||||
|
||||
// get repos
|
||||
getGithubRepo(orgPath, orgType, &repoArray, "", tokenFix)
|
||||
|
||||
count := len(repoArray)
|
||||
if count == 0 {
|
||||
color.Yellow.Printf("No repositories found in https://github.com/%s\nPlease check if path is valid\n", orgPath)
|
||||
return nil, nil
|
||||
} else { // TODO optimize code with local repo and struct repos
|
||||
for _, item := range repoArray {
|
||||
outOf1G := false
|
||||
size, _ := item["size"].(json.Number).Int64()
|
||||
if size > 1024*1024 {
|
||||
outOf1G = true
|
||||
}
|
||||
sizeMB := float32(size) / 1024.0
|
||||
repoPath := fmt.Sprintf("https://%s.com/%s/%s", platform, orgPath, item["name"].(string))
|
||||
reposLocal = append(reposLocal, RepoLocal{path: repoPath, sizeM: sizeMB, alert: outOf1G})
|
||||
repos = append(repos, repoPath)
|
||||
}
|
||||
return reposLocal, repos
|
||||
}
|
||||
}
|
||||
|
||||
func getRepoItemWorker(paths <- chan string, wp *sync.WaitGroup, reposLocal *[]RepoLocal, mutex *sync.Mutex) {
|
||||
for path := range paths {
|
||||
size, outAlert, _ := repoSize(path)
|
||||
|
@ -163,6 +200,75 @@ func getRepoItemWorker(paths <- chan string, wp *sync.WaitGroup, reposLocal *[]R
|
|||
}
|
||||
}
|
||||
|
||||
func errGithub(body *simplejson.Json) bool {
|
||||
mesg, err := body.Get("message").String()
|
||||
if err != nil {
|
||||
return false
|
||||
} else {
|
||||
color.Red.Println(mesg)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func getGithubType(orgPath, tokenFix string) string {
|
||||
typeUrl := fmt.Sprintf("https://api.github.com/users/%s?from=up2Gitx%s", orgPath, tokenFix)
|
||||
result, _, err := GetByte(typeUrl)
|
||||
if err != nil {
|
||||
color.Red.Printf("Request Type error: %s \n", err.Error())
|
||||
return ""
|
||||
}
|
||||
|
||||
typeRes, _ := simplejson.NewJson([]byte(result))
|
||||
errGithub(typeRes)
|
||||
|
||||
typeStr, _ := typeRes.Get("type").String()
|
||||
if typeStr == "User" {
|
||||
return "users"
|
||||
} else if typeStr == "Organization" {
|
||||
return "orgs"
|
||||
} else {
|
||||
color.Red.Printf("%s not exists, please enter a valid path!\n", orgPath)
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func getGithubRepo(orgPath string, orgType string, repoArray *[]map[string]interface{}, url string, tokenFix string) {
|
||||
var apiUrl string
|
||||
if url != "" {
|
||||
apiUrl = url
|
||||
} else {
|
||||
apiUrl = fmt.Sprintf("https://api.github.com/%s/%s/repos?per_page=20%s", orgType, orgPath, tokenFix)
|
||||
}
|
||||
fmt.Printf("Fetching %s \n", apiUrl)
|
||||
|
||||
result, header, err := GetByte(apiUrl)
|
||||
if err != nil {
|
||||
color.Red.Printf("Request Repositories error: %s \n", err.Error())
|
||||
}
|
||||
|
||||
jsonRes, _ := simplejson.NewJson([]byte(result))
|
||||
errGithub(jsonRes)
|
||||
|
||||
// TODO optimize type assertion remove for
|
||||
tmpRepos, _ := jsonRes.Array()
|
||||
for _, re := range tmpRepos {
|
||||
*repoArray = append(*repoArray, re.(map[string]interface {}))
|
||||
}
|
||||
|
||||
pageLink := header.Get("link")
|
||||
if pageLink != "" {
|
||||
link := strings.Split(pageLink, ",")
|
||||
for _, l := range link {
|
||||
kv := strings.Split(l, ";")
|
||||
if strings.Contains(kv[1], "next") {
|
||||
getGithubRepo(orgPath, orgType, repoArray, kv[0][1 : len(kv[0])-1], "")
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func repoSize(path string) (float32, bool, error) {
|
||||
var size int64
|
||||
err := filepath.Walk(path, func(_ string, info os.FileInfo, err error) error {
|
||||
|
@ -180,16 +286,44 @@ func repoSize(path string) (float32, bool, error) {
|
|||
return sizeMB, outOf1G, err
|
||||
}
|
||||
|
||||
func ReadyToAuth(repoDir string) []string {
|
||||
func ReadyToAuth(args []string, platform string) ([]string, string, string) {
|
||||
repoDir := args[0]
|
||||
|
||||
// if up to gitee from github|gitlab|bitbucket...
|
||||
if platform == "gitee" {
|
||||
reg, err := regexp.Compile(`^github:.+`)
|
||||
if err == nil && reg.MatchString(repoDir) {
|
||||
var tokenFix string
|
||||
if len(args) == 2 && args[1] != "" {
|
||||
tokenFix = fmt.Sprintf("&access_token=%s", args[1])
|
||||
}
|
||||
reposLocal, repos := getRepoApi(repoDir, tokenFix)
|
||||
if repos != nil {
|
||||
printRepos(reposLocal)
|
||||
if len(repos) > 1000 {
|
||||
color.Warn.Println("\nWarning: Gitee only support 1000 projects for each user, some of projects will not sync as expect")
|
||||
}
|
||||
inPut, _ := interact.ReadLine("\nCheck if this repositories are what you expected, ready to the next step? (y/n) ")
|
||||
if inPut == "y" {
|
||||
return repos, repoDir[:6], repoDir[7:]
|
||||
} else {
|
||||
ExitMessage()
|
||||
}
|
||||
}
|
||||
return nil, "", ""
|
||||
}
|
||||
}
|
||||
|
||||
if FileExists(repoDir) {
|
||||
repos, _ := GetGitDir(repoDir)
|
||||
if len(repos) == 0 {
|
||||
color.Red.Printf("No git repositories detected in %s \n", repoDir)
|
||||
} else {
|
||||
printRepos(repos)
|
||||
reposLocal := getRepoLocal(repos)
|
||||
printRepos(reposLocal)
|
||||
inPut, _ := interact.ReadLine("\nCheck if this repositories are what you expected, ready to the next step? (y/n) ")
|
||||
if inPut == "y" {
|
||||
return repos
|
||||
return repos, "local", ""
|
||||
} else {
|
||||
ExitMessage()
|
||||
}
|
||||
|
@ -197,35 +331,28 @@ func ReadyToAuth(repoDir string) []string {
|
|||
} else {
|
||||
color.Red.Println("The path you provided is not a dir or not exists")
|
||||
}
|
||||
return nil
|
||||
return nil, "", ""
|
||||
}
|
||||
|
||||
func ExitMessage() {
|
||||
color.Yellow.Println("Bye, see you next time!")
|
||||
}
|
||||
|
||||
func Get(url string) (map[string]interface{}, error) {
|
||||
func GetByte(url string) ([]byte, NetHttp.Header, error) {
|
||||
response, err := NetHttp.Get(url)
|
||||
if err != nil {
|
||||
color.Red.Printf("Request failed, Error: %s \n", err.Error())
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
body, _ := ioutil.ReadAll(response.Body)
|
||||
|
||||
var result map[string]interface{}
|
||||
json.Unmarshal(body, &result)
|
||||
return result, nil
|
||||
header := response.Header
|
||||
return body, header, nil
|
||||
}
|
||||
|
||||
func Post(uri string, params map[string]interface{}) (map[string]interface{}, error) {
|
||||
data := url.Values{}
|
||||
for k, v := range params {
|
||||
data.Add(k, v.(string))
|
||||
}
|
||||
|
||||
response, err := NetHttp.PostForm(uri, data)
|
||||
func Get(url string) (map[string]interface{}, error) {
|
||||
response, err := NetHttp.Get(url)
|
||||
if err != nil {
|
||||
color.Red.Printf("Request failed, Error: %s \n", err.Error())
|
||||
return nil, err
|
||||
|
@ -259,6 +386,25 @@ func PostForm(uri string, params map[string]interface{}) (map[string]interface{}
|
|||
return result, nil
|
||||
}
|
||||
|
||||
func PostFormByte(uri string, params map[string]interface{}) ([]byte, NetHttp.Header, error) {
|
||||
data := ""
|
||||
for k, v := range params {
|
||||
data += fmt.Sprintf("%s=%s&%s", k, v.(string), data)
|
||||
}
|
||||
|
||||
response, err := NetHttp.Post(uri, "application/x-www-form-urlencoded", strings.NewReader(data))
|
||||
if err != nil {
|
||||
color.Red.Printf("Request failed, Error: %s \n", err.Error())
|
||||
return nil, nil, err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
body, _ := ioutil.ReadAll(response.Body)
|
||||
header := response.Header
|
||||
|
||||
return body, header, nil
|
||||
}
|
||||
|
||||
func ShowProjectLists(host string, repos []string, path string) {
|
||||
for i, r := range repos {
|
||||
i = i + 1
|
||||
|
@ -283,7 +429,7 @@ func AskPublic(npType string) string {
|
|||
func AskError() string {
|
||||
howTo := []string{"Exit and fix them",
|
||||
"Skip them"}
|
||||
ques := "There are errors on some dirs, what would you like to do?"
|
||||
ques := "There are errors on some projects, what would you like to do?"
|
||||
return selectOne(howTo, ques)
|
||||
}
|
||||
|
||||
|
@ -300,12 +446,35 @@ func selectOne(items []string, ques string) string {
|
|||
return interact.SelectOne(ques, items, "",)
|
||||
}
|
||||
|
||||
func SyncRepo(auth *http.BasicAuth ,local string, uri string, force string) error {
|
||||
var forceStr string
|
||||
func SyncRepo(auth *http.BasicAuth ,source string, uri string, force string, tmpDir string) error {
|
||||
var forceStr, repoPath, rHead string
|
||||
// if project from api, clone it before pushing to target
|
||||
if tmpDir != "" {
|
||||
rHead = "remotes/origin"
|
||||
repoSplit := strings.Split(source, "/")
|
||||
repoName := repoSplit[len(repoSplit) - 1]
|
||||
repoPath = fmt.Sprintf("%s/%s", tmpDir, repoName)
|
||||
if r, err := git.PlainOpen(repoPath); err == nil { // fetch if repo exists
|
||||
r.Fetch(&git.FetchOptions{
|
||||
RefSpecs: []config.RefSpec{"refs/*:refs/*"},
|
||||
})
|
||||
} else {
|
||||
_, err := git.PlainClone(repoPath, true, &git.CloneOptions{
|
||||
URL: source,
|
||||
RecurseSubmodules: git.DefaultSubmoduleRecursionDepth,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
rHead = "heads"
|
||||
repoPath = source
|
||||
}
|
||||
|
||||
// generate a tmp remote
|
||||
remote := fmt.Sprintf("up2GitX-%d", time.Now().Unix())
|
||||
r, err := git.PlainOpen(local)
|
||||
r, err := git.PlainOpen(repoPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -326,7 +495,7 @@ func SyncRepo(auth *http.BasicAuth ,local string, uri string, force string) erro
|
|||
default:
|
||||
forceStr = ""
|
||||
}
|
||||
rHeadStrings := fmt.Sprintf("%srefs/%s/*:refs/%s/*", forceStr, "heads", "heads")
|
||||
rHeadStrings := fmt.Sprintf("%srefs/%s/*:refs/%s/*", forceStr, rHead, "heads")
|
||||
rTagStrings := fmt.Sprintf("%srefs/%s/*:refs/%s/*", forceStr, "tags", "tags")
|
||||
rHeads := config.RefSpec(rHeadStrings)
|
||||
rTags := config.RefSpec(rTagStrings)
|
||||
|
|
Loading…
Reference in New Issue