Add ability to repo create within an org

This commit is contained in:
Mislav Marohnić 2020-03-03 21:49:47 +01:00
parent 8deb0d8783
commit be09717918
4 changed files with 196 additions and 2 deletions

24
api/queries_org.go Normal file
View file

@ -0,0 +1,24 @@
package api
import "fmt"
// using API v3 here because the equivalent in GraphQL needs `read:org` scope
func resolveOrganization(client *Client, orgName string) (string, error) {
var response struct {
NodeID string `json:"node_id"`
}
err := client.REST("GET", fmt.Sprintf("users/%s", orgName), nil, &response)
return response.NodeID, err
}
// using API v3 here because the equivalent in GraphQL needs `read:org` scope
func resolveOrganizationTeam(client *Client, orgName, teamSlug string) (string, string, error) {
var response struct {
NodeID string `json:"node_id"`
Organization struct {
NodeID string `json:"node_id"`
}
}
err := client.REST("GET", fmt.Sprintf("orgs/%s/teams/%s", orgName, teamSlug), nil, &response)
return response.Organization.NodeID, response.NodeID, err
}

View file

@ -225,6 +225,9 @@ type RepoCreateInput struct {
Homepage string `json:"homepage,omitempty"`
Description string `json:"description,omitempty"`
OwnerID string `json:"ownerId,omitempty"`
TeamID string `json:"teamId,omitempty"`
HasIssuesEnabled bool `json:"hasIssuesEnabled"`
HasWikiEnabled bool `json:"hasWikiEnabled"`
}
@ -237,6 +240,21 @@ func RepoCreate(client *Client, input RepoCreateInput) (*Repository, error) {
}
}
if input.TeamID != "" {
orgID, teamID, err := resolveOrganizationTeam(client, input.OwnerID, input.TeamID)
if err != nil {
return nil, err
}
input.TeamID = teamID
input.OwnerID = orgID
} else if input.OwnerID != "" {
orgID, err := resolveOrganization(client, input.OwnerID)
if err != nil {
return nil, err
}
input.OwnerID = orgID
}
variables := map[string]interface{}{
"input": input,
}

View file

