3 ms·
I use cobra sometimes, it's not that cobra or mow is too heavy, but I think that the oklog-style command parsing pattern is no more code or boiler-plate than us
by marpaia 9y ago
I use cobra sometimes, it's not that cobra or mow is too heavy, but I think that the oklog-style command parsing pattern is no more code or boiler-plate than using a library (for many use-cases) and it reduces developer overhead because most people are already familiar with how `flag` works. The whole pattern is really just the normal `flag` library and a switch statement, but it works really nicely because `flag` is really great and has a lot of features.
- justinsaccount 9y agoYeah.. I like cobra, but it seems to somewhat force you into using a lot of globals to accomplish things. The only downside to the oklog-style seems to be that you can't introspect the full cli tool at runtime, which rules out things like outputting documentation or shell completion files.
- zalmoxes 9y agoWe did use cobra in fleet, and worked hard not to use globals https://github.com/kolide/fleet/blob/f909f4808b17ffd5616c6c837cb86206c61dbe20/cmd/fleet/serve.go#L45 https://github.com/kolide/fleet/blob/f909f4808b17ffd5616c6c8... It's doable, but it was hard and while cobra is a nice library, it's probably overkill unless you're a project like kubernetes. Most projects in Go are very small in terms of configuration surface, and something like https://github.com/kolide/launcher/blob/9dcf149957b9e9757a24c300a019b3b8b95a0b3c/cmd/package-builder/package-builder.go#L153-L174 https://github.com/kolide/launcher/blob/9dcf149957b9e9757a24... is much cleaner IMO.
- justinsaccount 9y agoYeah, I think I will try the simpler approach. I generally just have stuff like tool [-debug] [-store sqlite] cmd [-opt] [arg] So, main needs to bootstraps logger and store and then delegate to downstream commands that almost always will consume the store and logger. I'm currently using PersistentPreRunE to bootstrap the logging and store.. but I'm not really happy with the end result and some things are awkward. Maybe it would be less awkward if cobra had a 'Context' I could stick things in. The last issue was I added a version subcommand, which kept creating the store. I ended up having to do PersistentPreRunE: func(cmd *cobra.Command, args []string) error { return nil }, PersistentPostRunE: func(cmd *cobra.Command, args []string) error { return nil }, Which would have been a lot simpler if I was doing things manually.
- tetraodonpuffer 9y agocobra / viper are nice, but to me they are a fair bit more heavyweight than mow. With mow also due to closures it seems easier to manage without globals, at least for programs where you don't also need a config file (which is where cobra / viper shines) but just simple cli it seems easier than rolling your own unless you just have one or two options.
- jawher 9y agoThanks for citing mow.cli [1] and glad you're liking it! And you are spot-on regarding closures: it was a design choice made expressly for the purpose of being able to: - scope command specific flag and args, instead of having one giant catch-all context map - declare real and typed Go variables instead of something like context.String("--option") - use the same pattern as the flag std package (flag.Bool, etc.) which I liked a lot Disclaimer: I am the author of mow.cli [1] https://github.com/jawher/mow.cli https://github.com/jawher/mow.cli
- tetraodonpuffer 9y agono problem and thanks for creating it! I also enjoyed how easy it is to extend it to support, say, enums: it was just a matter of adding flag interface support to my type and calling app.VarOpt to set it up, very straightforward