-
Notifications
You must be signed in to change notification settings - Fork 7
/
dependency.go
154 lines (133 loc) · 4.5 KB
/
dependency.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
package dev
import (
"github.com/goombaio/dag"
d "github.com/goombaio/dag"
"github.com/pkg/errors"
c "github.com/wish/dev/config"
log "github.com/sirupsen/logrus"
)
const (
// BUILD constant referring to the build command of this project which
// builds the project with dobi as specified in this tools
// configuration file.
BUILD = "build"
// DOWNLOAD constant referring to the build command of this project which
// downloads the project with dobi as specified in this tools
// configuration file.
DOWNLOAD = "download"
// DOWN constant referring to the "down" command of this project which
// stops and removes the project container.
DOWN = "down"
// ALLDOWN constant referring to the "alldown" command of this project which
// stops and removes all project containers.
ALLDOWN = "alldown"
// PS constant referring to the "ps" command of this project which
// shows the status of the containers used by the project.
PS = "ps"
// SH constant referring to the "sh" command of this project which runs
// commands on the project container.
SH = "sh"
// UP constant referring to the "up" command of this project which
// starts the project and any of the specified dependencies.
UP = "up"
)
// Dependency is the interface that is used by all objects in the dev
// configuration implement such that they can be used as a dependency by other
// objects of the configuration.
type Dependency interface {
// PreRun does whatever is required of the dependency. It is run prior
// to the specified command for the given project.
PreRun(command string, appConfig *c.Dev, project *Project)
// Dependencies returns the names of all the dev objects it depends on
// in order to function.
Dependencies() []string
// Name of the dependency. Maps to the name given to the object in the
// dev configuration file.
GetName() string
}
func addDeps(objMap map[string]Dependency, dag *d.DAG, obj Dependency) error {
for _, depName := range obj.Dependencies() {
vertex := d.NewVertex(depName, objMap[depName])
if err := dag.AddVertex(vertex); err != nil {
return err
}
parent, err := dag.GetVertex(obj.GetName())
if err != nil {
return errors.Wrapf(err, "Unable to locate vertex for: %s", obj.GetName())
}
if err := dag.AddEdge(parent, vertex); err != nil {
return errors.Wrapf(err, "Failure adding edge from %s to %s", parent.ID, vertex.ID)
}
if err := addDeps(objMap, dag, objMap[depName]); err != nil {
return err
}
}
return nil
}
// Hmm, the libraries deleteEdge does not remove the parent from the childs
// parent list.
func deleteEdge(parent *dag.Vertex, child *dag.Vertex) error {
for _, c := range parent.Children.Values() {
if c == child {
parent.Children.Remove(child)
child.Parents.Remove(parent)
}
}
return nil
}
func topologicalSort(dag *d.DAG, vertex *d.Vertex) ([]string, error) {
sorted := []string{}
parentless := make(map[string]bool)
parentless[vertex.ID] = true
for ok := true; ok; ok = len(parentless) > 0 {
var n string
for key := range parentless {
n = key
break
}
sorted = SliceInsertString(sorted, n, 0)
delete(parentless, n)
v, err := dag.GetVertex(n)
if err != nil {
return nil, errors.Wrapf(err, "Unexpected missing vertex for: %s", n)
}
children, _ := dag.Successors(v)
for _, child := range children {
//log.Debugf("got a child %s with %d incoming edges", child.ID, child.InDegree())
if err := deleteEdge(v, child); err != nil {
return nil, err
}
if child.InDegree() == 0 {
//log.Debugf("no incoming edges for %s", child.ID)
parentless[child.ID] = true
}
}
}
if dag.Size() != 0 {
return nil, errors.Errorf("Dependency graph has a cycle")
}
// do not return the initial vertex in the final list, b/c we are only
// interested in the dependencies
return sorted[0 : len(sorted)-1], nil
}
// InitDeps runs the PreRun method on each dependency for the specified
// Project.
func InitDeps(objMap map[string]Dependency, appConfig *c.Dev, cmd string, project *Project) error {
dag := d.NewDAG()
vertex := d.NewVertex(project.Name, project)
if err := dag.AddVertex(vertex); err != nil {
return err
}
if err := addDeps(objMap, dag, project); err != nil {
return errors.Wrap(err, "Failure mapping dependencies")
}
deps, err := topologicalSort(dag, vertex)
if err != nil {
return errors.Wrapf(err, "Failure sorting dependencies for %s", project.Name)
}
log.Debugf("Initializing dependencies for %s: %s", project.Name, deps)
for _, dep := range deps {
objMap[dep].PreRun(cmd, appConfig, project)
}
return nil
}