Mercurial > grim > convey
changeset 304:9de876d04a05
Add support for meta-plans. closes #82
author | Gary Kramlich <grim@reaperworld.com> |
---|---|
date | Mon, 24 Apr 2017 00:15:09 -0500 |
parents | d3b0c59359d7 |
children | cfa2230015f6 |
files | ChangeLog README.md config/config.go config/convey.go convey.yml main.go plans/metaplans.go runners/graphviz.go runners/listmetaplans.go |
diffstat | 9 files changed, 247 insertions(+), 50 deletions(-) [+] |
line wrap: on
line diff
--- a/ChangeLog Thu Apr 20 18:41:01 2017 -0500 +++ b/ChangeLog Mon Apr 24 00:15:09 2017 -0500 @@ -1,6 +1,7 @@ 0.5.0: * Added support for disabling stages (fixes #33) * Added support for aliasing the default plan (fixes #56) + * Added support for metaplans (fixes #82) * Updated the graphviz colors by removing the yellow colors and added a bunch more colors. (Fixes #77)
--- a/README.md Thu Apr 20 18:41:01 2017 -0500 +++ b/README.md Mon Apr 24 00:15:09 2017 -0500 @@ -39,6 +39,7 @@ -S, --force-sequential Don't run anything concurrently -g, --graphviz Output a graphviz diagram of the config file -P, --list-plans List the plans that are available + -M, --list-meta-plans List the meta plans that are available -L, --list-tasks List the supported tasks -m, --memory=MEMORY The ammount of memor to give the run task --ssh-agent Enabled ssh-agent support for tasks @@ -84,7 +85,7 @@ # Configuration -Configuration is done via a file named `convey.yaml`. This file defines the tasks as well as the plans. There are four top level-items: default-plan, environment, plans, and tasks. +Configuration is done via a file named `convey.yaml`. This file defines the tasks as well as the plans. There are five top level-items: default-plan, environment, meta-plans, plans, and tasks. `default-plan` is the name of the plan that should be ran by default. @@ -376,7 +377,7 @@ ### Plan -Plans themselves are pretty straight forward. They group a `stages` with an optional `environment`. +Plans themselves are pretty straight forward. They group `stages` with an optional `environment`. ### Attributes @@ -404,6 +405,35 @@ ---- +## Meta Plans + +Meta plans are used to group plans together and run them in serial. This is also possible via the command line by specifying multiple plans to run. + +There is currently no support for nesting meta plans. + +### Example + + meta-plans: + world: + plans: + - plan1 + - plan2 + - plan3 + +---- + +### Meta Plan + +Meta plans contain a single attribute which is the list of `plans` to run. + +### Attributes + +| Name | Required | Description | +| ----------- | -------- | ----------- | +| plans | Yes | A list of `plans` to run in the order they should be run in. | + +---- + # Config Loaders Convey supports multiple different types of configs that it can load. Currently supported loaders are convey and bitbucket.\
--- a/config/config.go Thu Apr 20 18:41:01 2017 -0500 +++ b/config/config.go Mon Apr 24 00:15:09 2017 -0500 @@ -25,5 +25,6 @@ type Config struct { Tasks map[string]tasks.Task Plans map[string]plans.Plan + MetaPlans map[string]plans.MetaPlan Environment []string }
--- a/config/convey.go Thu Apr 20 18:41:01 2017 -0500 +++ b/config/convey.go Mon Apr 24 00:15:09 2017 -0500 @@ -28,10 +28,11 @@ ) type conveyConfig struct { - DefaultPlan string `yaml:"default-plan"` - Tasks map[string]yaml.MapSlice `yaml:"tasks"` - Plans map[string]plans.Plan `yaml:"plans"` - Environment []string `yaml:"environment"` + DefaultPlan string `yaml:"default-plan"` + Tasks map[string]yaml.MapSlice `yaml:"tasks"` + Plans map[string]plans.Plan `yaml:"plans"` + MetaPlans map[string]plans.MetaPlan `yaml:"meta-plans"` + Environment []string `yaml:"environment"` } type conveyOverride struct { @@ -81,6 +82,7 @@ realConfig := &Config{ Environment: config.Environment, Plans: config.Plans, + MetaPlans: config.MetaPlans, Tasks: realTasks, }
--- a/convey.yml Thu Apr 20 18:41:01 2017 -0500 +++ b/convey.yml Mon Apr 24 00:15:09 2017 -0500 @@ -200,3 +200,12 @@ stages: - name: clean tasks: [clean] +meta-plans: + everything: + plans: + - images + - default + release: + plans: + - default + - deploy
--- a/main.go Thu Apr 20 18:41:01 2017 -0500 +++ b/main.go Mon Apr 24 00:15:09 2017 -0500 @@ -48,6 +48,7 @@ graphviz = app.Flag("graphviz", "Output a graphviz diagram of the config file").Short('g').Default("False").Bool() keep = app.Flag("keep", "Keep the workspace volume").Short('k').Hidden().Default("False").Bool() listTasks = app.Flag("list-tasks", "List the supported tasks").Short('L').Default("false").Bool() + listMetaPlans = app.Flag("list-meta-plans", "List the meta plans that are available").Short('M').Default("false").Bool() listPlans = app.Flag("list-plans", "List the plans that are available").Short('P').Default("false").Bool() memory = app.Flag("memory", "The ammount of memor to give the run task").Short('m').String() showConfig = app.Flag("show-config", "Show a dump of the config file").Short('C').Hidden().Default("false").Bool() @@ -117,6 +118,8 @@ var runner runners.Runner if *graphviz { runner = &runners.Graphviz{} + } else if *listMetaPlans { + runner = &runners.ListMetaPlans{} } else if *listPlans { runner = &runners.ListPlans{} } else if *listTasks { @@ -130,7 +133,11 @@ // resolve the plan name with the load and options realPlans := []string{} for _, planName := range *planNames { - realPlans = append(realPlans, loader.ResolvePlanName(planName, cfg, opts)) + if metaPlan, found := cfg.MetaPlans[planName]; found { + realPlans = append(realPlans, metaPlan.Plans...) + } else { + realPlans = append(realPlans, loader.ResolvePlanName(planName, cfg, opts)) + } } if len(realPlans) == 0 {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plans/metaplans.go Mon Apr 24 00:15:09 2017 -0500 @@ -0,0 +1,43 @@ +/* + * Convey + * Copyright 2016-2017 Gary Kramlich <grim@reaperworld.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package plans + +import ( + "fmt" +) + +type MetaPlan struct { + Plans []string `yaml:"plans"` +} + +func (m *MetaPlan) UnmarshalYAML(unmarshal func(interface{}) error) error { + type rawMetaPlan MetaPlan + raw := rawMetaPlan{} + + if err := unmarshal(&raw); err != nil { + return err + } + + if len(raw.Plans) == 0 { + return fmt.Errorf("no plans specified") + } + + *m = MetaPlan(raw) + + return nil +}
--- a/runners/graphviz.go Thu Apr 20 18:41:01 2017 -0500 +++ b/runners/graphviz.go Mon Apr 24 00:15:09 2017 -0500 @@ -27,24 +27,52 @@ "bitbucket.org/rw_grim/convey/config" "bitbucket.org/rw_grim/convey/logging" "bitbucket.org/rw_grim/convey/options" + "bitbucket.org/rw_grim/convey/plans" ) var graphvizTemplate = ` digraph { graph[rankdir="LR"] + + node[style="filled,rounded" shape="rect"] + + # add the meta plans + node[shape="rect" fillcolor="plum"] +{{range .MetaPlans}} {{.Normalized}}_start[label="{{.Name}} start"] + {{.Normalized}}_finish[label="{{.Name}} finish"] +{{end}} + + # add a start and finish node for each plan + node[shape="component" fillcolor="palegreen"] +{{range .Plans}} {{.Normalized}}_start[label="{{.Name}} start"] + {{.Normalized}}_finish[label="{{.Name}} finish"] +{{end}} + + # add the tasks node[style="filled,rounded" shape="rect" fillcolor="powderblue"] - {{range .Tasks}} {{.Normalized}}[label="{{.Name}}"] {{end}} -{{range .Plans}} edge[color="{{.Color}}"]{{$planNormalized := .Normalized }} - {{.Normalized}}_start[label="{{.Name}}" fillcolor="palegreen" shape="rect"] - {{range .Stages}}{{$planNormalized}}_{{.Normalized}} [label="{{.Name}}" shape="octagon" fillcolor="{{if .Enabled}}lightpink1{{else}}gray{{end}}"] - {{end}} - {{.Normalized}}_start{{range .Stages}} -> {{$planNormalized}}_{{.Normalized}}{{if .Concurrent}} -> { {{range .Tasks}} {{.}} {{end}} }{{else}}{{range .Tasks}} -> {{.}}{{end}}{{end}}{{end}} - {{/*add the fall throughs */}} -{{- range .Stages}}{{$stage := .}}{{range .FallThroughs}}{{$planNormalized}}_{{$stage.Normalized}} -> {{$planNormalized}}_{{.Normalized}} [style="dotted"] + +{{range .Plans}} + + # add the stages for {{.Name}} + edge[color="{{.Color}}"]{{$planNormalized := .Normalized }} + {{- range .Stages}} + {{$planNormalized}}_{{.Normalized}} [label="{{.Name}}" shape="octagon" fillcolor="{{if .Enabled}}lightpink1{{else}}gray{{end}}"]{{end}} + + # {{.Name}} happy path + {{.Normalized}}_start{{range .Stages}} -> {{$planNormalized}}_{{.Normalized}}{{if .Concurrent}} -> { {{range .Tasks}} {{.}} {{end}} }{{else}}{{range .Tasks}} -> {{.}}{{end}}{{end}}{{end}} -> {{.Normalized}}_finish + # {{.Name}} unhappy path + {{- range .Stages}}{{$stage := .}}{{range .FallThroughs}}{{$planNormalized}}_{{$stage.Normalized}} -> {{$planNormalized}}_{{.Normalized}} [style="dotted"] {{end}}{{end -}} {{- end}} + +{{range .MetaPlans}} + # metaplan {{.Name}} + edge[color="{{.Color}}"] + {{.Normalized}}_start -> {{range .Plans}}{{.Normalized}}_start + {{.Normalized}}_finish -> {{end}} {{.Normalized}}_finish +{{end}} } ` @@ -74,14 +102,21 @@ Color string Stages []GraphvizStage } + + GraphvizMetaPlan struct { + Normalized string + Name string + Color string + Plans []GraphvizPlan + } ) -func normalize(str string) string { +func normalize(prefix, str string) string { if normalizeRegex == nil { normalizeRegex = regexp.MustCompile("[^a-zA-Z0-9_]") } - return normalizeRegex.ReplaceAllString(str, "_") + return prefix + "_" + normalizeRegex.ReplaceAllString(str, "_") } func (g *Graphviz) getTasks(cfg *config.Config) []GraphvizTask { @@ -89,7 +124,7 @@ for name, _ := range cfg.Tasks { task := GraphvizTask{ - Normalized: normalize(name), + Normalized: normalize("task", name), Name: name, } tasks = append(tasks, task) @@ -98,40 +133,66 @@ return tasks } +func (g *Graphviz) getMetaPlans(cfg *config.Config) []GraphvizMetaPlan { + metaPlans := []GraphvizMetaPlan{} + + for name, cMetaPlan := range cfg.MetaPlans { + metaPlan := GraphvizMetaPlan{ + Normalized: normalize("metaplan", name), + Name: name, + Color: color.X11(name), + } + + for _, name := range cMetaPlan.Plans { + if plan, found := cfg.Plans[name]; found { + metaPlan.Plans = append(metaPlan.Plans, g.plan(name, plan)) + } + } + + metaPlans = append(metaPlans, metaPlan) + } + + return metaPlans +} + +func (g Graphviz) plan(name string, plan plans.Plan) GraphvizPlan { + graphvizPlan := GraphvizPlan{ + Normalized: normalize("plan", name), + Name: name, + Color: color.X11(name), + Stages: []GraphvizStage{}, + } + + for idx, stage := range plan.Stages { + graphvizStage := GraphvizStage{ + Normalized: normalize("stage", stage.Name), + Name: stage.Name, + Enabled: stage.Enabled, + Concurrent: stage.Concurrent, + Always: stage.Always, + Tasks: []string{}, + } + + for _, task := range stage.Tasks { + graphvizStage.Tasks = append(graphvizStage.Tasks, normalize("task", task)) + } + + graphvizPlan.Stages = append(graphvizPlan.Stages, graphvizStage) + + // if this always runs, if it's not the first add it to the previous stage's fallthroughs + if stage.Always && idx > 0 { + graphvizPlan.Stages[idx-1].FallThroughs = append(graphvizPlan.Stages[idx-1].FallThroughs, graphvizStage) + } + } + + return graphvizPlan +} + func (g *Graphviz) getPlans(cfg *config.Config) []GraphvizPlan { plans := []GraphvizPlan{} for name, plan := range cfg.Plans { - graphvizPlan := GraphvizPlan{ - Normalized: normalize(name), - Name: name, - Color: color.X11(name), - Stages: []GraphvizStage{}, - } - - for idx, stage := range plan.Stages { - graphvizStage := GraphvizStage{ - Normalized: normalize(stage.Name), - Name: stage.Name, - Enabled: stage.Enabled, - Concurrent: stage.Concurrent, - Always: stage.Always, - Tasks: []string{}, - } - - for _, task := range stage.Tasks { - graphvizStage.Tasks = append(graphvizStage.Tasks, normalize(task)) - } - - graphvizPlan.Stages = append(graphvizPlan.Stages, graphvizStage) - - // if this always runs, if it's not the first add it to the previous stage's fallthroughs - if stage.Always && idx > 0 { - graphvizPlan.Stages[idx-1].FallThroughs = append(graphvizPlan.Stages[idx-1].FallThroughs, graphvizStage) - } - } - - plans = append(plans, graphvizPlan) + plans = append(plans, g.plan(name, plan)) } return plans @@ -141,8 +202,9 @@ logger := logging.NewAdapter("graphviz") params := map[string]interface{}{ - "Tasks": g.getTasks(cfg), - "Plans": g.getPlans(cfg), + "Tasks": g.getTasks(cfg), + "Plans": g.getPlans(cfg), + "MetaPlans": g.getMetaPlans(cfg), } // Load the template
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/runners/listmetaplans.go Mon Apr 24 00:15:09 2017 -0500 @@ -0,0 +1,42 @@ +/* + * Convey + * Copyright 2016-2017 Gary Kramlich <grim@reaperworld.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package runners + +import ( + "fmt" + + "bitbucket.org/rw_grim/convey/config" + "bitbucket.org/rw_grim/convey/options" +) + +type ListMetaPlans struct{} + +func (l *ListMetaPlans) Run(cfg *config.Config, plans []string, env []string, opts *options.Options) int { + for metaPlanName, metaPlan := range cfg.MetaPlans { + fmt.Printf("%s\n", metaPlanName) + if len(metaPlan.Plans) == 0 { + fmt.Printf(" (no plans)\n") + } else { + for _, plan := range metaPlan.Plans { + fmt.Printf(" %s\n", plan) + } + } + } + + return 0 +}