diff --git a/api/queries_repo.go b/api/queries_repo.go index 9d0c4f00d..56959e86a 100644 --- a/api/queries_repo.go +++ b/api/queries_repo.go @@ -14,6 +14,7 @@ import ( type Repository struct { ID string Name string + URL string Owner RepositoryOwner IsPrivate bool @@ -216,3 +217,43 @@ func ForkRepo(client *Client, repo ghrepo.Interface) (*Repository, error) { ViewerPermission: "WRITE", }, nil } + +// RepoCreateInput represents input parameters for RepoCreate +type RepoCreateInput struct { + Name string `json:"name"` + Visibility string `json:"visibility"` + Homepage string `json:"homepage,omitempty"` + Description string `json:"description,omitempty"` + + HasIssuesEnabled bool `json:"hasIssuesEnabled"` + HasWikiEnabled bool `json:"hasWikiEnabled"` +} + +// RepoCreate creates a new GitHub repository +func RepoCreate(client *Client, input RepoCreateInput) (*Repository, error) { + var response struct { + CreateRepository struct { + Repository Repository + } + } + + variables := map[string]interface{}{ + "input": input, + } + + err := client.GraphQL(` + mutation($input: CreateRepositoryInput!) { + createRepository(input: $input) { + repository { + id + url + } + } + } + `, variables, &response) + if err != nil { + return nil, err + } + + return &response.CreateRepository.Repository, nil +} diff --git a/command/repo.go b/command/repo.go index 7b8dd2728..905885c00 100644 --- a/command/repo.go +++ b/command/repo.go @@ -6,6 +6,7 @@ import ( "path" "strings" + "github.com/cli/cli/api" "github.com/cli/cli/git" "github.com/cli/cli/internal/ghrepo" "github.com/cli/cli/utils" @@ -16,6 +17,10 @@ func init() { RootCmd.AddCommand(repoCmd) repoCmd.AddCommand(repoCloneCmd) repoCmd.AddCommand(repoCreateCmd) + repoCreateCmd.Flags().StringP("description", "d", "", "Description of repository") + repoCreateCmd.Flags().StringP("homepage", "h", "", "Repository home page URL") + repoCreateCmd.Flags().Bool("enable-issues", true, "Enable issues in the new repository") + repoCreateCmd.Flags().Bool("enable-wiki", true, "Enable wiki in the new repository") repoCreateCmd.Flags().Bool("public", false, "Make the new repository public") repoCmd.AddCommand(repoViewCmd) } @@ -85,46 +90,54 @@ func repoCreate(cmd *cobra.Command, args []string) error { name = path.Base(dir) } + isPublic, err := cmd.Flags().GetBool("public") + if err != nil { + return err + } + hasIssuesEnabled, err := cmd.Flags().GetBool("enable-issues") + if err != nil { + return err + } + hasWikiEnabled, err := cmd.Flags().GetBool("enable-wiki") + if err != nil { + return err + } + description, err := cmd.Flags().GetString("description") + if err != nil { + return err + } + homepage, err := cmd.Flags().GetString("homepage") + if err != nil { + return err + } + + // TODO: move this into constant within `api` visibility := "PRIVATE" - if isPublic, err := cmd.Flags().GetBool("public"); err == nil && isPublic { + if isPublic { visibility = "PUBLIC" } + input := api.RepoCreateInput{ + Name: name, + Visibility: visibility, + Description: description, + Homepage: homepage, + HasIssuesEnabled: hasIssuesEnabled, + HasWikiEnabled: hasWikiEnabled, + } + ctx := contextForCommand(cmd) client, err := apiClientForContext(ctx) if err != nil { return err } - variables := map[string]interface{}{ - "input": map[string]interface{}{ - "name": name, - "visibility": visibility, - }, - } - - var response struct { - CreateRepository struct { - Repository struct { - URL string - } - } - } - - err = client.GraphQL(` - mutation($input: CreateRepositoryInput!) { - createRepository(input: $input) { - repository { - url - } - } - } - `, variables, &response) + repo, err := api.RepoCreate(client, input) if err != nil { return err } - cmd.Println(response.CreateRepository.Repository.URL) + fmt.Fprintln(cmd.OutOrStdout(), repo.URL) return nil } diff --git a/command/repo_test.go b/command/repo_test.go index 588ae3520..04bda3f6a 100644 --- a/command/repo_test.go +++ b/command/repo_test.go @@ -1,6 +1,9 @@ package command import ( + "bytes" + "encoding/json" + "io/ioutil" "os/exec" "strings" "testing" @@ -61,6 +64,59 @@ func TestRepoClone(t *testing.T) { } } +func TestRepoCreate(t *testing.T) { + ctx := context.NewBlank() + ctx.SetBranch("master") + initContext = func() context.Context { + return ctx + } + + http := initFakeHTTP() + http.StubResponse(200, bytes.NewBufferString(` + { "data": { "createRepository": { + "repository": { + "id": "REPOID", + "url": "https://github.com/OWNER/REPO" + } + } } } + `)) + + var seenCmd *exec.Cmd + restoreCmd := utils.SetPrepareCmd(func(cmd *exec.Cmd) utils.Runnable { + seenCmd = cmd + return &outputStub{} + }) + defer restoreCmd() + + output, err := RunCommand(repoCreateCmd, "repo create REPO") + if err != nil { + t.Errorf("error running command `repo create`: %v", err) + } + + eq(t, output.String(), "https://github.com/OWNER/REPO\n") + eq(t, output.Stderr(), "") + + if seenCmd != nil { + t.Fatal("expected a command to run") + } + + var reqBody struct { + Query string + Variables struct { + Input map[string]interface{} + } + } + + bodyBytes, _ := ioutil.ReadAll(http.Requests[0].Body) + json.Unmarshal(bodyBytes, &reqBody) + if repoName := reqBody.Variables.Input["name"].(string); repoName != "REPO" { + t.Errorf("expected %q, got %q", "REPO", repoName) + } + if repoVisibility := reqBody.Variables.Input["visibility"].(string); repoVisibility != "PRIVATE" { + t.Errorf("expected %q, got %q", "PRIVATE", repoVisibility) + } +} + func TestRepoView(t *testing.T) { initBlankContext("OWNER/REPO", "master") http := initFakeHTTP()