Compare commits

...

9 Commits

Author SHA1 Message Date
Zoker 1fd918e5e3 update demo pic 2020-07-11 11:00:13 +08:00
Zoker b7b96ce1b7 update artifacts download url 2020-07-11 10:45:05 +08:00
Zoker 1a4a897b6e update readme and some description 2020-07-11 10:33:08 +08:00
Zoker 249e90e00c add create Gitee group feature 2020-07-11 08:26:11 +08:00
Zoker 657f09c93d migrate to normal Github api and fix projects count bug 2020-07-11 07:38:32 +08:00
Zoker 09d8e17937 clone and sync complete 2020-07-08 23:55:32 +08:00
Zoker 4c254a2c23 add github:xxx support and list over 2020-07-08 22:47:19 +08:00
Zoker 1d212795a6 add design process 2020-06-13 18:10:32 +08:00
Zoker b37b0b1c97 add attach files link 2020-06-13 17:55:05 +08:00
5 changed files with 328 additions and 69 deletions

View File

@ -6,17 +6,25 @@
### 功能特点
- [简单] 可自动在对应平台创建同名仓库并上传
- [可靠] 可选择是否本地覆盖远程(如果同名仓库在远端已存在)
- [灵活] 可通过多种方式导入仓库源(目录、文本列表)
- [方便] 可选择上传到 Gitee 组织或者新建组织上传
- [灵活] 可通过多种方式导入仓库源目录、文本列表、Github 源)
- [快速] 并发5个同时操作各平台均有限制5个并发可保证成功率及速度
### 注意事项
- 工具会自动生成临时的一个 Remote `up2GitX-{timestamp}` 进行推送,不会影响现有的 Remote 配置
- 工具会调用OAuth授权进行仓库的创建后续可以在第三方应用管理取消授权
- 工具在进行处理的过程中会对特殊情况进行交互,请仔细阅读并慎重选择
- Github 的镜像操作会生成临时目录来存储这些临时的裸仓库,同步完成后,自行删除即可
### 设计流程
![输入图片说明](https://gitee.com/uploads/20/144842_4d529ecd_62561.png)
### 使用简介
[下载](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
![输入图片说明](https://images.gitee.com/uploads/images/2020/0613/174031_79068a5e_62561.png "2.png")
![输入图片说明](https://images.gitee.com/uploads/images/2020/0711/105952_6389c7a9_62561.png "屏幕截图.png")
输入注册 Gitee 所用的邮箱以及密码,认证成功后会列出可用的 Namespace在 Gitee 上,它可以是个人名下、组织名下或者企业名下。(目前仅支持上传到个人名下,后续接口功能到位会支持组织和企业)
输入注册 Gitee 所用的邮箱以及密码,认证成功后会列出可用的 Namespace你也可以选择新建一个组织。在 Gitee 上,它可以是个人名下、组织名下或者企业名下。(目前仅支持上传到个人和组织,后续接口功能到位会支持企业)
#### 3、确认将要生成的仓库链接并选择公开属性

1
go.mod
View File

@ -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
View File

@ -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=

View File

@ -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), &paramsJson)
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), &paramsJson)
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()

View File

@ -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)