Add --remove-type flag to gh issue edit
Pair --type with --remove-type so callers can clear an issue's type without going through the interactive editor, mirroring the --milestone / --remove-milestone and --parent / --remove-parent patterns. The two type flags are mutually exclusive. UpdateIssueIssueType now sends a null issueTypeId when the caller passes an empty string, which is what the API requires to clear the field. The orchestrator fires the mutation when either IssueTypeID is non-empty or RemoveIssueType is set. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
parent
eb7397695f
commit
6bbe6e5bac
3 changed files with 66 additions and 8 deletions
|
|
@ -481,11 +481,12 @@ func (i Issue) CurrentUserComments() []Comment {
|
|||
return i.Comments.CurrentUserComments()
|
||||
}
|
||||
|
||||
// UpdateIssueIssueType sets the issue type on an issue.
|
||||
// UpdateIssueIssueType sets or clears the issue type on an issue. Pass an
|
||||
// empty issueTypeID to clear the issue type.
|
||||
func UpdateIssueIssueType(client *Client, hostname string, issueID string, issueTypeID string) error {
|
||||
type UpdateIssueIssueTypeInput struct {
|
||||
IssueID githubv4.ID `json:"issueId"`
|
||||
IssueTypeID githubv4.ID `json:"issueTypeId"`
|
||||
IssueID githubv4.ID `json:"issueId"`
|
||||
IssueTypeID *githubv4.ID `json:"issueTypeId"`
|
||||
}
|
||||
|
||||
var mutation struct {
|
||||
|
|
@ -496,10 +497,16 @@ func UpdateIssueIssueType(client *Client, hostname string, issueID string, issue
|
|||
} `graphql:"updateIssueIssueType(input: $input)"`
|
||||
}
|
||||
|
||||
var typeID *githubv4.ID
|
||||
if issueTypeID != "" {
|
||||
id := githubv4.ID(issueTypeID)
|
||||
typeID = &id
|
||||
}
|
||||
|
||||
variables := map[string]interface{}{
|
||||
"input": UpdateIssueIssueTypeInput{
|
||||
IssueID: githubv4.ID(issueID),
|
||||
IssueTypeID: githubv4.ID(issueTypeID),
|
||||
IssueTypeID: typeID,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -614,7 +621,8 @@ type DeferredUpdateIssueOptions struct {
|
|||
IssueID string
|
||||
Hostname string
|
||||
|
||||
IssueTypeID string
|
||||
IssueTypeID string
|
||||
RemoveIssueType bool
|
||||
|
||||
ParentID string
|
||||
ReplaceExistingParent bool
|
||||
|
|
@ -639,7 +647,7 @@ type DeferredUpdateIssueOptions struct {
|
|||
func DeferredUpdateIssue(client *Client, opts DeferredUpdateIssueOptions) error {
|
||||
var mutations []func() error
|
||||
|
||||
if opts.IssueTypeID != "" {
|
||||
if opts.IssueTypeID != "" || opts.RemoveIssueType {
|
||||
mutations = append(mutations, func() error {
|
||||
return UpdateIssueIssueType(client, opts.Hostname, opts.IssueID, opts.IssueTypeID)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -36,6 +36,8 @@ type EditOptions struct {
|
|||
IssueNumbers []int
|
||||
Interactive bool
|
||||
|
||||
RemoveIssueType bool
|
||||
|
||||
Parent string
|
||||
RemoveParent bool
|
||||
AddSubIssues []string
|
||||
|
|
@ -87,6 +89,7 @@ func NewCmdEdit(f *cmdutil.Factory, runF func(*EditOptions) error) *cobra.Comman
|
|||
$ gh issue edit 23 --body-file body.txt
|
||||
$ gh issue edit 23 34 --add-label "help wanted"
|
||||
$ gh issue edit 23 --type Bug
|
||||
$ gh issue edit 23 --remove-type
|
||||
$ gh issue edit 23 --parent 100
|
||||
$ gh issue edit 23 --remove-parent
|
||||
$ gh issue edit 100 --add-sub-issue 123,124
|
||||
|
|
@ -142,6 +145,14 @@ func NewCmdEdit(f *cmdutil.Factory, runF func(*EditOptions) error) *cobra.Comman
|
|||
return err
|
||||
}
|
||||
|
||||
if err := cmdutil.MutuallyExclusive(
|
||||
"specify only one of `--type` or `--remove-type`",
|
||||
flags.Changed("type"),
|
||||
opts.RemoveIssueType,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := cmdutil.MutuallyExclusive(
|
||||
"specify only one of --parent or --remove-parent",
|
||||
flags.Changed("parent"),
|
||||
|
|
@ -174,13 +185,14 @@ func NewCmdEdit(f *cmdutil.Factory, runF func(*EditOptions) error) *cobra.Comman
|
|||
opts.Editable.IssueType.Edited = true
|
||||
}
|
||||
|
||||
hasRelationshipFlags := flags.Changed("parent") || opts.RemoveParent ||
|
||||
hasDeferredFlags := opts.RemoveIssueType ||
|
||||
flags.Changed("parent") || opts.RemoveParent ||
|
||||
len(opts.AddSubIssues) > 0 || len(opts.RemoveSubIssues) > 0 ||
|
||||
len(opts.AddBlockedBy) > 0 || len(opts.RemoveBlockedBy) > 0 ||
|
||||
len(opts.AddBlocking) > 0 || len(opts.RemoveBlocking) > 0
|
||||
|
||||
// Drop into interactive mode only if the user passed no edit flags at all.
|
||||
if !opts.Editable.Dirty() && !hasRelationshipFlags {
|
||||
if !opts.Editable.Dirty() && !hasDeferredFlags {
|
||||
opts.Interactive = true
|
||||
}
|
||||
|
||||
|
|
@ -216,6 +228,7 @@ func NewCmdEdit(f *cmdutil.Factory, runF func(*EditOptions) error) *cobra.Comman
|
|||
cmd.Flags().StringVarP(&opts.Editable.Milestone.Value, "milestone", "m", "", "Edit the milestone the issue belongs to by `name`")
|
||||
cmd.Flags().BoolVar(&removeMilestone, "remove-milestone", false, "Remove the milestone association from the issue")
|
||||
cmd.Flags().StringVar(&opts.Editable.IssueType.Value, "type", "", "Set the issue type by `name`")
|
||||
cmd.Flags().BoolVar(&opts.RemoveIssueType, "remove-type", false, "Remove the issue type from the issue")
|
||||
cmd.Flags().StringVar(&opts.Parent, "parent", "", "Set the parent issue by `number` or URL")
|
||||
cmd.Flags().BoolVar(&opts.RemoveParent, "remove-parent", false, "Remove the parent issue")
|
||||
cmd.Flags().StringSliceVar(&opts.AddSubIssues, "add-sub-issue", nil, "Add sub-issues by `number` or URL")
|
||||
|
|
@ -458,6 +471,7 @@ func deferredUpdateIssueOptions(client *api.Client, baseRepo ghrepo.Interface, i
|
|||
IssueID: issue.ID,
|
||||
Hostname: baseRepo.RepoHost(),
|
||||
IssueTypeID: issueTypeID,
|
||||
RemoveIssueType: editOpts.RemoveIssueType,
|
||||
ReplaceExistingParent: true,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -297,6 +297,19 @@ func TestNewCmdEdit(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "remove-type flag",
|
||||
input: "23 --remove-type",
|
||||
output: EditOptions{
|
||||
IssueNumbers: []int{23},
|
||||
RemoveIssueType: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "both type and remove-type flags",
|
||||
input: "23 --type Bug --remove-type",
|
||||
wantsErr: true,
|
||||
},
|
||||
{
|
||||
name: "parent flag",
|
||||
input: "23 --parent 100",
|
||||
|
|
@ -855,6 +868,29 @@ func Test_editRun(t *testing.T) {
|
|||
},
|
||||
stdout: "https://github.com/OWNER/REPO/issue/123\n",
|
||||
},
|
||||
{
|
||||
name: "remove type",
|
||||
input: &EditOptions{
|
||||
Detector: &fd.EnabledDetectorMock{},
|
||||
IssueNumbers: []int{123},
|
||||
Interactive: false,
|
||||
RemoveIssueType: true,
|
||||
FetchOptions: prShared.FetchOptions,
|
||||
},
|
||||
httpStubs: func(t *testing.T, reg *httpmock.Registry) {
|
||||
mockIssueGet(t, reg)
|
||||
reg.Register(
|
||||
httpmock.GraphQL(`mutation UpdateIssueIssueType\b`),
|
||||
httpmock.GraphQLMutation(`
|
||||
{ "data": { "updateIssueIssueType": { "issue": { "id": "123" } } } }`,
|
||||
func(inputs map[string]interface{}) {
|
||||
assert.Equal(t, "123", inputs["issueId"])
|
||||
assert.Nil(t, inputs["issueTypeId"])
|
||||
}),
|
||||
)
|
||||
},
|
||||
stdout: "https://github.com/OWNER/REPO/issue/123\n",
|
||||
},
|
||||
{
|
||||
name: "interactive edit type prompt",
|
||||
input: &EditOptions{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue