From d25ec726adec3bc5b3112233103eb44bebd7051b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mislav=20Marohni=C4=87?= Date: Tue, 25 Feb 2020 16:47:42 +0100 Subject: [PATCH] Add `repo clone ` command --- command/repo.go | 30 +++++++++++++++++++++++++ command/repo_test.go | 53 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+) diff --git a/command/repo.go b/command/repo.go index b00c3d87b..484ef609c 100644 --- a/command/repo.go +++ b/command/repo.go @@ -2,8 +2,10 @@ package command import ( "fmt" + "os" "strings" + "github.com/cli/cli/git" "github.com/cli/cli/internal/ghrepo" "github.com/cli/cli/utils" "github.com/spf13/cobra" @@ -11,6 +13,7 @@ import ( func init() { RootCmd.AddCommand(repoCmd) + repoCmd.AddCommand(repoCloneCmd) repoCmd.AddCommand(repoViewCmd) } @@ -24,6 +27,16 @@ A repository can be supplied as an argument in any of the following formats: - by URL, e.g. "https://github.com/OWNER/REPO"`, } +var repoCloneCmd = &cobra.Command{ + Use: "clone ", + Args: cobra.MinimumNArgs(1), + Short: "Clone a repository locally", + Long: `Clone a GitHub repository locally. + +To pass 'git clone' options, separate them with '--'.`, + RunE: repoClone, +} + var repoViewCmd = &cobra.Command{ Use: "view []", Short: "View a repository in the browser", @@ -33,6 +46,23 @@ With no argument, the repository for the current directory is opened.`, RunE: repoView, } +func repoClone(cmd *cobra.Command, args []string) error { + cloneURL := args[0] + if !strings.Contains(cloneURL, ":") { + cloneURL = fmt.Sprintf("https://github.com/%s.git", cloneURL) + } + + cloneArgs := []string{"clone"} + cloneArgs = append(cloneArgs, args[1:]...) + cloneArgs = append(cloneArgs, cloneURL) + + cloneCmd := git.GitCommand(cloneArgs...) + cloneCmd.Stdin = os.Stdin + cloneCmd.Stdout = os.Stdout + cloneCmd.Stderr = os.Stderr + return utils.PrepareCmd(cloneCmd).Run() +} + func repoView(cmd *cobra.Command, args []string) error { ctx := contextForCommand(cmd) diff --git a/command/repo_test.go b/command/repo_test.go index 6c4d6f1ef..588ae3520 100644 --- a/command/repo_test.go +++ b/command/repo_test.go @@ -2,12 +2,65 @@ package command import ( "os/exec" + "strings" "testing" "github.com/cli/cli/context" "github.com/cli/cli/utils" ) +func TestRepoClone(t *testing.T) { + tests := []struct { + name string + args string + want string + }{ + { + name: "shorthand", + args: "repo clone OWNER/REPO", + want: "git clone https://github.com/OWNER/REPO.git", + }, + { + name: "clone arguments", + args: "repo clone OWNER/REPO -- -o upstream --depth 1", + want: "git clone -o upstream --depth 1 https://github.com/OWNER/REPO.git", + }, + { + name: "HTTPS URL", + args: "repo clone https://github.com/OWNER/REPO", + want: "git clone https://github.com/OWNER/REPO", + }, + { + name: "SSH URL", + args: "repo clone git@github.com:OWNER/REPO.git", + want: "git clone git@github.com:OWNER/REPO.git", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var seenCmd *exec.Cmd + restoreCmd := utils.SetPrepareCmd(func(cmd *exec.Cmd) utils.Runnable { + seenCmd = cmd + return &outputStub{} + }) + defer restoreCmd() + + output, err := RunCommand(repoViewCmd, tt.args) + if err != nil { + t.Fatalf("error running command `repo clone`: %v", err) + } + + eq(t, output.String(), "") + eq(t, output.Stderr(), "") + + if seenCmd == nil { + t.Fatal("expected a command to run") + } + eq(t, strings.Join(seenCmd.Args, " "), tt.want) + }) + } +} + func TestRepoView(t *testing.T) { initBlankContext("OWNER/REPO", "master") http := initFakeHTTP()