grim/convey
Clone
Summary
Browse
Changes
Graph
Bump the version for release
v0.14.0-alpha3
2018-02-20, Gary Kramlich
166a6d1979fa
Bump the version for release
// Convey
// Copyright 2016-2018 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
(
"bytes"
"fmt"
"regexp"
"text/template"
"bitbucket.org/rw_grim/convey/color"
"bitbucket.org/rw_grim/convey/config"
"bitbucket.org/rw_grim/convey/logging"
"bitbucket.org/rw_grim/convey/plans"
"bitbucket.org/rw_grim/convey/state"
)
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}}
# 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}}"{{if .Always}} style="bold,filled,rounded"{{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}}" style="dashed,bold"]
{{.Normalized}}_start -> {{range .Plans}}{{.Normalized}}_start
{{.Normalized}}_finish -> {{end}} {{.Normalized}}_finish
{{end}}
}
`
var
normalizeRegex
*
regexp
.
Regexp
type
(
// Graphviz is a Runner that will create a graphviz output for the config
// that has been loaded.
Graphviz
struct
{}
graphvizTask
struct
{
Normalized
string
Name
string
}
graphvizStage
struct
{
Normalized
string
Name
string
Enabled
bool
Concurrent
bool
Always
bool
Tasks
[]
string
FallThroughs
[]
graphvizStage
}
graphvizPlan
struct
{
Normalized
string
Name
string
Color
string
Stages
[]
graphvizStage
}
graphvizMetaPlan
struct
{
Normalized
string
Name
string
Color
string
Plans
[]
graphvizPlan
}
)
func
normalize
(
prefix
,
str
string
)
string
{
if
normalizeRegex
==
nil
{
normalizeRegex
=
regexp
.
MustCompile
(
"[^a-zA-Z0-9_]"
)
}
return
prefix
+
"_"
+
normalizeRegex
.
ReplaceAllString
(
str
,
"_"
)
}
func
(
g
*
Graphviz
)
getTasks
(
cfg
*
config
.
Config
)
[]
graphvizTask
{
tasks
:=
[]
graphvizTask
{}
for
name
:=
range
cfg
.
Tasks
{
task
:=
graphvizTask
{
Normalized
:
normalize
(
"task"
,
name
),
Name
:
name
,
}
tasks
=
append
(
tasks
,
task
)
}
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
{
gvizPlan
:=
graphvizPlan
{
Normalized
:
normalize
(
"plan"
,
name
),
Name
:
name
,
Color
:
color
.
X11
(
name
),
Stages
:
[]
graphvizStage
{},
}
for
idx
,
stage
:=
range
plan
.
Stages
{
gvizStage
:=
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
{
gvizStage
.
Tasks
=
append
(
gvizStage
.
Tasks
,
normalize
(
"task"
,
task
))
}
gvizPlan
.
Stages
=
append
(
gvizPlan
.
Stages
,
gvizStage
)
// if this always runs, if it's not the first add it to the previous stages' fallthroughs
if
stage
.
Always
&&
idx
>
0
{
for
i
:=
0
;
i
<
idx
;
i
++
{
gvizPlan
.
Stages
[
i
].
FallThroughs
=
append
(
gvizPlan
.
Stages
[
idx
-
1
].
FallThroughs
,
gvizStage
)
}
}
}
return
gvizPlan
}
func
(
g
*
Graphviz
)
getPlans
(
cfg
*
config
.
Config
)
[]
graphvizPlan
{
plans
:=
[]
graphvizPlan
{}
for
name
,
plan
:=
range
cfg
.
Plans
{
plans
=
append
(
plans
,
g
.
plan
(
name
,
plan
))
}
return
plans
}
// Run runs the graphviz runner.
func
(
g
*
Graphviz
)
Run
(
cfg
*
config
.
Config
,
plans
[]
string
,
env
[]
string
,
st
*
state
.
State
)
int
{
logger
:=
logging
.
NewAdapter
(
"graphviz"
)
params
:=
map
[
string
]
interface
{}{
"Tasks"
:
g
.
getTasks
(
cfg
),
"Plans"
:
g
.
getPlans
(
cfg
),
"MetaPlans"
:
g
.
getMetaPlans
(
cfg
),
}
// Load the template
tmpl
,
err
:=
template
.
New
(
"graphviz"
).
Parse
(
graphvizTemplate
)
if
err
!=
nil
{
if
nErr
:=
logger
.
Fatalf
(
"error: %s"
,
err
);
nErr
!=
nil
{
fmt
.
Printf
(
"error reporting error: %s\n"
,
nErr
)
}
return
1
}
// Run the template
output
:=
new
(
bytes
.
Buffer
)
err
=
tmpl
.
Execute
(
output
,
params
)
if
err
!=
nil
{
if
nErr
:=
logger
.
Fatalf
(
"error: %s"
,
err
);
nErr
!=
nil
{
fmt
.
Printf
(
"error reporting error: %s\n"
,
nErr
)
}
return
1
}
// Output the template
fmt
.
Print
(
output
)
return
0
}