cli
cli copied to clipboard
Accessing global flags from subcommand
Here is how I am accessing global flags from a subcommand. This func also handles accessing a global flag when the provided context is also global, because it simplifies code.
func globalFlag(name string, c *cli.Context) interface{} {
ancestor := c.Lineage()[len(c.Lineage())-1]
if len(ancestor.Args().Slice()) == 0 {
// When the global context calls this func, the last in the lineage
// has no args for some reason. So return the second-last instead.
return c.Lineage()[len(c.Lineage())-2].Value(name)
}
return ancestor.Value(name)
}
This has been working for me. Is there a better way or built-in way to do this? Also, do you know why I have to handle that special case? Is that a bug?
This issue or PR has been automatically marked as stale because it has not had recent activity. Please add a comment bumping this if you're still interested in it's resolution! Thanks for your help, please let us know if you need anything else.
Bump
This issue or PR has been bumped and is no longer marked as stale! Feel free to bump it again in the future, if it's still relevant.
Duplicate of #427? Looks like this has been asked about several times over the span of years.
@dargueta no, I'm interested in global flags staying in their usual place, but being accessible from within subcommand code.
Oh, I misunderstood. Ignore me! 😅
@makeworld-the-better-one The way you do it is fine. I dont think there is a better way.
@dearchap Well this is obviously kind of a hack, and not something simple for a library user to figure out on their own. If what I'm doing is the best way, then it should be added to the library API as function for getting global args.
And there is even potentially a bug in the library code, as you can see from my original issue comment.
[...] this is obviously kind of a hack, and not something simple for a library user to figure out on their own. If what I'm doing is the best way, then it should be added to the library API as function for getting global args.
@makeworld-the-better-one I agree and I would love too see an addition to the library API to help with this 🤘🏼 Is this something you're up for contributing?
Not right at the moment, but maybe some time in the next couple weeks. I'll comment here if I take it on, so if anyone else wants to before then feel free.
@makeworld-the-better-one couldnt you just use context.Value('globalFlagName') ?
@makeworld-the-better-one after thinking about this a bit more, I'm recalling the Global{Type}
functions that used to be available on *cli.Context
in v1. The example code you provide in the description is doing a similar dance to what's in the v1 code:
https://github.com/urfave/cli/blob/c24c9f398d5d925f8c3d990509e8c7b3ae942646/context.go#L245-L255
It seems to me now that dropping the Global{Type}
functions in favor of a unified interface that automatically traverses the lineage may not have been sufficient:
https://github.com/urfave/cli/blob/d62ac9c02e3224397c92e27a0d973477092f58d4/context.go#L165-L176
In your usage, are you finding that the internal traversal of context lineage isn't working because there's a flag name conflict or similar?
@meatballhat it's been a while since I used this code, so I'm not sure right now. I don't believe there was a conflict, just that I couldn't easily access the value of a global flag from within a subcommand context.
@makew0rld I really am not seeing the issue.
package main
import (
"log"
"os"
"github.com/urfave/cli/v2"
)
func dummyAction(c *cli.Context) error {
log.Printf("%d", c.NumFlags())
log.Printf("val %v", c.Value("hello"))
for index, ctx := range c.Lineage() {
log.Printf("%d %v", index, ctx.Value("hello"))
if ctx.App != nil {
log.Printf("%v", ctx.App.Flags)
}
}
return nil
}
var (
command1 = cli.Command{
Name: "command1",
Usage: "command1.Usage",
Description: "command1.Description",
Subcommands: []*cli.Command{
{
Action: dummyAction,
Name: "action1",
Usage: "command1action1.Usage",
ArgsUsage: "<arg1>",
Description: "command1action1.Description",
},
{
Action: dummyAction,
Name: "action2",
Usage: "command1action2.Usage",
ArgsUsage: "<arg1> [<arg2>]",
Description: "command1action2.Description",
},
{
Action: dummyAction,
Name: "action3",
Usage: "command1action3.Usage",
ArgsUsage: "<arg1> [<arg2>]...",
Description: "command1action3.Description",
},
},
}
command2 = cli.Command{
Action: dummyAction,
Name: "command2",
Usage: "command2.Usage",
Description: "command2.Description",
Subcommands: []*cli.Command{
{
Action: dummyAction,
Name: "action1",
Usage: "command2action1.Usage",
ArgsUsage: "<arg1>",
Description: "command2action1.Description",
},
{
Action: dummyAction,
Name: "action2",
Usage: "command2action2.Usage",
ArgsUsage: "<arg1> [<arg2>]",
Description: "command2action2.Description",
},
{
Action: dummyAction,
Name: "action3",
Usage: "command2action3.Usage",
ArgsUsage: "<arg1> [<arg2>]...",
Description: "command2action3.Description",
},
},
}
command3 = cli.Command{
Action: dummyAction,
Name: "command3",
Usage: "command3.Usage",
ArgsUsage: "<arg1> [<arg2>]...",
Description: "command3.Description",
}
)
func main() {
app := cli.NewApp()
app.Name = "app.Name"
//app.Usage = "app.Usage"
app.Version = "app.Version"
app.EnableBashCompletion = true
app.Flags = []cli.Flag{
&cli.StringFlag{
Name: "hello",
},
}
app.Commands = []*cli.Command{
&command1,
&command2,
&command3,
}
app.Run(os.Args)
}
$ ./main --hello foo command2
2023/06/07 20:45:42 0
2023/06/07 20:45:42 val foo
You can get the value of a global flag from a subcommand action. If you mean set the value of a global flag in a subcommand that support is available in v3 via marking a flag as persistent
can this tun into a feature? I have a cli im working on soon where every sub-command will need a --env flag
This is a feature in v3. It's already implemented and working. You can try it out right now
@dearchap can you point me to v3? i see no branch/tag/release/docs/etc
@acidjazz v3
is what's in progress in the main
branch. There are a few alpha tags available which may be installed/used via the /v3
suffix like:
go get github.com/urfave/cli/v3
I hope this helps! 😁