@ -20,6 +20,7 @@ func init() {
repoCmd.AddCommand(repoCreateCmd)
repoCreateCmd.Flags().StringP("description", "d", "", "Description of repository")
repoCreateCmd.Flags().StringP("homepage", "h", "", "Repository home page URL")
repoCreateCmd.Flags().StringP("team", "t", "", "The name of the organization team to be granted access")
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")
@ -49,8 +50,10 @@ To pass 'git clone' options, separate them with '--'.`,
var repoCreateCmd = &cobra.Command{
Use: "create [<name>]",
Short: "Create a new repository",
Long: `Create a new GitHub repository.`,
RunE: repoCreate,
Long: `Create a new GitHub repository.
Use the "ORG/NAME" syntax to create a repository within your organization.`,
RunE: repoCreate,
}
var repoViewCmd = &cobra.Command{
@ -82,9 +85,20 @@ func repoClone(cmd *cobra.Command, args []string) error {
func repoCreate(cmd *cobra.Command, args []string) error {
projectDir, projectDirErr := git.ToplevelDir()
orgName := ""
teamSlug, err := cmd.Flags().GetString("team")
if err != nil {
return err
}
var name string
if len(args) > 0 {
name = args[0]
if strings.Contains(name, "/") {
newRepo := ghrepo.FromFullName(name)
orgName = newRepo.RepoOwner()
name = newRepo.RepoName()
}
} else {
if projectDirErr != nil {
return projectDirErr
@ -122,6 +136,8 @@ func repoCreate(cmd *cobra.Command, args []string) error {
input := api.RepoCreateInput{
Name: name,
Visibility: visibility,
OwnerID: orgName,
TeamID: teamSlug,
Description: description,
Homepage: homepage,
HasIssuesEnabled: hasIssuesEnabled,

View file

@ -108,6 +108,10 @@ func TestRepoCreate(t *testing.T) {
}
}
if len(http.Requests) != 1 {
t.Fatalf("expected 1 HTTP request, got %d", len(http.Requests))
}
bodyBytes, _ := ioutil.ReadAll(http.Requests[0].Body)
json.Unmarshal(bodyBytes, &reqBody)
if repoName := reqBody.Variables.Input["name"].(string); repoName != "REPO" {
@ -116,6 +120,138 @@ func TestRepoCreate(t *testing.T) {
if repoVisibility := reqBody.Variables.Input["visibility"].(string); repoVisibility != "PRIVATE" {
t.Errorf("expected %q, got %q", "PRIVATE", repoVisibility)
}
if _, ownerSet := reqBody.Variables.Input["ownerId"]; ownerSet {
t.Error("expected ownerId not to be set")
}
}
func TestRepoCreate_org(t *testing.T) {
ctx := context.NewBlank()
ctx.SetBranch("master")
initContext = func() context.Context {
return ctx
}
http := initFakeHTTP()
http.StubResponse(200, bytes.NewBufferString(`
{ "node_id": "ORGID"
}
`))
http.StubResponse(200, bytes.NewBufferString(`
{ "data": { "createRepository": {
"repository": {
"id": "REPOID",
"url": "https://github.com/ORG/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 ORG/REPO")
if err != nil {
t.Errorf("error running command `repo create`: %v", err)
}
eq(t, output.String(), "https://github.com/ORG/REPO\n")
eq(t, output.Stderr(), "")
if seenCmd == nil {
t.Fatal("expected a command to run")
}
eq(t, strings.Join(seenCmd.Args, " "), "git remote add origin https://github.com/ORG/REPO.git")
var reqBody struct {
Query string
Variables struct {
Input map[string]interface{}
}
}
if len(http.Requests) != 2 {
t.Fatalf("expected 2 HTTP requests, got %d", len(http.Requests))
}
eq(t, http.Requests[0].URL.Path, "/users/ORG")
bodyBytes, _ := ioutil.ReadAll(http.Requests[1].Body)
json.Unmarshal(bodyBytes, &reqBody)
if orgID := reqBody.Variables.Input["ownerId"].(string); orgID != "ORGID" {
t.Errorf("expected %q, got %q", "ORGID", orgID)
}
if _, teamSet := reqBody.Variables.Input["teamId"]; teamSet {
t.Error("expected teamId not to be set")
}
}
func TestRepoCreate_orgWithTeam(t *testing.T) {
ctx := context.NewBlank()
ctx.SetBranch("master")
initContext = func() context.Context {
return ctx
}
http := initFakeHTTP()
http.StubResponse(200, bytes.NewBufferString(`
{ "node_id": "TEAMID",
"organization": { "node_id": "ORGID" }
}
`))
http.StubResponse(200, bytes.NewBufferString(`
{ "data": { "createRepository": {
"repository": {
"id": "REPOID",
"url": "https://github.com/ORG/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 ORG/REPO --team monkeys")
if err != nil {
t.Errorf("error running command `repo create`: %v", err)
}
eq(t, output.String(), "https://github.com/ORG/REPO\n")
eq(t, output.Stderr(), "")
if seenCmd == nil {
t.Fatal("expected a command to run")
}
eq(t, strings.Join(seenCmd.Args, " "), "git remote add origin https://github.com/ORG/REPO.git")
var reqBody struct {
Query string
Variables struct {
Input map[string]interface{}
}
}
if len(http.Requests) != 2 {
t.Fatalf("expected 2 HTTP requests, got %d", len(http.Requests))
}
eq(t, http.Requests[0].URL.Path, "/orgs/ORG/teams/monkeys")
bodyBytes, _ := ioutil.ReadAll(http.Requests[1].Body)
json.Unmarshal(bodyBytes, &reqBody)
if orgID := reqBody.Variables.Input["ownerId"].(string); orgID != "ORGID" {
t.Errorf("expected %q, got %q", "ORGID", orgID)
}
if teamID := reqBody.Variables.Input["teamId"].(string); teamID != "TEAMID" {
t.Errorf("expected %q, got %q", "TEAMID", teamID)
}
}
func TestRepoView(t *testing.T) {