Allow forcibly creating labels (#5451)
This commit is contained in:
parent
fc8739cf97
commit
1b7d03fa8a
2 changed files with 57 additions and 3 deletions
|
|
@ -3,6 +3,7 @@ package create
|
|||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
|
|
@ -45,6 +46,7 @@ type CreateOptions struct {
|
|||
Color string
|
||||
Description string
|
||||
Name string
|
||||
Force bool
|
||||
}
|
||||
|
||||
func NewCmdCreate(f *cmdutil.Factory, runF func(*CreateOptions) error) *cobra.Command {
|
||||
|
|
@ -81,10 +83,13 @@ func NewCmdCreate(f *cmdutil.Factory, runF func(*CreateOptions) error) *cobra.Co
|
|||
|
||||
cmd.Flags().StringVarP(&opts.Description, "description", "d", "", "Description of the label")
|
||||
cmd.Flags().StringVarP(&opts.Color, "color", "c", "", "Color of the label, if not specified one will be selected at random")
|
||||
cmd.Flags().BoolVarP(&opts.Force, "force", "f", false, "Set the label color and description even if the name already exists.")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
var errLabelAlreadyExists = errors.New("label already exists")
|
||||
|
||||
func createRun(opts *CreateOptions) error {
|
||||
httpClient, err := opts.HttpClient()
|
||||
if err != nil {
|
||||
|
|
@ -105,12 +110,15 @@ func createRun(opts *CreateOptions) error {
|
|||
err = createLabel(httpClient, baseRepo, opts)
|
||||
opts.IO.StopProgressIndicator()
|
||||
if err != nil {
|
||||
if errors.Is(err, errLabelAlreadyExists) {
|
||||
return fmt.Errorf("label with name %q already exists; use `--force` to update its color and description", opts.Name)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
if opts.IO.IsStdoutTTY() {
|
||||
cs := opts.IO.ColorScheme()
|
||||
successMsg := fmt.Sprintf("\n%s Label %q created in %s\n", cs.SuccessIcon(), opts.Name, ghrepo.FullName(baseRepo))
|
||||
successMsg := fmt.Sprintf("%s Label %q created in %s\n", cs.SuccessIcon(), opts.Name, ghrepo.FullName(baseRepo))
|
||||
fmt.Fprint(opts.IO.Out, successMsg)
|
||||
}
|
||||
|
||||
|
|
@ -130,5 +138,33 @@ func createLabel(client *http.Client, repo ghrepo.Interface, opts *CreateOptions
|
|||
}
|
||||
requestBody := bytes.NewReader(requestByte)
|
||||
result := shared.Label{}
|
||||
return apiClient.REST(repo.RepoHost(), "POST", path, requestBody, &result)
|
||||
err = apiClient.REST(repo.RepoHost(), "POST", path, requestBody, &result)
|
||||
|
||||
if httpError, ok := err.(api.HTTPError); ok && isLabelAlreadyExistsError(httpError) {
|
||||
err = errLabelAlreadyExists
|
||||
}
|
||||
|
||||
if opts.Force && errors.Is(err, errLabelAlreadyExists) {
|
||||
return updateLabel(apiClient, repo, opts)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func updateLabel(apiClient *api.Client, repo ghrepo.Interface, opts *CreateOptions) error {
|
||||
path := fmt.Sprintf("repos/%s/%s/labels/%s", repo.RepoOwner(), repo.RepoName(), opts.Name)
|
||||
requestByte, err := json.Marshal(map[string]string{
|
||||
"description": opts.Description,
|
||||
"color": opts.Color,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
requestBody := bytes.NewReader(requestByte)
|
||||
result := shared.Label{}
|
||||
return apiClient.REST(repo.RepoHost(), "PATCH", path, requestBody, &result)
|
||||
}
|
||||
|
||||
func isLabelAlreadyExistsError(err api.HTTPError) bool {
|
||||
return err.StatusCode == 422 && len(err.Errors) == 1 && err.Errors[0].Field == "name" && err.Errors[0].Code == "already_exists"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ func TestCreateRun(t *testing.T) {
|
|||
httpmock.StatusStringResponse(201, "{}"),
|
||||
)
|
||||
},
|
||||
wantStdout: "\n✓ Label \"test\" created in OWNER/REPO\n",
|
||||
wantStdout: "✓ Label \"test\" created in OWNER/REPO\n",
|
||||
},
|
||||
{
|
||||
name: "creates label notty",
|
||||
|
|
@ -115,6 +115,24 @@ func TestCreateRun(t *testing.T) {
|
|||
},
|
||||
wantStdout: "",
|
||||
},
|
||||
{
|
||||
name: "creates existing label",
|
||||
opts: &CreateOptions{Name: "test", Description: "some description", Force: true},
|
||||
httpStubs: func(reg *httpmock.Registry) {
|
||||
reg.Register(
|
||||
httpmock.REST("POST", "repos/OWNER/REPO/labels"),
|
||||
httpmock.WithHeader(
|
||||
httpmock.StatusStringResponse(422, `{"message":"Validation Failed","errors":[{"resource":"Label","code":"already_exists","field":"name"}]}`),
|
||||
"Content-Type",
|
||||
"application/json",
|
||||
),
|
||||
)
|
||||
reg.Register(
|
||||
httpmock.REST("PATCH", "repos/OWNER/REPO/labels/test"),
|
||||
httpmock.StatusStringResponse(201, "{}"),
|
||||
)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue