My urfave/cli version is
v3.10.1
Checklist
Dependency Management
- My project is using go modules.
- My project is automatically downloading the latest version.
Describe the bug
BoolWithInverseFlag does NOT honor its property DefaultText, which, if I am not mistaken, should override the (default: […]) output in the help message regardless of any actual value.
To reproduce
- Create a new Go module
- Add urfave/cli
- Copy/paste the following snippet and run it
package main
import (
"context"
"os"
"github.com/urfave/cli/v3"
)
var cmd = &cli.Command{
Name: "test",
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "debug",
Usage: "set log level to DEBUG",
Value: true,
DefaultText: "blabla", // for comparison
},
# control output color
&cli.BoolWithInverseFlag{
// accepts (true|false|auto) where "auto" is tty-aware
// --color (not set) = auto
// --color (set) = ture
// --no-color = false
Name: "color",
Usage: "colorise text output",
// BUG: default text is not shown but the actual default value "false"
DefaultText: "auto",
},
},
}
func main() {
cmd.Run(context.Background(), os.Args)
}
Observed behavior
$ go run main.go
NAME:
test - A new cli application
USAGE:
test [global options]
GLOBAL OPTIONS:
--debug set log level to DEBUG (default: blabla)
--[no-]color colorise text output (default: false)
--help, -h show help
Expected behavior
I specified DefaultText: "auto" for the --[no-]color flag and expected the help message to print this value, regardless of the actual default value.
Additional context
I was basically trying to achieve a ternary state flag using BoolWithInverseFlag and Flag.IsSet, where the flag is not required and defaults to "auto":
- default (user did nothing) → "auto" (i.e.,
IsSet evaluates to false)
- user set
--color → true
- user set
--no-color → false
This flag would allow to explicitly control colour printing in the application's output. If left untouched (=auto), it would automatically check whether the output stream (usually STDERR) is a TTY, and then set a proper value (TTY→true, no TTY→false).
Reading #2214 (comment), the current behaviour I perceived as a bug could be intentional though, as boolean flags usually suggest to have a binary state only.
Want to fix this yourself?
I am not certain, but it appears that this method is the culprit:
|
func (bif *BoolWithInverseFlag) GetDefaultText() string { |
|
if bif.Required { |
|
return bif.DefaultText |
|
} |
|
return boolValue{}.ToString(bif.Value) |
|
} |
if bif.Required {
return bif.DefaultText
}
is also a dead branch, because
https://github.com/urfave/cli/blob/main/docs.go#L108-L117
prevents the (default: […]) text from being rendered at all if the flag is required.
So the obvious solution could simply be:
func (bif *BoolWithInverseFlag) GetDefaultText() string {
if bif.DefaultText != "" {
return bif.DefaultText
}
return boolValue{}.ToString(bif.Value)
}
I changed that locally and modified/extended the tests accordingly. They pass but
- with
Required=true and no DefaultText, GetDefaultText() now returns "false" (was "" before)
- the suppressing of the default text now depends on external factors (
GetDefaultText probably should not handle that anyway)
Run go version and paste its output here
go version go1.26.5 darwin/arm64
Run go env and paste its output here
My urfave/cli version is
v3.10.1
Checklist
Dependency Management
Describe the bug
BoolWithInverseFlagdoes NOT honor its propertyDefaultText, which, if I am not mistaken, should override the(default: […])output in the help message regardless of any actual value.To reproduce
Observed behavior
Expected behavior
I specified
DefaultText: "auto"for the--[no-]colorflag and expected the help message to print this value, regardless of the actual default value.Additional context
I was basically trying to achieve a ternary state flag using
BoolWithInverseFlagandFlag.IsSet, where the flag is not required and defaults to "auto":IsSetevaluates to false)--color→ true--no-color→ falseThis flag would allow to explicitly control colour printing in the application's output. If left untouched (=auto), it would automatically check whether the output stream (usually STDERR) is a TTY, and then set a proper value (TTY→true, no TTY→false).
Reading #2214 (comment), the current behaviour I perceived as a bug could be intentional though, as boolean flags usually suggest to have a binary state only.
Want to fix this yourself?
I am not certain, but it appears that this method is the culprit:
cli/flag_bool_with_inverse.go
Lines 221 to 226 in c6f4cf7
is also a dead branch, because
https://github.com/urfave/cli/blob/main/docs.go#L108-L117
prevents the
(default: […])text from being rendered at all if the flag is required.So the obvious solution could simply be:
I changed that locally and modified/extended the tests accordingly. They pass but
Required=true and noDefaultText,GetDefaultText()now returns "false" (was "" before)GetDefaultTextprobably should not handle that anyway)Run
go versionand paste its output hereRun
go envand paste its output here