Merge pull request #2222 from Crunch09/issue-2115
add `gist clone` command
This commit is contained in:
commit
230441e1a5
3 changed files with 221 additions and 0 deletions
101
pkg/cmd/gist/clone/clone.go
Normal file
101
pkg/cmd/gist/clone/clone.go
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
package clone
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/MakeNowJust/heredoc"
|
||||
"github.com/cli/cli/git"
|
||||
"github.com/cli/cli/internal/config"
|
||||
"github.com/cli/cli/internal/ghinstance"
|
||||
"github.com/cli/cli/pkg/cmdutil"
|
||||
"github.com/cli/cli/pkg/iostreams"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
type CloneOptions struct {
|
||||
HttpClient func() (*http.Client, error)
|
||||
Config func() (config.Config, error)
|
||||
IO *iostreams.IOStreams
|
||||
|
||||
GitArgs []string
|
||||
Directory string
|
||||
Gist string
|
||||
}
|
||||
|
||||
func NewCmdClone(f *cmdutil.Factory, runF func(*CloneOptions) error) *cobra.Command {
|
||||
opts := &CloneOptions{
|
||||
IO: f.IOStreams,
|
||||
HttpClient: f.HttpClient,
|
||||
Config: f.Config,
|
||||
}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
DisableFlagsInUseLine: true,
|
||||
|
||||
Use: "clone <gist> [<directory>] [-- <gitflags>...]",
|
||||
Args: cmdutil.MinimumArgs(1, "cannot clone: gist argument required"),
|
||||
Short: "Clone a gist locally",
|
||||
Long: heredoc.Doc(`
|
||||
Clone a GitHub gist locally.
|
||||
|
||||
A gist can be supplied as argument in either of the following formats:
|
||||
- by ID, e.g. 5b0e0062eb8e9654adad7bb1d81cc75f
|
||||
- by URL, e.g. "https://gist.github.com/OWNER/5b0e0062eb8e9654adad7bb1d81cc75f"
|
||||
|
||||
Pass additional 'git clone' flags by listing them after '--'.
|
||||
`),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.Gist = args[0]
|
||||
opts.GitArgs = args[1:]
|
||||
|
||||
if runF != nil {
|
||||
return runF(opts)
|
||||
}
|
||||
|
||||
return cloneRun(opts)
|
||||
},
|
||||
}
|
||||
|
||||
cmd.SetFlagErrorFunc(func(cmd *cobra.Command, err error) error {
|
||||
if err == pflag.ErrHelp {
|
||||
return err
|
||||
}
|
||||
return &cmdutil.FlagError{Err: fmt.Errorf("%w\nSeparate git clone flags with '--'.", err)}
|
||||
})
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func cloneRun(opts *CloneOptions) error {
|
||||
gistURL := opts.Gist
|
||||
|
||||
if !git.IsURL(gistURL) {
|
||||
cfg, err := opts.Config()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
hostname := ghinstance.OverridableDefault()
|
||||
protocol, err := cfg.Get(hostname, "git_protocol")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
gistURL = formatRemoteURL(hostname, gistURL, protocol)
|
||||
}
|
||||
|
||||
_, err := git.RunClone(gistURL, opts.GitArgs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func formatRemoteURL(hostname string, gistID string, protocol string) string {
|
||||
if protocol == "ssh" {
|
||||
return fmt.Sprintf("git@gist.%s:%s.git", hostname, gistID)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("https://gist.%s/%s.git", hostname, gistID)
|
||||
}
|
||||
118
pkg/cmd/gist/clone/clone_test.go
Normal file
118
pkg/cmd/gist/clone/clone_test.go
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
package clone
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/cli/cli/internal/config"
|
||||
"github.com/cli/cli/pkg/cmdutil"
|
||||
"github.com/cli/cli/pkg/httpmock"
|
||||
"github.com/cli/cli/pkg/iostreams"
|
||||
"github.com/cli/cli/test"
|
||||
"github.com/google/shlex"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func runCloneCommand(httpClient *http.Client, cli string) (*test.CmdOut, error) {
|
||||
io, stdin, stdout, stderr := iostreams.Test()
|
||||
fac := &cmdutil.Factory{
|
||||
IOStreams: io,
|
||||
HttpClient: func() (*http.Client, error) {
|
||||
return httpClient, nil
|
||||
},
|
||||
Config: func() (config.Config, error) {
|
||||
return config.NewBlankConfig(), nil
|
||||
},
|
||||
}
|
||||
|
||||
cmd := NewCmdClone(fac, nil)
|
||||
|
||||
argv, err := shlex.Split(cli)
|
||||
cmd.SetArgs(argv)
|
||||
|
||||
cmd.SetIn(stdin)
|
||||
cmd.SetOut(stdout)
|
||||
cmd.SetErr(stderr)
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
_, err = cmd.ExecuteC()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &test.CmdOut{OutBuf: stdout, ErrBuf: stderr}, nil
|
||||
}
|
||||
|
||||
func Test_GistClone(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
args string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "shorthand",
|
||||
args: "GIST",
|
||||
want: "git clone https://gist.github.com/GIST.git",
|
||||
},
|
||||
{
|
||||
name: "shorthand with directory",
|
||||
args: "GIST target_directory",
|
||||
want: "git clone https://gist.github.com/GIST.git target_directory",
|
||||
},
|
||||
{
|
||||
name: "clone arguments",
|
||||
args: "GIST -- -o upstream --depth 1",
|
||||
want: "git clone -o upstream --depth 1 https://gist.github.com/GIST.git",
|
||||
},
|
||||
{
|
||||
name: "clone arguments with directory",
|
||||
args: "GIST target_directory -- -o upstream --depth 1",
|
||||
want: "git clone -o upstream --depth 1 https://gist.github.com/GIST.git target_directory",
|
||||
},
|
||||
{
|
||||
name: "HTTPS URL",
|
||||
args: "https://gist.github.com/OWNER/GIST",
|
||||
want: "git clone https://gist.github.com/OWNER/GIST",
|
||||
},
|
||||
{
|
||||
name: "SSH URL",
|
||||
args: "git@gist.github.com:GIST.git",
|
||||
want: "git clone git@gist.github.com:GIST.git",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
reg := &httpmock.Registry{}
|
||||
|
||||
httpClient := &http.Client{Transport: reg}
|
||||
|
||||
cs, restore := test.InitCmdStubber()
|
||||
defer restore()
|
||||
|
||||
cs.Stub("") // git clone
|
||||
|
||||
output, err := runCloneCommand(httpClient, tt.args)
|
||||
if err != nil {
|
||||
t.Fatalf("error running command `gist clone`: %v", err)
|
||||
}
|
||||
|
||||
assert.Equal(t, "", output.String())
|
||||
assert.Equal(t, "", output.Stderr())
|
||||
assert.Equal(t, 1, cs.Count)
|
||||
assert.Equal(t, tt.want, strings.Join(cs.Calls[0].Args, " "))
|
||||
reg.Verify(t)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_GistClone_flagError(t *testing.T) {
|
||||
_, err := runCloneCommand(nil, "--depth 1 GIST")
|
||||
if err == nil || err.Error() != "unknown flag: --depth\nSeparate git clone flags with '--'." {
|
||||
t.Errorf("unexpected error %v", err)
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ package gist
|
|||
|
||||
import (
|
||||
"github.com/MakeNowJust/heredoc"
|
||||
gistCloneCmd "github.com/cli/cli/pkg/cmd/gist/clone"
|
||||
gistCreateCmd "github.com/cli/cli/pkg/cmd/gist/create"
|
||||
gistDeleteCmd "github.com/cli/cli/pkg/cmd/gist/delete"
|
||||
gistEditCmd "github.com/cli/cli/pkg/cmd/gist/edit"
|
||||
|
|
@ -26,6 +27,7 @@ func NewCmdGist(f *cmdutil.Factory) *cobra.Command {
|
|||
},
|
||||
}
|
||||
|
||||
cmd.AddCommand(gistCloneCmd.NewCmdClone(f, nil))
|
||||
cmd.AddCommand(gistCreateCmd.NewCmdCreate(f, nil))
|
||||
cmd.AddCommand(gistListCmd.NewCmdList(f, nil))
|
||||
cmd.AddCommand(gistViewCmd.NewCmdView(f, nil))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue