From 58f62a4a155cb8ea3e6ac686e0fdd6764b5613b9 Mon Sep 17 00:00:00 2001 From: Colin Shum Date: Thu, 27 Aug 2020 13:00:34 -0400 Subject: [PATCH] [Feature] Create repository from template MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Mislav Marohnić --- api/queries_user.go | 11 ++++++ pkg/cmd/repo/create/create.go | 38 ++++++++++++++++++++ pkg/cmd/repo/create/http.go | 67 +++++++++++++++++++++++++++++++---- 3 files changed, 110 insertions(+), 6 deletions(-) diff --git a/api/queries_user.go b/api/queries_user.go index 0a9b68bd2..4ce7a3c46 100644 --- a/api/queries_user.go +++ b/api/queries_user.go @@ -14,3 +14,14 @@ func CurrentLoginName(client *Client, hostname string) (string, error) { err := gql.QueryNamed(context.Background(), "UserCurrent", &query, nil) return query.Viewer.Login, err } + +func CurrentUserID(client *Client, hostname string) (string, error) { + var query struct { + Viewer struct { + ID string + } + } + gql := graphQLClient(client.http, hostname) + err := gql.QueryNamed(context.Background(), "UserCurrent", &query, nil) + return query.Viewer.ID, err +} diff --git a/pkg/cmd/repo/create/create.go b/pkg/cmd/repo/create/create.go index 7628184e5..28d8384cc 100644 --- a/pkg/cmd/repo/create/create.go +++ b/pkg/cmd/repo/create/create.go @@ -8,8 +8,10 @@ import ( "github.com/AlecAivazis/survey/v2" "github.com/MakeNowJust/heredoc" + "github.com/cli/cli/api" "github.com/cli/cli/git" "github.com/cli/cli/internal/config" + "github.com/cli/cli/internal/ghinstance" "github.com/cli/cli/internal/ghrepo" "github.com/cli/cli/internal/run" "github.com/cli/cli/pkg/cmdutil" @@ -28,6 +30,7 @@ type CreateOptions struct { Description string Homepage string Team string + Template string EnableIssues bool EnableWiki bool Public bool @@ -80,6 +83,7 @@ func NewCmdCreate(f *cmdutil.Factory, runF func(*CreateOptions) error) *cobra.Co cmd.Flags().StringVarP(&opts.Description, "description", "d", "", "Description of repository") cmd.Flags().StringVarP(&opts.Homepage, "homepage", "h", "", "Repository home page URL") cmd.Flags().StringVarP(&opts.Team, "team", "t", "", "The name of the organization team to be granted access") + cmd.Flags().StringVarP(&opts.Template, "template", "p", "", "Make the new repository based on a template repository") cmd.Flags().BoolVar(&opts.EnableIssues, "enable-issues", true, "Enable issues in the new repository") cmd.Flags().BoolVar(&opts.EnableWiki, "enable-wiki", true, "Enable wiki in the new repository") cmd.Flags().BoolVar(&opts.Public, "public", false, "Make the new repository public") @@ -164,11 +168,45 @@ func createRun(opts *CreateOptions) error { } } + // find template ID + + if opts.Template != "" { + httpClient, err := opts.HttpClient() + if err != nil { + return err + } + + var toView ghrepo.Interface + apiClient := api.NewClientFromHTTP(httpClient) + + // var err errors + viewURL := opts.Template + if !strings.Contains(viewURL, "/") { + currentUser, err := api.CurrentLoginName(apiClient, ghinstance.Default()) + if err != nil { + return err + } + viewURL = currentUser + "/" + viewURL + } + toView, err = ghrepo.FromFullName(viewURL) + if err != nil { + return fmt.Errorf("argument error: %w", err) + } + + repo, err := api.GitHubRepo(apiClient, toView) + if err != nil { + return err + } + + opts.Template = repo.ID + } + input := repoCreateInput{ Name: repoToCreate.RepoName(), Visibility: visibility, OwnerID: repoToCreate.RepoOwner(), TeamID: opts.Team, + RepositoryID: opts.Template, Description: opts.Description, HomepageURL: opts.Homepage, HasIssuesEnabled: opts.EnableIssues, diff --git a/pkg/cmd/repo/create/http.go b/pkg/cmd/repo/create/http.go index 3a46c0d9a..f3623c6e2 100644 --- a/pkg/cmd/repo/create/http.go +++ b/pkg/cmd/repo/create/http.go @@ -14,6 +14,8 @@ type repoCreateInput struct { HomepageURL string `json:"homepageUrl,omitempty"` Description string `json:"description,omitempty"` + RepositoryID string `json:"repositoryId,omitempty"` + OwnerID string `json:"ownerId,omitempty"` TeamID string `json:"teamId,omitempty"` @@ -21,16 +23,18 @@ type repoCreateInput struct { HasWikiEnabled bool `json:"hasWikiEnabled"` } +type repoTemplateInput struct { + Name string `json:"name"` + Visibility string `json:"visibility"` + OwnerID string `json:"ownerId,omitempty"` + + RepositoryID string `json:"repositoryId,omitempty"` +} + // repoCreate creates a new GitHub repository func repoCreate(client *http.Client, hostname string, input repoCreateInput) (*api.Repository, error) { apiClient := api.NewClientFromHTTP(client) - var response struct { - CreateRepository struct { - Repository api.Repository - } - } - if input.TeamID != "" { orgID, teamID, err := resolveOrganizationTeam(apiClient, hostname, input.OwnerID, input.TeamID) if err != nil { @@ -46,6 +50,57 @@ func repoCreate(client *http.Client, hostname string, input repoCreateInput) (*a input.OwnerID = orgID } + if input.RepositoryID != "" { + var response struct { + CloneTemplateRepository struct { + Repository api.Repository + } + } + + if input.OwnerID == "" { + var err error + input.OwnerID, err = api.CurrentUserID(apiClient, hostname) + if err != nil { + return nil, err + } + } + + templateInput := repoTemplateInput{ + Name: input.Name, + Visibility: input.Visibility, + OwnerID: input.OwnerID, + RepositoryID: input.RepositoryID, + } + + variables := map[string]interface{}{ + "input": templateInput, + } + + err := apiClient.GraphQL(hostname, ` + mutation CloneTemplateRepository($input: CloneTemplateRepositoryInput!) { + cloneTemplateRepository(input: $input) { + repository { + id + name + owner { login } + url + } + } + } + `, variables, &response) + if err != nil { + return nil, err + } + + return api.InitRepoHostname(&response.CloneTemplateRepository.Repository, hostname), nil + } + + var response struct { + CreateRepository struct { + Repository api.Repository + } + } + variables := map[string]interface{}{ "input": input, }