Skip to content

Commit

Permalink
feat: support to clone and checkout code (#11)
Browse files Browse the repository at this point in the history
* feat: support to clone and checkout code

* update readme file

Co-authored-by: rick <[email protected]>
  • Loading branch information
LinuxSuRen and LinuxSuRen authored Dec 30, 2022
1 parent 02834db commit 53ddb16
Show file tree
Hide file tree
Showing 10 changed files with 260 additions and 17 deletions.
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
build:
CGO_ENABLE=0 go build -ldflags "-w -s" -o bin/gogit
copy: build
cp bin/gogit /usr/local/bin
test:
go test ./... -coverprofile coverage.out
goreleaser:
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
Below is an example of sending build status to a private Gitlab server:

```shell
gogit --provider gitlab \
gogit status --provider gitlab \
--server http://10.121.218.82:6080 \
--repo yaml-readme \
--pr 1 \
Expand Down Expand Up @@ -38,6 +38,7 @@ data:
sidecar.automountServiceAccountToken: "true"
sidecar.container: |
args:
- status
- --provider
- gitlab
- --target
Expand Down
120 changes: 120 additions & 0 deletions cmd/checkout.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package cmd

import (
"fmt"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/config"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/transport/ssh"
"github.com/spf13/cobra"
"os"
"path/filepath"
)

func newCheckoutCommand() (c *cobra.Command) {
opt := &checkoutOption{}

c = &cobra.Command{
Use: "checkout",
Short: "Clond and checkout the git repository with branch, tag, or pull request",
Example: "gogit checkout https://github.com/linuxsuren/gogit",
PreRunE: opt.preRunE,
RunE: opt.runE,
}

flags := c.Flags()
flags.StringVarP(&opt.url, "url", "", "", "The git repository URL")
flags.StringVarP(&opt.remote, "remote", "", "origin", "The remote name")
flags.StringVarP(&opt.branch, "branch", "", "master", "The branch want to checkout")
flags.StringVarP(&opt.tag, "tag", "", "", "The tag want to checkout")
flags.IntVarP(&opt.pr, "pr", "", -1, "The pr number want to checkout, -1 means do nothing")
flags.StringVarP(&opt.target, "target", "", ".", "Clone git repository to the target path")
flags.StringVarP(&opt.versionOutput, "version-output", "", "", "Write the version to target file")
return
}

func (o *checkoutOption) preRunE(c *cobra.Command, args []string) (err error) {
if o.url == "" && len(args) > 0 {
o.url = args[0]
}
return
}

func (o *checkoutOption) runE(c *cobra.Command, args []string) (err error) {
var repoDir string
if repoDir, err = filepath.Abs(o.target); err != nil {
return
}
rsa := os.ExpandEnv("$HOME/.ssh/id_rsa")

var publicKeys *ssh.PublicKeys
if publicKeys, err = ssh.NewPublicKeysFromFile("git", rsa, ""); err != nil {
return
}

if _, err = git.PlainClone(repoDir, false, &git.CloneOptions{
RemoteName: o.remote,
Auth: publicKeys,
URL: o.url,
ReferenceName: plumbing.NewBranchReferenceName(o.branch),
Progress: c.OutOrStdout(),
}); err != nil {
err = fmt.Errorf("failed to clone git repository '%s' into '%s', error: %v", o.url, repoDir, err)
return
}

var repo *git.Repository
if repo, err = git.PlainOpen(repoDir); err == nil {
var wd *git.Worktree

if wd, err = repo.Worktree(); err == nil {
if o.tag != "" {
if err = wd.Checkout(&git.CheckoutOptions{
Branch: plumbing.NewTagReferenceName(o.tag),
}); err != nil {
err = fmt.Errorf("unable to checkout git branch: %s, error: %v", o.tag, err)
return
}
}

if o.pr > 0 {
// TODO add GitHub support, see also https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/checking-out-pull-requests-locally?gt
if err = repo.Fetch(&git.FetchOptions{
RemoteName: o.remote,
Auth: publicKeys,
Progress: c.OutOrStdout(),
RefSpecs: []config.RefSpec{config.RefSpec(fmt.Sprintf("refs/merge-requests/%d/head:mr-%d", o.pr, o.pr))},
}); err != nil && err != git.NoErrAlreadyUpToDate {
err = fmt.Errorf("failed to fetch '%s', error: %v", o.remote, err)
return
}

if err = wd.Checkout(&git.CheckoutOptions{
Create: true,
Branch: plumbing.NewBranchReferenceName(fmt.Sprintf("mr-%d", o.pr)),
}); err != nil {
err = fmt.Errorf("unable to checkout git branch: %s, error: %v", o.tag, err)
return
}
}

var head *plumbing.Reference
if head, err = repo.Head(); err == nil {
if o.versionOutput != "" {
os.WriteFile(o.versionOutput, []byte(head.Name().Short()), 0444)
}
}
}
}
return
}

type checkoutOption struct {
url string
remote string
branch string
tag string
pr int
target string
versionOutput string
}
15 changes: 15 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package cmd

import "github.com/spf13/cobra"

// NewRootCommand returns the root command
func NewRootCommand() (c *cobra.Command) {
c = &cobra.Command{
Use: "gogit",
Short: "Git client across Gitlab/GitHub",
}

c.AddCommand(newCheckoutCommand(),
newStatusCmd())
return
}
12 changes: 12 additions & 0 deletions cmd/root_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package cmd

import (
"github.com/stretchr/testify/assert"
"testing"
)

func TestNewRootCommand(t *testing.T) {
c := NewRootCommand()
assert.NotNil(t, c)
assert.Equal(t, "gogit", c.Use)
}
12 changes: 6 additions & 6 deletions cmd/build.go → cmd/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ import (
"strings"
)

func NewBuildCmd() (cmd *cobra.Command) {
opt := &option{}
func newStatusCmd() (cmd *cobra.Command) {
opt := &statusOption{}
cmd = &cobra.Command{
Use: "gogit",
Use: "status",
Short: "Send the build token to a PR of Gitlab/GitHub",
PreRunE: opt.preRunE,
RunE: opt.runE,
Expand Down Expand Up @@ -40,7 +40,7 @@ func NewBuildCmd() (cmd *cobra.Command) {
return
}

func (o *option) preRunE(cmd *cobra.Command, args []string) (err error) {
func (o *statusOption) preRunE(cmd *cobra.Command, args []string) (err error) {
if o.owner == "" {
o.owner = o.username
}
Expand Down Expand Up @@ -69,7 +69,7 @@ func (o *option) preRunE(cmd *cobra.Command, args []string) (err error) {
return
}

func (o *option) runE(cmd *cobra.Command, args []string) (err error) {
func (o *statusOption) runE(cmd *cobra.Command, args []string) (err error) {
err = pkg.Reconcile(cmd.Context(), pkg.RepoInformation{
Provider: o.provider,
Server: o.server,
Expand All @@ -86,7 +86,7 @@ func (o *option) runE(cmd *cobra.Command, args []string) (err error) {
return
}

type option struct {
type statusOption struct {
provider string
server string
username string
Expand Down
12 changes: 6 additions & 6 deletions cmd/build_test.go → cmd/status_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
func TestStatus(t *testing.T) {
tests := []struct {
name string
opt *option
opt *statusOption
status string
expectStatus string
}{{
Expand All @@ -25,7 +25,7 @@ func TestStatus(t *testing.T) {
for i := range tests {
tt := tests[i]
t.Run(tt.name, func(t *testing.T) {
opt := &option{status: tt.status}
opt := &statusOption{status: tt.status}
_ = opt.preRunE(nil, nil)
assert.Equal(t, tt.expectStatus, opt.status)
})
Expand All @@ -35,7 +35,7 @@ func TestStatus(t *testing.T) {
func TestTokenFromFile(t *testing.T) {
tests := []struct {
name string
opt *option
opt *statusOption
token string
prepare func() string
expect string
Expand All @@ -55,7 +55,7 @@ func TestTokenFromFile(t *testing.T) {
for i := range tests {
tt := tests[i]
t.Run(tt.name, func(t *testing.T) {
opt := &option{token: tt.token}
opt := &statusOption{token: tt.token}
if tt.prepare != nil {
token := tt.prepare()
opt.token = "file://" + token
Expand All @@ -70,8 +70,8 @@ func TestTokenFromFile(t *testing.T) {
}

func TestFlags(t *testing.T) {
cmd := NewBuildCmd()
assert.Equal(t, "gogit", cmd.Use)
cmd := newStatusCmd()
assert.Equal(t, "status", cmd.Use)
flags := cmd.Flags()
assert.NotNil(t, flags.Lookup("provider"))
assert.NotNil(t, flags.Lookup("server"))
Expand Down
18 changes: 18 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,27 @@ require (
github.com/stretchr/testify v1.8.1
)

require (
github.com/Microsoft/go-winio v0.4.16 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 // indirect
github.com/acomagu/bufpipe v1.0.3 // indirect
github.com/emirpasic/gods v1.12.0 // indirect
github.com/go-git/gcfg v1.5.0 // indirect
github.com/go-git/go-billy/v5 v5.3.1 // indirect
github.com/imdario/mergo v0.3.12 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/sergi/go-diff v1.1.0 // indirect
github.com/xanzy/ssh-agent v0.3.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
)

require (
code.gitea.io/sdk/gitea v0.14.0 // indirect
github.com/bluekeyes/go-gitdiff v0.4.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-git/go-git/v5 v5.4.2
github.com/golang/protobuf v1.5.2 // indirect
github.com/hashicorp/go-version v1.3.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
Expand All @@ -23,6 +40,7 @@ require (
github.com/shurcooL/graphql v0.0.0-20181231061246-d48a9a75455f // indirect
github.com/sirupsen/logrus v1.9.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b // indirect
golang.org/x/net v0.3.1-0.20221206200815-1e63c2f08a10 // indirect
golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 // indirect
golang.org/x/sys v0.3.0 // indirect
Expand Down
Loading

0 comments on commit 53ddb16

Please sign in to comment.