-
Notifications
You must be signed in to change notification settings - Fork 1.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Ability to add context values #1927
Comments
I agree context should be available everywhere 🥳 |
@nejtr0n What exactly are you trying to achieve ? when calling rootCmd.Run(ctx, args) you can pass in a ctx of your choice with whatever values you define. All subcmds will have a context with parent context set to that so you should have able to retrieve said values. Do you mean you want to pass in a new context to the Action function for the command based on what you set in Before ? I hadnt really thought about that use case. If you can give me a sample of Before/Action functions of your older code I can see what can be done. |
@bartekpacia I am thinking this would be valuable. What do you think ? |
It seems to be a valid feature request. @nejtr0n, could you show an API you'd like (just like you demonstrated what it looked like in "old"/v2 version of this module)? |
I just encountered this problem as well and hope that the CLI can have this capability. Based on my needs, the API usage scenario I hope for: var name string
cmd := &cli.Command{
Commands: []*cli.Command{
{
Name: "config",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "name",
Destination: &name,
},
},
Before: func(ctx context.Context, command *cli.Command) (context.Context, error) {
switch {
case strings.HasPrefix(name, "good-"):
return context.WithValue(ctx, "name", name), nil
case strings.HasPrefix(name, "bad-"):
return nil, fmt.Errorf("bad name: %s", name)
default:
return context.WithValue(ctx, "name", "unknown-"+name), nil
}
},
Action: func(ctx context.Context, command *cli.Command) error {
name := ctx.Value("name").(string)
fmt.Println(name)
return nil
},
},
},
} or var name string
cmd := &cli.Command{
Commands: []*cli.Command{
{
Name: "config",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "name",
Destination: &name,
},
},
Before: func(ctx context.Context, command *cli.Command) error {
switch {
case strings.HasPrefix(name, "good-"):
command.AddKey("name", name)
return nil
case strings.HasPrefix(name, "bad-"):
return fmt.Errorf("bad name: %s", name)
default:
command.AddKey("name", "unknown"+name)
return nil
}
},
Action: func(ctx context.Context, command *cli.Command) error {
name := command.GetValue("name").(string)
fmt.Println(name)
return nil
},
},
},
} Reason:I hope the function of Before is to validate the Flag and generate a value based on the validation (which could be a struct, a string, etc.), so that Take my current real needs as an example: var doCtx = &do.DoContext{}
var name string
cmd := &cli.Command{
Commands: []*cli.Command{
{
Name: "config",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "name",
Destination: &name,
},
},
Before: func(ctx context.Context, command *cli.Command) error {
dc, err := do.Setup(name);
if err != nil {
return err
}
doCtx = dc
return nil
},
Action: func(ctx context.Context, command *cli.Command) error {
return dc.Start()
},
},
},
} Personally, I prefer the second option because it won't make destructive changes to v3 (even though it's currently in alpha), and it allows Action to have the same capabilities, so that After can also handle things based on the values set by Action :) BTW. I am interested in submitting a PR for this feat. |
@BlackHole1 Can you submit a PR ? |
@dearchap Of course, but before submitting a PR, I need to know which one urfave/cli currently prefers? var name string
cmd := &cli.Command{
Commands: []*cli.Command{
{
Name: "config",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "name",
Destination: &name,
},
},
Before: func(ctx context.Context, command *cli.Command) (context.Context, error) {
switch {
case strings.HasPrefix(name, "good-"):
return context.WithValue(ctx, "name", name), nil
case strings.HasPrefix(name, "bad-"):
return nil, fmt.Errorf("bad name: %s", name)
default:
return context.WithValue(ctx, "name", "unknown-"+name), nil
}
},
Action: func(ctx context.Context, command *cli.Command) error {
name := ctx.Value("name").(string)
fmt.Println(name)
return nil
},
},
},
} var name string
cmd := &cli.Command{
Commands: []*cli.Command{
{
Name: "config",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "name",
Destination: &name,
},
},
Before: func(ctx context.Context, command *cli.Command) error {
switch {
case strings.HasPrefix(name, "good-"):
command.AddKey("name", name)
return nil
case strings.HasPrefix(name, "bad-"):
return fmt.Errorf("bad name: %s", name)
default:
command.AddKey("name", "unknown"+name)
return nil
}
},
Action: func(ctx context.Context, command *cli.Command) error {
name := command.GetValue("name").(string)
fmt.Println(name)
return nil
},
},
},
} |
The first one seems better. @urfave/cli want to chime in ? Alternatively we could have Before with the current signature and also BeforeWithContext with new signature. Depends on how we want cli/v3 to evolve. I can understand the need for subcommands to pass new contexts down the hierarchy. |
To add another datapoint, our current CLI has a global |
Checklist
What problem does this solve?
In old versions we could add context fields in Before function like this
It's useful for example for passing logger through all commands.
Solution description
Add some middleware layer to command with signature
where we could modify passing ctx
Describe alternatives you've considered
Return back Context field to command
The text was updated successfully, but these errors were encountered: