cli
cli copied to clipboard
Map sub-command to nested yaml elements
my question is...
( how can I bind flags to nested yaml elements ? )
I'm evaluation urfave/cli
- so I'm a newbiew here - as foundation for the new camel-k cli which is now based on spf13/cobra
and I'm trying to figure out how to property bind sub-command flags from a nested yaml structure.
As example the camel-k cli has a run
sub command with it's own flags like dependency
and I want to be able to load values for such flags from a YAML like:
run:
dependencies:
- foo
- bar
I've created the following example:
func main() {
var kubeconf string
var namespace string
var dependencies cli.StringSlice
flags := []cli.Flag{
&cli.StringFlag{
Name: "kamelconfig",
Usage: "The location of the kamel cli configuration",
Value: ".kamel/config.yaml",
},
altsrc.NewStringFlag(&cli.StringFlag{
Name: "kubeconf",
Usage: "The path to the kubernetes config file to use for CLI requests",
Destination: &kubeconf,
EnvVars: []string{"KUBECONFIG"},
}),
altsrc.NewStringFlag(&cli.StringFlag{
Name: "namespace",
Aliases: []string{"n"},
Usage: "The `namespace` to use for all operations",
Destination: &namespace,
}),
}
runFlags := []cli.Flag{
altsrc.NewStringSliceFlag(&cli.StringSliceFlag{
Name: "dependency",
Usage: "dependency",
Destination: &dependencies,
}),
}
app := &cli.App{
Name: "kamel",
Usage: "kamel",
EnableBashCompletion: true,
Before: altsrc.InitInputSourceWithContext(flags, altsrc.NewYamlSourceFromFlagFunc("kamelconfig")),
Flags: flags,
Commands: []*cli.Command{
{
Name: "run",
Usage: "run",
Flags: runFlags,
Action: func(c *cli.Context) error {
fmt.Println(dependencies)
return nil
},
},
},
Action: func(c *cli.Context) error {
fmt.Println(kubeconf)
fmt.Println(namespace)
return nil
},
}
err := app.Run(os.Args)
if err != nil {
log.Fatal(err)
}
}
I can't figure out how I can achieve what I'm looking for as:
- the
Before
option is set on the root command: do I need to set it on each command ? - is there a way to set the name of the field used to search in the yaml source ? as example here I'd like to set that the
dependency
get translated todependencies
and the full key should berun.dependencies
to properly navigate the path of the source
btw, I'm not even sure if it is possible
question +1
I'm also curious about this...
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.
Nope
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.
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.
Nope
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.
@lburgazzoli I added two lines to your code
package main
import (
"fmt"
"log"
"os"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v2/altsrc"
)
func main() {
var kubeconf string
var namespace string
var dependencies cli.StringSlice
flags := []cli.Flag{
&cli.StringFlag{
Name: "kamelconfig",
Usage: "The location of the kamel cli configuration",
Value: ".kamel/config.yaml",
},
altsrc.NewStringFlag(&cli.StringFlag{
Name: "kubeconf",
Usage: "The path to the kubernetes config file to use for CLI requests",
Destination: &kubeconf,
EnvVars: []string{"KUBECONFIG"},
}),
altsrc.NewStringFlag(&cli.StringFlag{
Name: "namespace",
Aliases: []string{"n"},
Usage: "The `namespace` to use for all operations",
Destination: &namespace,
}),
}
runFlags := []cli.Flag{
altsrc.NewStringSliceFlag(&cli.StringSliceFlag{
Name: "dependency",
Usage: "dependency",
Aliases: []string{"run.dependencies"}, // added this line
Destination: &dependencies,
}),
}
flags = append(flags, runFlags...) // added this line
app := &cli.App{
Name: "kamel",
Usage: "kamel",
EnableBashCompletion: true,
Before: altsrc.InitInputSourceWithContext(flags, altsrc.NewYamlSourceFromFlagFunc("kamelconfig")),
Flags: flags,
Commands: []*cli.Command{
{
Name: "run",
Usage: "run",
Flags: runFlags,
Action: func(c *cli.Context) error {
fmt.Println(dependencies)
return nil
},
},
},
Action: func(c *cli.Context) error {
fmt.Println(kubeconf)
fmt.Println(namespace)
return nil
},
}
err := app.Run(os.Args)
if err != nil {
log.Fatal(err)
}
}
and updated your .kamel/config.yaml as follows
$ cat .kamel/config.yaml
kubeconf: hello
namespace: dd
run:
dependencies:
- foo
- bar
This is what I get when I run the code
$ go run main.go run
{[foo bar] true}
$ go run main.go
hello
dd
@dearchap thanks for looking into this, two notes:
- dogin
flags = append(flags, runFlags...)
seems counter intuitive as therunFlags
do not belong to the root command - adding
run.dependencies
as an alias has a side effect right ? so one can do things likekamrl run --run.dependencies=foo
@lburgazzoli This would work
app := &cli.App{
Name: "kamel",
Usage: "kamel",
EnableBashCompletion: true,
Before: altsrc.InitInputSourceWithContext(flags, altsrc.NewYamlSourceFromFlagFunc("kamelconfig")),
Flags: flags,
Commands: []*cli.Command{
{
Name: "run",
Usage: "run",
Flags: runFlags,
Before: altsrc.InitInputSourceWithContext(runFlags, altsrc.NewYamlSourceFromFlagFunc("kamelconfig")),
Action: func(c *cli.Context) error {
fmt.Println(dependencies)
return nil
},
},
},
Action: func(c *cli.Context) error {
fmt.Println(kubeconf)
fmt.Println(namespace)
return nil
},
}
Yes you are correct in that you can use --run.dependencies=foo
on command line. Actually that is the correct behaviour of alias. The side effect is in traversing the json using that path.
What is the experience that you are looking for ?
What is the experience that you are looking for ?
I think what I see here is good enough,
As a suggestion, it would be nice if NewYamlSourceFromFlagFunc
would accept also some sort of root node
, like:
Before: altsrc.InitInputSourceWithContext(
runFlags,
altsrc.NewYamlSourceFromFlagFunc("kamelconfig", "run")),
The flag is searched up the context chain so it is not strictly necessary to specify the root node, unless you want the search to stop at current level