cmd/uplink: hide advanced flags from output

Change-Id: I536af267c38e153aeea682fca4a74dc0ea2c42f0
This commit is contained in:
Bryan White 2020-01-22 19:43:14 +01:00
parent c6f94ce9e4
commit fab58e9c12
9 changed files with 146 additions and 20 deletions

View File

@ -57,7 +57,7 @@ type Config struct {
// AccessConfig holds information about which accesses exist and are selected.
type AccessConfig struct {
Accesses map[string]string `internal:"true"`
Access string `help:"the serialized access, or name of the access to use" default:""`
Access string `help:"the serialized access, or name of the access to use" default:"" basic-help:"true"`
// used for backward compatibility
Scopes map[string]string `internal:"true"` // deprecated

View File

@ -38,6 +38,8 @@ func init() {
progress = cpCmd.Flags().Bool("progress", true, "if true, show progress")
expires = cpCmd.Flags().String("expires", "", "optional expiration date of an object. Please use format (yyyy-mm-ddThh:mm:ssZhh:mm)")
metadata = cpCmd.Flags().String("metadata", "", "optional metadata for the object. Please use a single level JSON object of string to string only")
setBasicFlags(cpCmd.Flags(), "progress", "expires", "metadata")
}
// upload transfers src from local machine to s3 compatible object dst

View File

@ -39,6 +39,9 @@ func init() {
// flags.
// TODO: revisit after the configuration/flag code is refactored.
process.Bind(importCmd, &importCfg, defaults, cfgstruct.ConfDir(confDir))
// NB: access is not supported by `setup` or `import`
cfgstruct.SetBoolAnnotation(importCmd.Flags(), "access", cfgstruct.BasicHelpAnnotationName, false)
}
// importMain is the function executed when importCmd is called

View File

@ -29,6 +29,8 @@ func init() {
}, RootCmd)
lsRecursiveFlag = lsCmd.Flags().Bool("recursive", false, "if true, list recursively")
lsEncryptedFlag = lsCmd.Flags().Bool("encrypted", false, "if true, show paths as base64-encoded encrypted paths")
setBasicFlags(lsCmd.Flags(), "recursive", "encrypted")
}
func list(cmd *cobra.Command, args []string) error {

View File

@ -25,6 +25,7 @@ func init() {
RunE: deleteObject,
}, RootCmd)
rmEncryptedFlag = rmCmd.Flags().Bool("encrypted", false, "if true, treat paths as base64-encoded encrypted paths")
setBasicFlags(rmCmd.Flags(), "encrypted")
}
func deleteObject(cmd *cobra.Command, args []string) error {

View File

@ -4,15 +4,19 @@
package cmd
import (
"bufio"
"bytes"
"context"
"flag"
"fmt"
"os"
"runtime"
"runtime/pprof"
"strings"
"github.com/spf13/cast"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"github.com/zeebo/errs"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
@ -26,6 +30,8 @@ import (
"storj.io/storj/private/version/checker"
)
const advancedFlagName = "advanced"
// UplinkFlags configuration flags
type UplinkFlags struct {
NonInteractive bool `help:"disable interactive mode" default:"false" setup:"true"`
@ -51,6 +57,12 @@ var (
func init() {
defaultConfDir := fpath.ApplicationDir("storj", "uplink")
cfgstruct.SetupFlag(zap.L(), RootCmd, &confDir, "config-dir", defaultConfDir, "main directory for uplink configuration")
// NB: more-help flag is always retrieved using `findBoolFlagEarly()`
RootCmd.PersistentFlags().BoolVar(new(bool), advancedFlagName, false, "if used in with -h, print advanced flags help")
setBasicFlags(RootCmd.PersistentFlags(), "config-dir", advancedFlagName)
setUsageFunc(RootCmd)
}
var cpuProfile = flag.String("profile.cpu", "", "file path of the cpu profile to be created")
@ -244,3 +256,98 @@ func combineCobraFuncs(funcs ...func(*cobra.Command, []string) error) func(*cobr
return err
}
}
/* `setUsageFunc` is a bit unconventional but cobra didn't leave much room for
extensibility here. `cmd.SetUsageTemplate` is fairly useless for our case without
the ability to add to the template's function map (see: https://golang.org/pkg/text/template/#hdr-Functions).
Because we can't alter what `cmd.Usage` generates, we have to edit it afterwards.
In order to hook this function *and* get the usage string, we have to juggle the
`cmd.usageFunc` between our hook and `nil`, so that we can get the usage string
from the default usage func.
*/
func setUsageFunc(cmd *cobra.Command) {
if findBoolFlagEarly(advancedFlagName) {
return
}
reset := func() (set func()) {
original := cmd.UsageFunc()
cmd.SetUsageFunc(nil)
return func() {
cmd.SetUsageFunc(original)
}
}
cmd.SetUsageFunc(func(cmd *cobra.Command) error {
set := reset()
usageStr := cmd.UsageString()
defer set()
usageScanner := bufio.NewScanner(bytes.NewBufferString(usageStr))
var basicFlags []string
cmd.Flags().VisitAll(func(flag *pflag.Flag) {
basic, ok := flag.Annotations[cfgstruct.BasicHelpAnnotationName]
if ok && len(basic) == 1 && basic[0] == "true" {
basicFlags = append(basicFlags, flag.Name)
}
})
for usageScanner.Scan() {
line := usageScanner.Text()
trimmedLine := strings.TrimSpace(line)
var flagName string
if _, err := fmt.Sscanf(trimmedLine, "--%s", &flagName); err != nil {
fmt.Println(line)
continue
}
// TODO: properly filter flags with short names
if !strings.HasPrefix(trimmedLine, "--") {
fmt.Println(line)
}
for _, basicFlag := range basicFlags {
if basicFlag == flagName {
fmt.Println(line)
}
}
}
return nil
})
}
func findBoolFlagEarly(flagName string) bool {
for i, arg := range os.Args {
arg := arg
argHasPrefix := func(format string, args ...interface{}) bool {
return strings.HasPrefix(arg, fmt.Sprintf(format, args...))
}
if !argHasPrefix("--%s", flagName) {
continue
}
// NB: covers `--<flagName> false` usage
if i+1 != len(os.Args) {
next := os.Args[i+1]
if next == "false" {
return false
}
}
if !argHasPrefix("--%s=false", flagName) {
return true
}
}
return false
}
func setBasicFlags(flagset interface{}, flagNames ...string) {
for _, name := range flagNames {
cfgstruct.SetBoolAnnotation(flagset, name, cfgstruct.BasicHelpAnnotationName, true)
}
}

View File

@ -32,6 +32,9 @@ var (
func init() {
RootCmd.AddCommand(setupCmd)
process.Bind(setupCmd, &setupCfg, defaults, cfgstruct.ConfDir(confDir), cfgstruct.SetupMode())
// NB: access is not supported by `setup` or `import`
cfgstruct.SetBoolAnnotation(setupCmd.Flags(), "access", cfgstruct.BasicHelpAnnotationName, false)
}
func cmdSetup(cmd *cobra.Command, args []string) (err error) {

View File

@ -21,16 +21,16 @@ import (
)
var shareCfg struct {
DisallowReads bool `default:"false" help:"if true, disallow reads"`
DisallowWrites bool `default:"false" help:"if true, disallow writes"`
DisallowLists bool `default:"false" help:"if true, disallow lists"`
DisallowDeletes bool `default:"false" help:"if true, disallow deletes"`
Readonly bool `default:"false" help:"implies disallow_writes and disallow_deletes"`
Writeonly bool `default:"false" help:"implies disallow_reads and disallow_lists"`
NotBefore string `help:"disallow access before this time"`
NotAfter string `help:"disallow access after this time"`
DisallowReads bool `default:"false" help:"if true, disallow reads" basic-help:"true"`
DisallowWrites bool `default:"false" help:"if true, disallow writes" basic-help:"true"`
DisallowLists bool `default:"false" help:"if true, disallow lists" basic-help:"true"`
DisallowDeletes bool `default:"false" help:"if true, disallow deletes" basic-help:"true"`
Readonly bool `default:"false" help:"implies disallow_writes and disallow_deletes" basic-help:"true"`
Writeonly bool `default:"false" help:"implies disallow_reads and disallow_lists" basic-help:"true"`
NotBefore string `help:"disallow access before this time" basic-help:"true"`
NotAfter string `help:"disallow access after this time" basic-help:"true"`
AllowedPathPrefix []string `help:"whitelist of path prefixes to require, overrides the [allowed-path-prefix] arguments"`
ExportTo string `default:"" help:"path to export the shared access to"`
ExportTo string `default:"" help:"path to export the shared access to" basic-help:"true"`
// Share requires information about the current access
AccessConfig

View File

@ -27,6 +27,10 @@ const (
// FlagSource is a source annotation for config values that just come from
// flags (i.e. are never persisted to file)
FlagSource = "flag"
// BasicHelpAnnotationName is the name of the annotation used to indicate
// a flag should be included in basic usage/help.
BasicHelpAnnotationName = "basic-help"
)
var (
@ -182,18 +186,18 @@ func bindConfig(flags FlagSet, prefix string, val reflect.Value, vars map[string
markHidden := false
if onlyForSetup {
setBoolAnnotation(flags, flagname, "setup")
SetBoolAnnotation(flags, flagname, "setup", true)
}
if field.Tag.Get("user") == "true" {
setBoolAnnotation(flags, flagname, "user")
SetBoolAnnotation(flags, flagname, "user", true)
}
if field.Tag.Get("hidden") == "true" {
markHidden = true
setBoolAnnotation(flags, flagname, "hidden")
SetBoolAnnotation(flags, flagname, "hidden", true)
}
if field.Tag.Get("deprecated") == "true" {
markHidden = true
setBoolAnnotation(flags, flagname, "deprecated")
SetBoolAnnotation(flags, flagname, "deprecated", true)
}
if source := field.Tag.Get("source"); source != "" {
setSourceAnnotation(flags, flagname, source)
@ -276,20 +280,23 @@ func bindConfig(flags FlagSet, prefix string, val reflect.Value, vars map[string
panic(fmt.Sprintf("invalid field type: %s", field.Type.String()))
}
if onlyForSetup {
setBoolAnnotation(flags, flagname, "setup")
SetBoolAnnotation(flags, flagname, "setup", true)
}
if field.Tag.Get("user") == "true" {
setBoolAnnotation(flags, flagname, "user")
SetBoolAnnotation(flags, flagname, "user", true)
}
if field.Tag.Get(BasicHelpAnnotationName) == "true" {
SetBoolAnnotation(flags, flagname, BasicHelpAnnotationName, true)
}
markHidden := false
if field.Tag.Get("hidden") == "true" {
markHidden = true
setBoolAnnotation(flags, flagname, "hidden")
SetBoolAnnotation(flags, flagname, "hidden", true)
}
if field.Tag.Get("deprecated") == "true" {
markHidden = true
setBoolAnnotation(flags, flagname, "deprecated")
SetBoolAnnotation(flags, flagname, "deprecated", true)
}
if source := field.Tag.Get("source"); source != "" {
setSourceAnnotation(flags, flagname, source)
@ -343,13 +350,14 @@ func setStringAnnotation(flagset interface{}, name, key, value string) {
}
}
func setBoolAnnotation(flagset interface{}, name, key string) {
// SetBoolAnnotation sets an annotation (if it can) on flagset with a value of []string{"true|false"}.
func SetBoolAnnotation(flagset interface{}, name, key string, value bool) {
flags, ok := flagset.(*pflag.FlagSet)
if !ok {
return
}
err := flags.SetAnnotation(name, key, []string{"true"})
err := flags.SetAnnotation(name, key, []string{strconv.FormatBool(value)})
if err != nil {
panic(fmt.Sprintf("unable to set %s annotation for %s: %v", key, name, err))
}