Skip to content

Commit

Permalink
GH-198 自动帮用户fork项目 (#218)
Browse files Browse the repository at this point in the history
* ✨ fork项目 接口

* ✨ 支持clone时自动fork 及 fork命令
  • Loading branch information
EchoJamie committed Oct 5, 2024
1 parent 113d981 commit 032d32e
Show file tree
Hide file tree
Showing 7 changed files with 225 additions and 2 deletions.
15 changes: 13 additions & 2 deletions cmd/clone.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package cmd
import (
"fmt"
"github.com/isxcode/isx-cli/common"
"github.com/isxcode/isx-cli/github"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"os"
Expand Down Expand Up @@ -155,13 +156,23 @@ func cloneProjectCode() {

// 下载主项目代码
mainRepository := viper.GetString(projectName + ".repository.url")
cloneCode(mainRepository, projectPath, projectName, true)
if !github.IsRepoForked(account, projectName) {
github.ForkRepository("isxcode", projectName, "")
cloneCode(mainRepository, projectPath, projectName, true)
} else {
cloneCode(mainRepository, projectPath, projectName, true)
}

// 下载子项目代码
var subRepository []Repository
viper.UnmarshalKey(projectName+".sub-repository", &subRepository)
for _, repository := range subRepository {
cloneCode(repository.Url, projectPath+"/"+projectName, repository.Name, false)
if !github.IsRepoForked(account, projectName) {
github.ForkRepository("isxcode", projectName, "")
cloneCode(repository.Url, projectPath+"/"+projectName, repository.Name, false)
} else {
cloneCode(repository.Url, projectPath+"/"+projectName, repository.Name, false)
}
}
}

Expand Down
56 changes: 56 additions & 0 deletions cmd/fork.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
Copyright © 2024 jamie HERE <EMAIL ADDRESS>
*/
package cmd

import (
"fmt"
"github.com/isxcode/isx-cli/github"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)

var testExistFlag bool

func init() {
forkCmd.Flags().BoolVarP(&testExistFlag, "test", "t", false, "测试是否已经fork过")
rootCmd.AddCommand(forkCmd)
}

var forkCmd = &cobra.Command{
Use: "fork",
Short: printCommand("isx fork", 65) + "| Fork当前项目为同名个人仓库",
Long: `isx fork | isx fork <project-name> | isx fork -t | isx fork -t <project-name>`,
Run: func(cmd *cobra.Command, args []string) {
ForkCmdMain(args)
},
}

func ForkCmdMain(args []string) {
if testExistFlag {
if len(args) > 0 {
forked := github.IsRepoForked(viper.GetString("user.account"), args[0])
if forked {
fmt.Println(args[0], "is forked!")
} else {
fmt.Println(args[0], "is not forked!")
}
} else {
for _, project := range viper.GetStringSlice("project-list") {
forked := github.IsRepoForked(viper.GetString("user.account"), viper.GetString(project+".name"))
if forked {
fmt.Println(viper.GetString(project+".name"), "is forked!")
} else {
fmt.Println(viper.GetString(project+".name"), "is not forked!")
}
}
}
return
} else {
projectName := viper.GetString("current-project.name")
if len(args) > 0 {
projectName = args[0]
}
github.ForkRepository("isxcode", projectName, "")
}
}
28 changes: 28 additions & 0 deletions common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ Copyright © 2024 jamie HERE <EMAIL ADDRESS>
package common

import (
"encoding/json"
"fmt"
"github.com/mitchellh/go-homedir"
"io"
"os"
)

Expand All @@ -26,3 +28,29 @@ func CurrentWorkDir() string {
}
return dir
}

func Parse(reader io.Reader, v any) {
body, err := io.ReadAll(reader)
if err != nil {
fmt.Println("读取响应体失败:", err)
os.Exit(1)
}
err = json.Unmarshal(body, v)
if err != nil {
fmt.Println("解析 JSON 失败:", err)
os.Exit(1)
}
}

func ToJsonString(v any) string {
return string(ToJsonBytes(v))
}

func ToJsonBytes(v any) []byte {
jsonBytes, err := json.Marshal(v)
if err != nil {
fmt.Println("解析 JSON 失败:", err)
os.Exit(1)
}
return jsonBytes
}
1 change: 1 addition & 0 deletions common/github.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const GithubDomain = "https://github.com"
const GithubRawDomain = "https://raw.github.com"
const GithubApiDomain = "https://api.github.com"
const GithubApiReposDomain = "https://api.github.com/repos"
const IsxcodeGithubApiReposDomain = GithubApiReposDomain + "/isxcode"

func GitHubHeader(accessToken string) http.Header {
headers := http.Header{}
Expand Down
47 changes: 47 additions & 0 deletions github/common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
Copyright © 2024 jamie HERE <EMAIL ADDRESS>
*/
package github

import (
"fmt"
"github.com/isxcode/isx-cli/common"
"github.com/spf13/viper"
"io"
"net/http"
"os"
)

func Get(url string, reqBody io.Reader) *http.Response {
return request(url, "GET", nil)
}

func Post(url string, reqBody io.Reader) *http.Response {
return request(url, "POST", reqBody)
}

func request(url, method string, reqBody io.Reader) *http.Response {
client := &http.Client{}
req, err := http.NewRequest(method, url, reqBody)

req.Header = common.GitHubHeader(viper.GetString("user.token"))
resp, err := client.Do(req)
if err != nil {
fmt.Println("请求失败:", err)
os.Exit(1)
}
return resp
}

/*
CloseRespBody 关闭响应体
@Example
defer closeRespBody(resp.Body)
*/

func CloseRespBody(Body io.ReadCloser) {
err := Body.Close()
if err != nil {
fmt.Println("关闭响应体失败:", err)
}
}
16 changes: 16 additions & 0 deletions github/entity.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
Copyright © 2024 jamie HERE <EMAIL ADDRESS>
*/
package github

type Repository struct {
Id int `json:"id"`
NodeId string `json:"node_id"`
Name string `json:"name"`
FullName string `json:"full_name"`
Private bool `json:"private"`
HtmlUrl string `json:"html_url"`
Description string `json:"description"`
Fork bool `json:"fork"`
Url string `json:"url"`
}
64 changes: 64 additions & 0 deletions github/fork.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
Copyright © 2024 jamie HERE <EMAIL ADDRESS>
*/
package github

import (
"bytes"
"fmt"
"github.com/isxcode/isx-cli/common"
"io"
"net/http"
"os"
"strings"
)

const createForkUrl = common.GithubApiReposDomain + "/%s/%s/forks"
const isForkedUrl = common.GithubApiReposDomain + "/%s/%s"

func IsRepoForked(account, projectName string) bool {
resp := Get(fmt.Sprintf(isForkedUrl, account, projectName), nil)
defer CloseRespBody(resp.Body)

if resp.StatusCode == http.StatusOK {
var repo Repository
common.Parse(resp.Body, &repo)
return repo.Fork
}
if resp.StatusCode == http.StatusUnauthorized {
fmt.Println("github token权限不足,请重新登录")
os.Exit(1)
}
if resp.StatusCode == http.StatusNotFound {
return false
}
return false
}

func ForkRepository(owner, projectName, newName string) bool {
var reqBody io.Reader = nil
if len(newName) > 0 {
newName = strings.Trim(newName, " ")
if len(newName) > 0 {
jsonBytes := common.ToJsonBytes(map[string]string{"name": newName})
reqBody = bytes.NewBuffer(jsonBytes)
}
}

resp := Post(fmt.Sprintf(createForkUrl, owner, projectName), reqBody)
defer CloseRespBody(resp.Body)

if resp.StatusCode == http.StatusAccepted {
fmt.Println("正在处理中,请稍后")
return true
}
if resp.StatusCode == http.StatusUnauthorized {
fmt.Println("github token权限不足,请重新登录")
os.Exit(1)
}
if resp.StatusCode == http.StatusNotFound {
fmt.Println("项目不存在")
os.Exit(1)
}
return false
}

0 comments on commit 032d32e

Please sign in to comment.