Prechádzať zdrojové kódy

Add cmdline item deployment

Vadim Markovtsev 7 rokov pred
rodič
commit
5d0acb7d88
4 zmenil súbory, kde vykonal 70 pridanie a 18 odobranie
  1. 4 0
      burndown.go
  2. 43 14
      cmd/hercules/main.go
  3. 4 0
      couples.go
  4. 19 4
      pipeline.go

+ 4 - 0
burndown.go

@@ -141,6 +141,10 @@ func (analyser *BurndownAnalysis) Configure(facts map[string]interface{}) {
 	}
 }
 
+func (analyser *BurndownAnalysis) Flag() string {
+	return "burndown"
+}
+
 func (analyser *BurndownAnalysis) Initialize(repository *git.Repository) {
 	if analyser.Granularity <= 0 {
 		fmt.Fprintln(os.Stderr, "Warning: adjusted the granularity to 30 days")

+ 43 - 14
cmd/hercules/main.go

@@ -32,9 +32,11 @@ import (
 	"flag"
 	"fmt"
 	"io"
+	"io/ioutil"
 	"net/http"
 	_ "net/http/pprof"
 	"os"
+	"plugin"
 	"runtime/pprof"
 	"sort"
 	"strings"
@@ -115,21 +117,52 @@ func loadRepository(uri string) *git.Repository {
 	return repository
 }
 
+
+type arrayPluginFlags map[string]bool
+
+func (apf *arrayPluginFlags) String() string {
+	list := []string{}
+	for key := range *apf {
+		list = append(list, key)
+	}
+	return strings.Join(list, ", ")
+}
+
+func (apf *arrayPluginFlags) Set(value string) error {
+	(*apf)[value] = true
+	return nil
+}
+
+func loadPlugins() {
+	pluginFlags := arrayPluginFlags{}
+	fs := flag.NewFlagSet(os.Args[0], flag.ContinueOnError)
+	fs.SetOutput(ioutil.Discard)
+	pluginFlagName := "plugin"
+	pluginDesc := "Load the specified plugin by the full or relative path. " +
+			"Can be specified multiple times."
+	fs.Var(&pluginFlags, pluginFlagName, pluginDesc)
+	flag.Var(&pluginFlags, pluginFlagName, pluginDesc)
+	fs.Parse(os.Args[1:])
+	for path := range pluginFlags {
+		_, err := plugin.Open(path)
+		if err != nil {
+			fmt.Fprintf(os.Stderr, "Failed to load plugin from %s %s", path, err)
+		}
+	}
+}
+
 func main() {
+	loadPlugins()
 	var protobuf bool
 	var profile bool
 	var commitsFile string
-	var withBurndown bool
-	var withCouples bool
 	flag.BoolVar(&profile, "profile", false, "Collect the profile to hercules.pprof.")
 	flag.StringVar(&commitsFile, "commits", "", "Path to the text file with the "+
 		"commit history to follow instead of the default rev-list "+
 		"--first-parent. The format is the list of hashes, each hash on a "+
 		"separate line. The first hash is the root.")
 	flag.BoolVar(&protobuf, "pb", false, "The output format will be Protocol Buffers instead of YAML.")
-	flag.BoolVar(&withBurndown, "burndown", false, "Analyse lines burndown.")
-	flag.BoolVar(&withCouples, "couples", false, "Analyse file and developer couples.")
-	facts := hercules.Registry.AddFlags()
+	facts, deployChoices := hercules.Registry.AddFlags()
 	flag.Parse()
 
 	if profile {
@@ -198,13 +231,11 @@ func main() {
 		pipeline.AddItem(uastSaver)
 	}
 	*/
-	var burndowner hercules.PipelineItem
-	if withBurndown {
-		burndowner = pipeline.DeployItem(&hercules.BurndownAnalysis{})
-	}
-	var coupler hercules.PipelineItem
-	if withCouples {
-		coupler = pipeline.DeployItem(&hercules.Couples{})
+	deployed := []hercules.PipelineItem{}
+	for name, valPtr := range deployChoices {
+		if *valPtr {
+			deployed = append(deployed, pipeline.DeployItem(hercules.Registry.Summon(name)[0]))
+		}
 	}
 	pipeline.Initialize(facts)
 	result, err := pipeline.Run(commits)
@@ -214,8 +245,6 @@ func main() {
 	progress.Stop()
 	fmt.Fprint(os.Stderr, "writing...    \r")
 	_ = result
-	_ = burndowner
-	_ = coupler
 	/*
 	burndownResults := result[burndowner].(hercules.BurndownResult)
 	if len(burndownResults.GlobalHistory) == 0 {

+ 4 - 0
couples.go

@@ -50,6 +50,10 @@ func (couples *Couples) Configure(facts map[string]interface{}) {
 	}
 }
 
+func (couples *Couples) Flag() string {
+	return "couples"
+}
+
 func (couples *Couples) Initialize(repository *git.Repository) {
 	couples.people = make([]map[string]int, couples.PeopleNumber+1)
 	for i := range couples.people {

+ 19 - 4
pipeline.go

@@ -80,16 +80,22 @@ type FinalizedPipelineItem interface {
 	PipelineItem
 	// Finalize returns the result of the analysis.
 	Finalize() interface{}
+	// Flag returns the cmdline name of the item.
+	Flag() string
 }
 
 type PipelineItemRegistry struct {
 	provided   map[string][]reflect.Type
 	registered map[string]reflect.Type
+	flags map[string]reflect.Type
 }
 
 func (registry *PipelineItemRegistry) Register(example PipelineItem) {
 	t := reflect.TypeOf(example)
 	registry.registered[example.Name()] = t
+	if fpi, ok := interface{}(example).(FinalizedPipelineItem); ok {
+		registry.flags[fpi.Flag()] = t
+	}
 	for _, dep := range example.Provides() {
 		ts := registry.provided[dep]
 		if ts == nil {
@@ -100,15 +106,18 @@ func (registry *PipelineItemRegistry) Register(example PipelineItem) {
 	}
 }
 
-func (registry *PipelineItemRegistry) Summon(provides string) []PipelineItem {
+func (registry *PipelineItemRegistry) Summon(providesOrName string) []PipelineItem {
 	if registry.provided == nil {
 		return []PipelineItem{}
 	}
-	ts := registry.provided[provides]
+	ts := registry.provided[providesOrName]
 	items := []PipelineItem{}
 	for _, t := range ts {
 		items = append(items, reflect.New(t.Elem()).Interface().(PipelineItem))
 	}
+	if t, exists := registry.registered[providesOrName]; exists {
+		items = append(items, reflect.New(t.Elem()).Interface().(PipelineItem))
+	}
 	return items
 }
 
@@ -133,8 +142,9 @@ func (acf *arrayFeatureFlags) Set(value string) error {
 
 var featureFlags = arrayFeatureFlags{Flags: []string{}, Choices: map[string]bool{}}
 
-func (registry *PipelineItemRegistry) AddFlags() map[string]interface{} {
+func (registry *PipelineItemRegistry) AddFlags() (map[string]interface{}, map[string]*bool) {
 	flags := map[string]interface{}{}
+	deployed := map[string]*bool{}
 	for name, it := range registry.registered {
 		formatHelp := func(desc string) string {
 			return fmt.Sprintf("%s [%s]", desc, name)
@@ -163,6 +173,10 @@ func (registry *PipelineItemRegistry) AddFlags() map[string]interface{} {
 				featureFlags.Choices[f] = true
 			}
 		}
+		if fpi, ok := itemIface.(FinalizedPipelineItem); ok {
+			deployed[fpi.Name()] = flag.Bool(
+				fpi.Flag(), false, fmt.Sprintf("Runs %s analysis.", fpi.Name()))
+		}
 	}
 	features := []string{}
 	for f := range featureFlags.Choices {
@@ -171,13 +185,14 @@ func (registry *PipelineItemRegistry) AddFlags() map[string]interface{} {
 	flag.Var(&featureFlags, "feature",
 		fmt.Sprintf("Enables specific analysis features, can be specified "+
 			"multiple times. Available features: [%s].", strings.Join(features, ", ")))
-	return flags
+	return flags, deployed
 }
 
 // Registry contains all known pipeline item types.
 var Registry = &PipelineItemRegistry{
 	provided:   map[string][]reflect.Type{},
 	registered: map[string]reflect.Type{},
+	flags: map[string]reflect.Type{},
 }
 
 type wrappedPipelineItem struct {