github.com/wtfutil/wtf@v0.43.0/flags/flags.go (about)

     1  package flags
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"path/filepath"
     7  	"runtime/debug"
     8  	"strings"
     9  
    10  	"github.com/chzyer/readline"
    11  	goFlags "github.com/jessevdk/go-flags"
    12  	"github.com/olebedev/config"
    13  	"github.com/wtfutil/wtf/cfg"
    14  	"github.com/wtfutil/wtf/help"
    15  )
    16  
    17  // Flags is the container for command line flag data
    18  type Flags struct {
    19  	Config  string `short:"c" long:"config" optional:"yes" description:"Path to config file"`
    20  	Module  string `short:"m" long:"module" optional:"yes" description:"Display info about a specific module, i.e.: 'wtfutil -m=todo'"`
    21  	Profile bool   `short:"p" long:"profile" optional:"yes" description:"Profile application memory usage"`
    22  	Version bool   `short:"v" long:"version" description:"Show version info"`
    23  	// Work-around go-flags misfeatures. If any sub-command is defined
    24  	// then `wtf` (no sub-commands, the common usage), is warned about.
    25  	Opt struct {
    26  		Cmd  string   `positional-arg-name:"command"`
    27  		Args []string `positional-arg-name:"args"`
    28  	} `positional-args:"yes"`
    29  
    30  	hasCustom bool
    31  }
    32  
    33  var EXTRA = `
    34  Commands:
    35    save-secret <service>
    36      service      Service URL or module name of secret.
    37    Save a secret into the secret store. The secret will be prompted for.
    38    Requires wtf.secretStore to be configured.  See individual modules for
    39    information on what service and secret means for their configuration,
    40    not all modules use secrets.
    41  `
    42  
    43  // NewFlags creates an instance of Flags
    44  func NewFlags() *Flags {
    45  	flags := Flags{}
    46  	return &flags
    47  }
    48  
    49  /* -------------------- Exported Functions -------------------- */
    50  
    51  // ConfigFilePath returns the path to the currently-loaded config file
    52  func (flags *Flags) ConfigFilePath() string {
    53  	return flags.Config
    54  }
    55  
    56  // RenderIf displays special-case information based on the flags passed
    57  // in, if any flags were passed in
    58  func (flags *Flags) RenderIf(config *config.Config) {
    59  	if flags.HasModule() {
    60  		help.Display(flags.Module, config)
    61  		os.Exit(0)
    62  	}
    63  
    64  	if flags.HasVersion() {
    65  		info, _ := debug.ReadBuildInfo()
    66  		version := "dev"
    67  		date := "now"
    68  		for _, setting := range info.Settings {
    69  			if setting.Key == "vcs.revision" {
    70  				version = setting.Value
    71  			} else if setting.Key == "vcs.time" {
    72  				date = setting.Value
    73  			}
    74  		}
    75  		fmt.Printf("%s (%s)\n", version, date)
    76  		os.Exit(0)
    77  	}
    78  
    79  	if flags.Opt.Cmd == "" {
    80  		return
    81  	}
    82  
    83  	switch cmd := flags.Opt.Cmd; cmd {
    84  	case "save-secret":
    85  		var service, secret string
    86  		args := flags.Opt.Args
    87  
    88  		if len(args) < 1 || args[0] == "" {
    89  			fmt.Fprintf(os.Stderr, "save-secret: service required, see `%s --help`\n", os.Args[0])
    90  			os.Exit(1)
    91  		}
    92  
    93  		service = args[0]
    94  
    95  		if len(args) > 1 {
    96  			fmt.Fprintf(os.Stderr, "save-secret: too many arguments, see `%s --help`\n", os.Args[0])
    97  			os.Exit(1)
    98  		}
    99  
   100  		b, err := readline.Password("Secret: ")
   101  		if err != nil {
   102  			fmt.Fprintf(os.Stderr, "Error: %v\n", err)
   103  			os.Exit(1)
   104  		}
   105  		secret = string(b)
   106  		secret = strings.TrimSpace(secret)
   107  
   108  		if secret == "" {
   109  			fmt.Fprintf(os.Stderr, "save-secret: secret required, see `%s --help`\n", os.Args[0])
   110  			os.Exit(1)
   111  		}
   112  
   113  		err = cfg.StoreSecret(config, &cfg.Secret{
   114  			Service:  service,
   115  			Secret:   secret,
   116  			Username: "default",
   117  		})
   118  
   119  		if err != nil {
   120  			fmt.Fprintf(os.Stderr, "Saving secret for service %q: %s\n", service, err.Error())
   121  			os.Exit(1)
   122  		}
   123  
   124  		fmt.Printf("Saved secret for service %q\n", service)
   125  		os.Exit(0)
   126  	default:
   127  		fmt.Fprintf(os.Stderr, "Command `%s` is not supported, try `%s --help`\n", cmd, os.Args[0])
   128  		os.Exit(1)
   129  	}
   130  }
   131  
   132  // HasCustomConfig returns TRUE if a config path was passed in, FALSE if one was not
   133  func (flags *Flags) HasCustomConfig() bool {
   134  	return flags.hasCustom
   135  }
   136  
   137  // HasModule returns TRUE if a module name was passed in, FALSE if one was not
   138  func (flags *Flags) HasModule() bool {
   139  	return len(flags.Module) > 0
   140  }
   141  
   142  // HasVersion returns TRUE if the version flag was passed in, FALSE if it was not
   143  func (flags *Flags) HasVersion() bool {
   144  	return flags.Version
   145  }
   146  
   147  // Parse parses the incoming flags
   148  func (flags *Flags) Parse() {
   149  	parser := goFlags.NewParser(flags, goFlags.Default)
   150  	if _, err := parser.Parse(); err != nil {
   151  		if flagsErr, ok := err.(*goFlags.Error); ok && flagsErr.Type == goFlags.ErrHelp {
   152  			fmt.Println(EXTRA)
   153  			os.Exit(0)
   154  		}
   155  	}
   156  
   157  	// If we have a custom config, then we're done parsing parameters, we don't need to
   158  	// generate the default value
   159  	flags.hasCustom = (len(flags.Config) > 0)
   160  	if flags.hasCustom {
   161  		return
   162  	}
   163  
   164  	// If no config file is explicitly passed in as a param then set the flag to the default config file
   165  	configDir, err := cfg.WtfConfigDir()
   166  	if err != nil {
   167  		fmt.Printf("Error: %v\n", err)
   168  		os.Exit(1)
   169  	}
   170  	flags.Config = filepath.Join(configDir, "config.yml")
   171  }