github.com/pure-x-eth/consensus_tm@v0.0.0-20230502163723-e3c2ff987250/libs/cli/helper.go (about) 1 package cli 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "os" 8 "path/filepath" 9 10 "github.com/spf13/cobra" 11 ) 12 13 // WriteConfigVals writes a toml file with the given values. 14 // It returns an error if writing was impossible. 15 func WriteConfigVals(dir string, vals map[string]string) error { 16 data := "" 17 for k, v := range vals { 18 data += fmt.Sprintf("%s = \"%s\"\n", k, v) 19 } 20 cfile := filepath.Join(dir, "config.toml") 21 return os.WriteFile(cfile, []byte(data), 0o600) 22 } 23 24 // RunWithArgs executes the given command with the specified command line args 25 // and environmental variables set. It returns any error returned from cmd.Execute() 26 func RunWithArgs(cmd Executable, args []string, env map[string]string) error { 27 oargs := os.Args 28 oenv := map[string]string{} 29 // defer returns the environment back to normal 30 defer func() { 31 os.Args = oargs 32 for k, v := range oenv { 33 os.Setenv(k, v) 34 } 35 }() 36 37 // set the args and env how we want them 38 os.Args = args 39 for k, v := range env { 40 // backup old value if there, to restore at end 41 oenv[k] = os.Getenv(k) 42 err := os.Setenv(k, v) 43 if err != nil { 44 return err 45 } 46 } 47 48 // and finally run the command 49 return cmd.Execute() 50 } 51 52 // RunCaptureWithArgs executes the given command with the specified command 53 // line args and environmental variables set. It returns string fields 54 // representing output written to stdout and stderr, additionally any error 55 // from cmd.Execute() is also returned 56 func RunCaptureWithArgs(cmd Executable, args []string, env map[string]string) (stdout, stderr string, err error) { 57 oldout, olderr := os.Stdout, os.Stderr // keep backup of the real stdout 58 rOut, wOut, _ := os.Pipe() 59 rErr, wErr, _ := os.Pipe() 60 os.Stdout, os.Stderr = wOut, wErr 61 defer func() { 62 os.Stdout, os.Stderr = oldout, olderr // restoring the real stdout 63 }() 64 65 // copy the output in a separate goroutine so printing can't block indefinitely 66 copyStd := func(reader *os.File) *(chan string) { 67 stdC := make(chan string) 68 go func() { 69 var buf bytes.Buffer 70 // io.Copy will end when we call reader.Close() below 71 io.Copy(&buf, reader) //nolint:errcheck //ignore error 72 stdC <- buf.String() 73 }() 74 return &stdC 75 } 76 outC := copyStd(rOut) 77 errC := copyStd(rErr) 78 79 // now run the command 80 err = RunWithArgs(cmd, args, env) 81 82 // and grab the stdout to return 83 wOut.Close() 84 wErr.Close() 85 stdout = <-*outC 86 stderr = <-*errC 87 return stdout, stderr, err 88 } 89 90 // NewCompletionCmd returns a cobra.Command that generates bash and zsh 91 // completion scripts for the given root command. If hidden is true, the 92 // command will not show up in the root command's list of available commands. 93 func NewCompletionCmd(rootCmd *cobra.Command, hidden bool) *cobra.Command { 94 flagZsh := "zsh" 95 cmd := &cobra.Command{ 96 Use: "completion", 97 Short: "Generate shell completion scripts", 98 Long: fmt.Sprintf(`Generate Bash and Zsh completion scripts and print them to STDOUT. 99 100 Once saved to file, a completion script can be loaded in the shell's 101 current session as shown: 102 103 $ . <(%s completion) 104 105 To configure your bash shell to load completions for each session add to 106 your $HOME/.bashrc or $HOME/.profile the following instruction: 107 108 . <(%s completion) 109 `, rootCmd.Use, rootCmd.Use), 110 RunE: func(cmd *cobra.Command, _ []string) error { 111 zsh, err := cmd.Flags().GetBool(flagZsh) 112 if err != nil { 113 return err 114 } 115 if zsh { 116 return rootCmd.GenZshCompletion(cmd.OutOrStdout()) 117 } 118 return rootCmd.GenBashCompletion(cmd.OutOrStdout()) 119 }, 120 Hidden: hidden, 121 Args: cobra.NoArgs, 122 } 123 124 cmd.Flags().Bool(flagZsh, false, "Generate Zsh completion script") 125 126 return cmd 127 }