Here is an snippet from my-tool/cmd/root.go
func Execute() {
if err := rootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}
func init() {
cobra.OnInitialize(initConfig)
// Here you will define your flags and configuration settings.
// Cobra supports persistent flags, which, if defined here,
// will be global for your application.
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.my-tool.yaml)")
// Cobra also supports local flags, which will only run
// when this action is called directly.
rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}
// initConfig reads in config file and ENV variables if set.
func initConfig() {
if cfgFile != "" {
fmt.Println("Config file set")
// Use config file from the flag.
viper.SetConfigFile(cfgFile)
} else {
fmt.Println("Config file NOT set")
// Find home directory.
home, err := homedir.Dir()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
The code has been generated from the scaffolding process of cobra cli, i.e. via ~/go/cobra/init my-tool --pkg-name github.com/something/my-tool
I am trying to tentatively pass the config flag to check if the program is handling it:
▶ go run main.go --config "test"
However, though I 'd expect the init() function to make the call to cobra.OnInitialize(initConfig) and parse the flag as indicated by line:
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.my-tool.yaml)")
and finally to see one of those two messages in the if statement:
func initConfig() {
if cfgFile != "" {
fmt.Println("Config file set")
// Use config file from the flag.
viper.SetConfigFile(cfgFile)
} else {
fmt.Println("Config file NOT set")
// Find home directory.
home, err := homedir.Dir()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
instead, all I get is the root command's help message; why is that?
edit: from what I see by adding some print statements, the initConfig() is never called (for some reason), i.e. as if cobra.OnInitialize(initConfig) does not do anything.
You need to Specify your command first
▶ go run main.go "yourcommand" --config "test"
See:
» go run main.go --config "blah"
A longer description that spans multiple lines and likely contains
examples and usage of using your application. For example:
...
» go run main.go preview --config "blah"
Error: open : no such file or directory
exit status 1
Related
is there a way of dynamically registering cobra commands based on a viper configuration file? Basically I want to only register commands that are available within my config file as "modules".
My root.go's init function looks like this
func init() {
cobra.OnInitialize(initConfig)
//don't I have access to viper.GetString("foo") here? It returns empty string instead of "bar"
registerCommandsPalette()
//disable help command
rootCmd.SetHelpCommand(&cobra.Command{
Use: "no-help",
Hidden: true,
})
//disable completion command
rootCmd.CompletionOptions.DisableDefaultCmd = true
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.toolbox.yaml)")
}
func registerCommandsPalette() {
//here I want to access the config e.g. viper.GetString("foo")
//on various conditions I want to add a command to rootCmd.
rootCmd.AddCommand(server.ServerCmd)
rootCmd.AddCommand(net.NetCmd)
}
func initConfig() {
if cfgFile != "" {
// Use config file from the flag.
viper.SetConfigFile(cfgFile)
} else {
// Find home directory.
home, err := os.UserHomeDir()
cobra.CheckErr(err)
// Search config in home directory with name ".toolbox" (without extension).
viper.AddConfigPath(home)
viper.SetConfigType("json")
viper.SetConfigName(".toolbox")
}
viper.AutomaticEnv() // read in environment variables that match
// If a config file is found, read it in.
if err := viper.ReadInConfig(); err != nil {
fmt.Println("Could not load configuration file")
os.Exit(1)
}
}
My config file looks like this:
{
"available_modules": [
"module-1",
"module-2"
],
"foo": "bar"
//....
}
Whenever I use viper.GetString("foo") in a command file, I do get the string "bar" back.
Do you have any ideas?
Best,
Jakob
I'm iterating through a mounted folder via filePath.Walk method in golang, but it returns the hidden files as well. I have to skip those hidden files.
For MaxOS and Linux, we can detect hidden file via .prefix in the filename, but for windows, when I'm trying to us this method GetFileAttributes, provided by "syscall", it's not detecting these methods and throwing an error.
Using below method to fetch the file
err := filepath.Walk(prefix, func(docPath string, f os.FileInfo, err error) error {
Below is how I'm trying to detect the hidden files
import (
"runtime"
"syscall"
)
func IsHiddenFile(filename string) (bool, error) {
if runtime.GOOS == "windows" {
pointer, err := syscall.UTF16PtrFromString(filename)
if err != nil {
return false, err
}
attributes, err := syscall.GetFileAttributes(pointer)
if err != nil {
return false, err
}
return attributes&syscall.FILE_ATTRIBUTE_HIDDEN != 0, nil
} else {
// unix/linux file or directory that starts with . is hidden
if filename[0:1] == "." {
return true, nil
}
}
return false, nil
}
Error :
.../ undefined: syscall.UTF16PtrFromString
.../ undefined: syscall.GetFileAttributes
.../ undefined: syscall.FILE_ATTRIBUTE_HIDDEN
I added this // +build windows in the start of the file before package name as suggested here : syscall variables undefined but it's sill not working and throwing the same error.
I need to know if go provides some common method to detect if a file is hidden or not? Or is there a way I can fetch all the files/folder in some mounted directory without receiving hidden files in the first place?
Really looking forward on receiving some feedback here, thanks.
EDIT : Fixed the above mentioned issue (please refer to the comments below), I also wants to know how can we detect the hidden file when we are connected with the remote server (SMB), the remoter system could be running any OS, and we compile these method based on the system it's running on. How can we detect Hidden files in that scenario ?
Conditional compilation is the right way to go, but it applies at source file level, so you need two separate files.
For example:
hidden_notwin.go:
//go:build !windows
package main
func IsHiddenFile(filename string) (bool, error) {
return filename[0] == '.', nil
}
hidden_windows.go:
//go:build windows
package main
import (
"syscall"
)
func IsHiddenFile(filename string) (bool, error) {
pointer, err := syscall.UTF16PtrFromString(filename)
if err != nil {
return false, err
}
attributes, err := syscall.GetFileAttributes(pointer)
if err != nil {
return false, err
}
return attributes&syscall.FILE_ATTRIBUTE_HIDDEN != 0, nil
}
Note that //go:build windows tag above is optional - the _windows source file suffix does the magic already. For more details see go command - Build constraints.
I'm having trouble using Cobra and Viper together. This is what I'm doing:
var options util.Config = util.Config{}
var rootCmd = &cobra.Command{
Use: "test [command] [subcommands]",
Run: func(cmd *cobra.Command, args []string) {
if err := server.Run(); err != nil {
l.Fatal(err)
}
},
}
// initConfig helps initialise configuration with a stated path
func initConfig() {
if options.Path != "" {
viper.SetConfigFile(options.Path)
}
viper.AutomaticEnv()
if err := viper.ReadInConfig(); err != nil {
fmt.Println("Could not use config file: ", viper.ConfigFileUsed())
}
}
func init() {
cobra.OnInitialize(initConfig)
rootCmd.PersistentFlags().StringVarP(&options.Path, "config", "n", "", "Path of a configuration file")
rootCmd.PersistentFlags().StringVarP(&options.Password, "password", "d", "", "Password to access the server")
viper.BindPFlag("password", rootCmd.PersistentFlags().Lookup("password"))
rootCmd.AddCommand(log.Cmd(&options))
}
func main() {
rootCmd.Execute()
}
I'm trying to retrieve the value options.Password within my subcommand (an added command within log.Cmd(&options)) however the field isn't being populated. I'm pretty sure I'm following the Cobra docs properly: https://github.com/spf13/cobra#create-rootcmd
Binding cobra flags to viper options only binds cobra flags to viper options, not vice versa. So you can access the password via
pass := viper.GetString("password")
if the password is set either via viper or cobra, but not via the variables defined in your flag definitions.
Basically, you have two options here: Either you use cobra without pointing your flags to variables, and then set your globals via various calls to viper.Get* (you can even sanitize them while being at it), or you use viper as sort of a „parameter registry“ and call viper.Get* where needed. I tend to go with the former solution.
Using cobra, if my app is invoked without a specific action (but arguments), I'd like to run a default command:
// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
Use: "mbmd",
Short: "ModBus Measurement Daemon",
Long: "Easily read and distribute data from ModBus meters and grid inverters",
Run: func(cmd *cobra.Command, args []string) {
run(cmd, args)
},
}
However, since the root command doesn't have all arguments the child command has this fails as it's apparently now aware of the child command's arguments:
❯ go run main.go -d sma:126#localhost:5061 --api 127.1:8081 -v
Error: unknown shorthand flag: 'd' in -d
as opposed to:
❯ go run main.go run -d sma:126#localhost:5061 --api 127.1:8081 -v
2019/07/29 20:58:10 mbmd unknown version (unknown commit)
How can I programmatically instantiate/invoke a child command?
Here is another solution:
cmd, _, err := rootCmd.Find(os.Args[1:])
// default cmd if no cmd is given
if err == nil && cmd.Use == rootCmd.Use && cmd.Flags().Parse(os.Args[1:]) != pflag.ErrHelp {
args := append([]string{defaultCmd.Use}, os.Args[1:]...)
rootCmd.SetArgs(args)
}
if err := rootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
Replace defaultCmd with one you want to be default
This part cmd.Flags().Parse(os.Args[1:]) != pflag.ErrHelp keeps help command working for root command if no arguments was set
March 2021: You might consider a workaround as the one presented in spf13/cobra issue 823
func subCommands() (commandNames []string) {
for _, command := range cmd.Commands() {
commandNames = append(commandNames, append(command.Aliases, command.Name())...)
}
return
}
func setDefaultCommandIfNonePresent() {
if len(os.Args) > 1 {
potentialCommand := os.Args[1]
for _, command := range subCommands() {
if command == potentialCommand {
return
}
}
os.Args = append([]string{os.Args[0], "<default subcommand>"}, os.Args[1:]...)
}
}
func main() {
setDefaultCommandIfNonePresent()
if err := cmd.Execute(); err != nil {
zap.S().Error(err)
os.Exit(1)
}
}
The difference here is that it checks if len(os.Args) > 1 before changing the default subcommand.
This means that, if ran without any arguments, it will print the default help command (with all of the subcommands).
Otherwise, if supplied any arguments, it will use the subcommand.
So, it will display the main 'help' without arguments, and the subcommand's help if supplied '-h'/'--help'.
Or (Oct. 2021), from the author of PR 823:
Latest solve for this is the following:
main.go
func main() {
// Define the default sub command 'defCmd' here. If user doesn't submit
// using a default command, we'll use what is here.
defCmd:="mydefaultcmd"
cmd.Execute(defCmd)
}
root.go
func Execute(defCmd string) {
var cmdFound bool
cmd :=rootCmd.Commands()
for _,a:=range cmd{
for _,b:=range os.Args[1:] {
if a.Name()==b {
cmdFound=true
break
}
}
}
if !cmdFound {
args:=append([]string{defCmd}, os.Args[1:]...)
rootCmd.SetArgs(args)
}
if err := rootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}
The documentation in Cobra and Viper are confusing me. I did cobra init fooproject and then inside the project dir I did cobra add bar. I have a PersistentFlag that is named foo and here is the init function from the root command.
func Execute() {
if err := RootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(-1)
}
fmt.Println(cfgFile)
fmt.Println("fooString is: ", fooString)
}
func init() {
cobra.OnInitialize(initConfig)
// Here you will define your flags and configuration settings.
// Cobra supports Persistent Flags, which, if defined here,
// will be global for your application.
RootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.fooproject.yaml)")
RootCmd.PersistentFlags().StringVar(&fooString, "foo", "", "loaded from config")
viper.BindPFlag("foo", RootCmd.PersistentFlags().Lookup("foo"))
// Cobra also supports local flags, which will only run
// when this action is called directly.
RootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}
My configuration file looks like this...
---
foo: aFooString
And when I call go run main.go I see this...
A longer description that spans multiple lines and likely contains
examples and usage of using your application. For example:
Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.
Usage:
fooproject [command]
Available Commands:
bar A brief description of your command
help Help about any command
Flags:
--config string config file (default is $HOME/.fooproject.yaml)
--foo string loaded from config
-h, --help help for fooproject
-t, --toggle Help message for toggle
Use "fooproject [command] --help" for more information about a command.
fooString is:
When I call go run main.go bar I see this...
Using config file: my/gopath/github.com/user/fooproject/.fooproject.yaml
bar called
fooString is:
So it is using the configuration file, but neither one of them seems to be reading it. Maybe I am misunderstanding the way that Cobra and Viper work. Any ideas?
To combine spf13/cobra and spf13/viper, first define the flag with Cobra:
RootCmd.PersistentFlags().StringP("foo", "", "loaded from config")
Bind it with Viper:
viper.BindPFlag("foo", RootCmd.PersistentFlags().Lookup("foo"))
And get the variable via the Viper's method:
fmt.Println("fooString is: ", viper.GetString("foo"))
Since Viper values are somewhat inferior to pflags (e.g. no support for custom data types), I was not satisfied with answer "use Viper to retrieve values", and ended up writing small helper type to put values back in pflags.
type viperPFlagBinding struct {
configName string
flagValue pflag.Value
}
type viperPFlagHelper struct {
bindings []viperPFlagBinding
}
func (vch *viperPFlagHelper) BindPFlag(configName string, flag *pflag.Flag) (err error) {
err = viper.BindPFlag(configName, flag)
if err == nil {
vch.bindings = append(vch.bindings, viperPFlagBinding{configName, flag.Value})
}
return
}
func (vch *viperPFlagHelper) setPFlagsFromViper() {
for _, v := range vch.bindings {
v.flagValue.Set(viper.GetString(v.configName))
}
}
func main() {
var rootCmd = &cobra.Command{}
var viperPFlagHelper viperPFlagHelper
rootCmd.PersistentFlags().StringVar(&config.Password, "password", "", "API server password (remote HTTPS mode only)")
viperPFlagHelper.BindPFlag("password", rootCmd.Flag("password"))
rootCmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error {
err := viper.ReadInConfig()
if err != nil {
if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
return err
}
}
viperPFlagHelper.setPFlagsFromViper()
return nil
}
}
For those facing the same issue, the problem is on this call:
cobra.OnInitialize(initConfig)
The function initConfig is not executed directly but append to an array of initializers https://github.com/spf13/cobra/blob/master/cobra.go#L80
So, the values stored in your config file will be loaded when the Run field is executed:
rootCmd = &cobra.Command{
Use: "example",
Short: "example cmd",
Run: func(cmd *cobra.Command, args []string) { // OnInitialize is called first
fmt.Println(viper.AllKeys())
},
}
#WGH 's answer is correct, you can do something with the persistent flag in rootCmd.PersistenPreRun, which will be available in all other sub commands.
var rootCmd = &cobra.Command{
PersistentPreRun: func(_ *cobra.Command, _ []string) {
if persistentflag {
// do something with persistentflag
}
},