From 5f7170024ec814001c8442aa73032b6a1af465f0 Mon Sep 17 00:00:00 2001 From: huiyifyj Date: Wed, 4 Jun 2025 14:14:01 +0800 Subject: [PATCH 1/5] fix: add missing `IsLocal` for BoolWithInverseFlag --- flag_bool_with_inverse.go | 4 ++++ godoc-current.txt | 2 ++ testdata/godoc-v3.x.txt | 2 ++ 3 files changed, 8 insertions(+) diff --git a/flag_bool_with_inverse.go b/flag_bool_with_inverse.go index 8d614552d4..272dd98fec 100644 --- a/flag_bool_with_inverse.go +++ b/flag_bool_with_inverse.go @@ -55,6 +55,10 @@ func (bif *BoolWithInverseFlag) RunAction(ctx context.Context, cmd *Command) err return nil } +func (bif *BoolWithInverseFlag) IsLocal() bool { + return bif.Local +} + func (bif *BoolWithInverseFlag) inversePrefix() string { if bif.InversePrefix == "" { bif.InversePrefix = DefaultInverseBoolPrefix diff --git a/godoc-current.txt b/godoc-current.txt index 44974f73ec..edd42feb88 100644 --- a/godoc-current.txt +++ b/godoc-current.txt @@ -367,6 +367,8 @@ func (bif *BoolWithInverseFlag) IsBoolFlag() bool func (bif *BoolWithInverseFlag) IsDefaultVisible() bool IsDefaultVisible returns true if the flag is not hidden, otherwise false +func (bif *BoolWithInverseFlag) IsLocal() bool + func (bif *BoolWithInverseFlag) IsRequired() bool func (bif *BoolWithInverseFlag) IsSet() bool diff --git a/testdata/godoc-v3.x.txt b/testdata/godoc-v3.x.txt index 44974f73ec..edd42feb88 100644 --- a/testdata/godoc-v3.x.txt +++ b/testdata/godoc-v3.x.txt @@ -367,6 +367,8 @@ func (bif *BoolWithInverseFlag) IsBoolFlag() bool func (bif *BoolWithInverseFlag) IsDefaultVisible() bool IsDefaultVisible returns true if the flag is not hidden, otherwise false +func (bif *BoolWithInverseFlag) IsLocal() bool + func (bif *BoolWithInverseFlag) IsRequired() bool func (bif *BoolWithInverseFlag) IsSet() bool From 233501616aeb47da874663e84bf511d82bf2c019 Mon Sep 17 00:00:00 2001 From: Ali Doustkani Date: Fri, 6 Jun 2025 20:50:31 +0200 Subject: [PATCH 2/5] add the tests --- command_test.go | 71 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/command_test.go b/command_test.go index f88fea9d05..00a2a649d9 100644 --- a/command_test.go +++ b/command_test.go @@ -5168,6 +5168,77 @@ func TestJSONExportCommand(t *testing.T) { assert.JSONEq(t, expected, string(out)) } +func TestCommand_ExclusiveFlags(t *testing.T) { + var ( + foo1 = &StringFlag{ + Name: "foo1", + } + foo2 = &StringFlag{ + Name: "foo2", + } + ) + cmd := &Command{ + Name: "bar", + Flags: []Flag{ + foo1, + foo2, + }, + MutuallyExclusiveFlags: []MutuallyExclusiveFlags{ + { + Flags: [][]Flag{ + { + foo1, + }, + { + foo2, + }, + }, + }, + }, + } + + err := cmd.Run(buildTestContext(t), []string{"bar", "--foo1", "var1", "--foo2", "var2"}) + + require.Equal(t, "option foo1 cannot be set along with option foo2", err.Error()) +} + +func TestCommand_ExclusiveFlagsWithOnUsageError(t *testing.T) { + var ( + foo1 = &StringFlag{ + Name: "foo1", + } + foo2 = &StringFlag{ + Name: "foo2", + } + ) + cmd := &Command{ + Name: "bar", + Flags: []Flag{ + foo1, + foo2, + }, + MutuallyExclusiveFlags: []MutuallyExclusiveFlags{ + { + Flags: [][]Flag{ + { + foo1, + }, + { + foo2, + }, + }, + }, + }, + OnUsageError: func(_ context.Context, _ *Command, _ error, _ bool) error { + return errors.New("my custom error") + }, + } + + err := cmd.Run(buildTestContext(t), []string{"bar", "--foo1", "v1", "--foo2", "v2"}) + + require.Equal(t, "my custom error", err.Error()) +} + func TestCommand_ExclusiveFlagsWithAfter(t *testing.T) { var called bool cmd := &Command{ From be9b01696a0f26f399f3eb10d8d278278185a89b Mon Sep 17 00:00:00 2001 From: Ali Doustkani Date: Fri, 6 Jun 2025 20:50:49 +0200 Subject: [PATCH 3/5] fix functionality --- command_run.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/command_run.go b/command_run.go index c1d450cf32..24b7935166 100644 --- a/command_run.go +++ b/command_run.go @@ -223,7 +223,11 @@ func (cmd *Command) run(ctx context.Context, osArgs []string) (_ context.Context for _, grp := range cmd.MutuallyExclusiveFlags { if err := grp.check(cmd); err != nil { - _ = ShowSubcommandHelp(cmd) + if cmd.OnUsageError != nil { + err = cmd.OnUsageError(ctx, cmd, err, cmd.parent != nil) + } else { + _ = ShowSubcommandHelp(cmd) + } return ctx, err } } From 7d84d04d904b0c4f1fbfb874f459cb35dd622263 Mon Sep 17 00:00:00 2001 From: Ali Doustkani Date: Fri, 6 Jun 2025 20:57:49 +0200 Subject: [PATCH 4/5] tidy --- command_test.go | 40 ++++++++++++---------------------------- 1 file changed, 12 insertions(+), 28 deletions(-) diff --git a/command_test.go b/command_test.go index 00a2a649d9..fe264f9362 100644 --- a/command_test.go +++ b/command_test.go @@ -5169,28 +5169,20 @@ func TestJSONExportCommand(t *testing.T) { } func TestCommand_ExclusiveFlags(t *testing.T) { - var ( - foo1 = &StringFlag{ - Name: "foo1", - } - foo2 = &StringFlag{ - Name: "foo2", - } - ) cmd := &Command{ Name: "bar", - Flags: []Flag{ - foo1, - foo2, - }, MutuallyExclusiveFlags: []MutuallyExclusiveFlags{ { Flags: [][]Flag{ { - foo1, + &StringFlag{ + Name: "foo1", + }, }, { - foo2, + &StringFlag{ + Name: "foo2", + }, }, }, }, @@ -5203,28 +5195,20 @@ func TestCommand_ExclusiveFlags(t *testing.T) { } func TestCommand_ExclusiveFlagsWithOnUsageError(t *testing.T) { - var ( - foo1 = &StringFlag{ - Name: "foo1", - } - foo2 = &StringFlag{ - Name: "foo2", - } - ) cmd := &Command{ Name: "bar", - Flags: []Flag{ - foo1, - foo2, - }, MutuallyExclusiveFlags: []MutuallyExclusiveFlags{ { Flags: [][]Flag{ { - foo1, + &StringFlag{ + Name: "foo1", + }, }, { - foo2, + &StringFlag{ + Name: "foo2", + }, }, }, }, From f76ffae49c0eba4d9fb6f3d9a64401102a125241 Mon Sep 17 00:00:00 2001 From: Ali Doustkani Date: Fri, 6 Jun 2025 21:00:25 +0200 Subject: [PATCH 5/5] tidy --- command_test.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/command_test.go b/command_test.go index fe264f9362..b9e69bc307 100644 --- a/command_test.go +++ b/command_test.go @@ -5195,6 +5195,7 @@ func TestCommand_ExclusiveFlags(t *testing.T) { } func TestCommand_ExclusiveFlagsWithOnUsageError(t *testing.T) { + expectedErr := errors.New("my custom error") cmd := &Command{ Name: "bar", MutuallyExclusiveFlags: []MutuallyExclusiveFlags{ @@ -5214,13 +5215,13 @@ func TestCommand_ExclusiveFlagsWithOnUsageError(t *testing.T) { }, }, OnUsageError: func(_ context.Context, _ *Command, _ error, _ bool) error { - return errors.New("my custom error") + return expectedErr }, } - err := cmd.Run(buildTestContext(t), []string{"bar", "--foo1", "v1", "--foo2", "v2"}) + actualErr := cmd.Run(buildTestContext(t), []string{"bar", "--foo1", "v1", "--foo2", "v2"}) - require.Equal(t, "my custom error", err.Error()) + require.ErrorIs(t, actualErr, expectedErr) } func TestCommand_ExclusiveFlagsWithAfter(t *testing.T) {