Browse Source

Add "path" command line arguments

Signed-off-by: Vadim Markovtsev <vadim@sourced.tech>
Vadim Markovtsev 6 years ago
parent
commit
8e3e2416b7

+ 46 - 11
cmd/hercules/root.go

@@ -147,6 +147,10 @@ func loadPlugins() {
 	const pluginDesc = "Load the specified plugin by the full or relative path. " +
 		"Can be specified multiple times."
 	fs.Var(&pluginFlags, pluginFlagName, pluginDesc)
+	err := cobra.MarkFlagFilename(fs, "plugin")
+	if err != nil {
+		panic(err)
+	}
 	pflag.Var(&pluginFlags, pluginFlagName, pluginDesc)
 	fs.Parse(os.Args[1:])
 	for path := range pluginFlags {
@@ -168,17 +172,39 @@ targets can be added using the --plugin system.`,
 	Args: cobra.RangeArgs(1, 2),
 	Run: func(cmd *cobra.Command, args []string) {
 		flags := cmd.Flags()
-		firstParent, _ := flags.GetBool("first-parent")
-		commitsFile, _ := flags.GetString("commits")
-		protobuf, _ := flags.GetBool("pb")
-		profile, _ := flags.GetBool("profile")
-		disableStatus, _ := flags.GetBool("quiet")
-		sshIdentity, _ := flags.GetString("ssh-identity")
+		getBool := func(name string) bool {
+			value, err := flags.GetBool(name)
+			if err != nil {
+				panic(err)
+			}
+			return value
+		}
+		getString := func(name string) string {
+			value, err := flags.GetString(name)
+			if err != nil {
+				panic(err)
+			}
+			return value
+		}
+		firstParent := getBool("first-parent")
+		commitsFile := getString("commits")
+		protobuf := getBool("pb")
+		profile := getBool("profile")
+		disableStatus := getBool("quiet")
+		sshIdentity := getString("ssh-identity")
 
 		if profile {
-			go http.ListenAndServe("localhost:6060", nil)
+			go func() {
+				err := http.ListenAndServe("localhost:6060", nil)
+				if err != nil {
+					panic(err)
+				}
+			}()
 			prof, _ := os.Create("hercules.pprof")
-			pprof.StartCPUProfile(prof)
+			err := pprof.StartCPUProfile(prof)
+			if err != nil {
+				panic(err)
+			}
 			defer pprof.StopCPUProfile()
 		}
 		uri := args[0]
@@ -350,6 +376,7 @@ func formatUsage(c *cobra.Command) error {
 	leaves := hercules.Registry.GetLeaves()
 	plumbing := hercules.Registry.GetPlumbingItems()
 	features := hercules.Registry.GetFeaturedItems()
+	hercules.EnablePathFlagTypeMasquerade()
 	filter := map[string]bool{}
 	for _, l := range leaves {
 		filter[l.Flag()] = true
@@ -450,13 +477,16 @@ var cmdlineDeployed map[string]*bool
 
 func init() {
 	loadPlugins()
-	rootCmd.MarkFlagFilename("plugin")
 	rootFlags := rootCmd.Flags()
 	rootFlags.String("commits", "", "Path to the text file with the "+
-		"commit history to follow instead of the default `git log`. "+
+		"commit history to follow instead of the default 'git log'. "+
 		"The format is the list of hashes, each hash on a "+
 		"separate line. The first hash is the root.")
-	rootCmd.MarkFlagFilename("commits")
+	err := rootCmd.MarkFlagFilename("commits")
+	if err != nil {
+		panic(err)
+	}
+	hercules.PathifyFlagValue(rootFlags.Lookup("commits"))
 	rootFlags.Bool("first-parent", false, "Follow only the first parent in the commit history - "+
 		"\"git log --first-parent\".")
 	rootFlags.Bool("pb", false, "The output format will be Protocol Buffers instead of YAML.")
@@ -464,6 +494,11 @@ func init() {
 		"Do not print status updates to stderr.")
 	rootFlags.Bool("profile", false, "Collect the profile to hercules.pprof.")
 	rootFlags.String("ssh-identity", "", "Path to SSH identity file (e.g., ~/.ssh/id_rsa) to clone from an SSH remote.")
+	err = rootCmd.MarkFlagFilename("ssh-identity")
+	if err != nil {
+		panic(err)
+	}
+	hercules.PathifyFlagValue(rootFlags.Lookup("ssh-identity"))
 	cmdlineFacts, cmdlineDeployed = hercules.Registry.AddFlags(rootFlags)
 	rootCmd.SetUsageFunc(formatUsage)
 	rootCmd.AddCommand(versionCmd)

+ 12 - 0
core.go

@@ -1,6 +1,7 @@
 package hercules
 
 import (
+	"github.com/spf13/pflag"
 	"gopkg.in/src-d/go-git.v4"
 	"gopkg.in/src-d/go-git.v4/plumbing/object"
 	"gopkg.in/src-d/hercules.v6/internal/core"
@@ -158,6 +159,17 @@ func SafeYamlString(str string) string {
 	return yaml.SafeString(str)
 }
 
+// PathifyFlagValue changes the type of a string command line argument to "path".
+func PathifyFlagValue(flag *pflag.Flag) {
+	core.PathifyFlagValue(flag)
+}
+
+// EnablePathFlagTypeMasquerade changes the type of all "path" command line arguments from "string"
+// to "path". This operation cannot be canceled and is intended to be used for better --help output.
+func EnablePathFlagTypeMasquerade() {
+	core.EnablePathFlagTypeMasquerade()
+}
+
 func init() {
 	// hack to link with .leaves
 	_ = leaves.BurndownAnalysis{}

+ 4 - 0
internal/core/pipeline.go

@@ -36,6 +36,8 @@ const (
 	FloatConfigurationOption
 	// StringsConfigurationOption reflects the array of strings value type.
 	StringsConfigurationOption
+	// PathConfigurationOption reflects the file system path value type.
+	PathConfigurationOption
 )
 
 // String() returns an empty string for the boolean type, "int" for integers and "string" for
@@ -52,6 +54,8 @@ func (opt ConfigurationOptionType) String() string {
 		return "float"
 	case StringsConfigurationOption:
 		return "string"
+	case PathConfigurationOption:
+		return "path"
 	}
 	log.Panicf("Invalid ConfigurationOptionType value %d", opt)
 	return ""

+ 2 - 0
internal/core/pipeline_test.go

@@ -584,6 +584,8 @@ func TestConfigurationOptionTypeString(t *testing.T) {
 	opt = ConfigurationOptionType(4)
 	assert.Equal(t, opt.String(), "string")
 	opt = ConfigurationOptionType(5)
+	assert.Equal(t, opt.String(), "path")
+	opt = ConfigurationOptionType(6)
 	assert.Panics(t, func() { _ = opt.String() })
 }
 

+ 46 - 2
internal/core/registry.go

@@ -7,6 +7,7 @@ import (
 	"strings"
 	"unsafe"
 
+	"github.com/spf13/cobra"
 	"github.com/spf13/pflag"
 )
 
@@ -39,7 +40,7 @@ func (registry *PipelineItemRegistry) Register(example PipelineItem) {
 // the specified string. It materializes all the found types and returns them.
 func (registry *PipelineItemRegistry) Summon(providesOrName string) []PipelineItem {
 	if registry.provided == nil {
-		return []PipelineItem{}
+		return nil
 	}
 	ts := registry.provided[providesOrName]
 	var items []PipelineItem
@@ -120,6 +121,41 @@ func (registry *PipelineItemRegistry) GetFeaturedItems() map[string][]FeaturedPi
 	return features
 }
 
+var pathFlagTypeMasquerade bool
+
+// EnablePathFlagTypeMasquerade changes the type of all "path" command line arguments from "string"
+// to "path". This operation cannot be canceled and is intended to be used for better --help output.
+func EnablePathFlagTypeMasquerade() {
+	pathFlagTypeMasquerade = true
+}
+
+type pathValue struct {
+	origin pflag.Value
+}
+
+func wrapPathValue(val pflag.Value) pflag.Value {
+	return &pathValue{val}
+}
+
+func (s *pathValue) Set(val string) error {
+	return s.origin.Set(val)
+}
+func (s *pathValue) Type() string {
+	if pathFlagTypeMasquerade {
+		return "path"
+	}
+	return "string"
+}
+
+func (s *pathValue) String() string {
+	return s.origin.String()
+}
+
+// PathifyFlagValue changes the type of a string command line argument to "path".
+func PathifyFlagValue(flag *pflag.Flag) {
+	flag.Value = wrapPathValue(flag.Value)
+}
+
 type arrayFeatureFlags struct {
 	// Flags contains the features activated through the command line.
 	Flags []string
@@ -172,10 +208,17 @@ func (registry *PipelineItemRegistry) AddFlags(flagSet *pflag.FlagSet) (
 				iface = interface{}(0)
 				ptr := (**int)(getPtr())
 				*ptr = flagSet.Int(opt.Flag, opt.Default.(int), formatHelp(opt.Description))
-			case StringConfigurationOption:
+			case StringConfigurationOption, PathConfigurationOption:
 				iface = interface{}("")
 				ptr := (**string)(getPtr())
 				*ptr = flagSet.String(opt.Flag, opt.Default.(string), formatHelp(opt.Description))
+				if opt.Type == PathConfigurationOption {
+					err := cobra.MarkFlagFilename(flagSet, opt.Flag)
+					if err != nil {
+						panic(err)
+					}
+					PathifyFlagValue(flagSet.Lookup(opt.Flag))
+				}
 			case FloatConfigurationOption:
 				iface = interface{}(float32(0))
 				ptr := (**float32)(getPtr())
@@ -203,6 +246,7 @@ func (registry *PipelineItemRegistry) AddFlags(flagSet *pflag.FlagSet) (
 		ptr1 := (**string)(unsafe.Pointer(uintptr(unsafe.Pointer(&iface)) + unsafe.Sizeof(&iface)))
 		*ptr1 = flagSet.String("dump-dag", "", "Write the pipeline DAG to a Graphviz file.")
 		flags[ConfigPipelineDAGPath] = iface
+		PathifyFlagValue(flagSet.Lookup("dump-dag"))
 		iface = interface{}(true)
 		ptr2 := (**bool)(unsafe.Pointer(uintptr(unsafe.Pointer(&iface)) + unsafe.Sizeof(&iface)))
 		*ptr2 = flagSet.Bool("dry-run", false, "Do not run any analyses - only resolve the DAG. "+

+ 17 - 0
internal/core/registry_test.go

@@ -1,10 +1,12 @@
 package core
 
 import (
+	"os"
 	"reflect"
 	"testing"
 
 	"github.com/spf13/cobra"
+	"github.com/spf13/pflag"
 	"github.com/stretchr/testify/assert"
 	"gopkg.in/src-d/go-git.v4"
 	"gopkg.in/src-d/hercules.v6/internal/test"
@@ -114,6 +116,7 @@ func (item *dummyPipelineItem2) Merge(branches []PipelineItem) {
 
 func TestRegistrySummon(t *testing.T) {
 	reg := getRegistry()
+	assert.Len(t, reg.Summon("whatever"), 0)
 	reg.Register(&testPipelineItem{})
 	summoned := reg.Summon((&testPipelineItem{}).Provides()[0])
 	assert.Len(t, summoned, 1)
@@ -215,3 +218,17 @@ func TestRegistryFeaturedItems(t *testing.T) {
 	assert.Equal(t, featured["power"][0].Name(), (&testPipelineItem{}).Name())
 	assert.Equal(t, featured["power"][1].Name(), (&dummyPipelineItem{}).Name())
 }
+
+func TestRegistryPathMasquerade(t *testing.T) {
+	fs := pflag.NewFlagSet(os.Args[0], pflag.ContinueOnError)
+	var value string
+	fs.StringVar(&value, "test", "", "usage")
+	flag := fs.Lookup("test")
+	PathifyFlagValue(flag)
+	assert.Equal(t, flag.Value.Type(), "string")
+	assert.Nil(t, flag.Value.Set("xxx"))
+	assert.Equal(t, flag.Value.String(), "xxx")
+	EnablePathFlagTypeMasquerade()
+	assert.Equal(t, flag.Value.Type(), "path")
+	assert.Equal(t, flag.Value.String(), "xxx")
+}

+ 1 - 1
internal/plumbing/identity/identity.go

@@ -76,7 +76,7 @@ func (detector *Detector) ListConfigurationOptions() []core.ConfigurationOption
 		Name:        ConfigIdentityDetectorPeopleDictPath,
 		Description: "Path to the file with developer -> name|email associations.",
 		Flag:        "people-dict",
-		Type:        core.StringConfigurationOption,
+		Type:        core.PathConfigurationOption,
 		Default:     ""},
 	}
 	return options[:]

+ 1 - 1
internal/plumbing/uast/uast.go

@@ -460,7 +460,7 @@ func (saver *ChangesSaver) ListConfigurationOptions() []core.ConfigurationOption
 		Name:        ConfigUASTChangesSaverOutputPath,
 		Description: "The target directory where to store the changed UAST files.",
 		Flag:        "changed-uast-dir",
-		Type:        core.StringConfigurationOption,
+		Type:        core.PathConfigurationOption,
 		Default:     "."},
 	}
 	return options[:]

+ 1 - 1
leaves/burndown.go

@@ -229,7 +229,7 @@ func (analyser *BurndownAnalysis) ListConfigurationOptions() []core.Configuratio
 		Description: "Temporary directory where to save the hibernated RBTree allocators; " +
 			"requires --burndown-hibernation-disk.",
 		Flag:    "burndown-hibernation-dir",
-		Type:    core.StringConfigurationOption,
+		Type:    core.PathConfigurationOption,
 		Default: ""}, {
 		Name:        ConfigBurndownDebug,
 		Description: "Validate the trees on each step.",

+ 1 - 1
leaves/devs.go

@@ -88,7 +88,7 @@ func (devs *DevsAnalysis) ListConfigurationOptions() []core.ConfigurationOption
 	options := [...]core.ConfigurationOption{{
 		Name:        ConfigDevsConsiderEmptyCommits,
 		Description: "Take into account empty commits such as trivial merges.",
-		Flag:        "--empty-commits",
+		Flag:        "empty-commits",
 		Type:        core.BoolConfigurationOption,
 		Default:     false}}
 	return options[:]

+ 1 - 1
leaves/devs_test.go

@@ -37,7 +37,7 @@ func TestDevsMeta(t *testing.T) {
 	assert.Equal(t, d.Flag(), "devs")
 	assert.Len(t, d.ListConfigurationOptions(), 1)
 	assert.Equal(t, d.ListConfigurationOptions()[0].Name, ConfigDevsConsiderEmptyCommits)
-	assert.Equal(t, d.ListConfigurationOptions()[0].Flag, "--empty-commits")
+	assert.Equal(t, d.ListConfigurationOptions()[0].Flag, "empty-commits")
 	assert.Equal(t, d.ListConfigurationOptions()[0].Type, core.BoolConfigurationOption)
 	assert.Equal(t, d.ListConfigurationOptions()[0].Default, false)
 	assert.True(t, len(d.Description()) > 0)