completed phase 2b
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
package ci
|
||||
|
||||
import "fmt"
|
||||
|
||||
// dagNode holds a job name and its resolved dependencies.
|
||||
type dagNode struct {
|
||||
name string
|
||||
needs []string
|
||||
}
|
||||
|
||||
// TopoSort returns the job names in a valid topological execution order.
|
||||
// Returns an error if the dependency graph has cycles or references unknown jobs.
|
||||
func TopoSort(jobs map[string]WorkflowJob) ([]string, error) {
|
||||
nodes := make(map[string]*dagNode, len(jobs))
|
||||
for name, job := range jobs {
|
||||
nodes[name] = &dagNode{name: name, needs: []string(job.Needs)}
|
||||
}
|
||||
// Validate all dependencies exist.
|
||||
for _, node := range nodes {
|
||||
for _, dep := range node.needs {
|
||||
if _, ok := nodes[dep]; !ok {
|
||||
return nil, fmt.Errorf("job %q depends on unknown job %q", node.name, dep)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var order []string
|
||||
visited := make(map[string]bool, len(nodes))
|
||||
inStack := make(map[string]bool, len(nodes))
|
||||
|
||||
var visit func(name string) error
|
||||
visit = func(name string) error {
|
||||
if inStack[name] {
|
||||
return fmt.Errorf("cycle detected at job %q", name)
|
||||
}
|
||||
if visited[name] {
|
||||
return nil
|
||||
}
|
||||
inStack[name] = true
|
||||
for _, dep := range nodes[name].needs {
|
||||
if err := visit(dep); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
inStack[name] = false
|
||||
visited[name] = true
|
||||
order = append(order, name)
|
||||
return nil
|
||||
}
|
||||
|
||||
for name := range nodes {
|
||||
if err := visit(name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return order, nil
|
||||
}
|
||||
|
||||
// ReadyJobs returns the names of jobs whose dependencies are all in completedJobs.
|
||||
func ReadyJobs(jobs map[string]WorkflowJob, completedJobs map[string]bool, enqueuedJobs map[string]bool) []string {
|
||||
var ready []string
|
||||
for name, job := range jobs {
|
||||
if enqueuedJobs[name] {
|
||||
continue
|
||||
}
|
||||
allDone := true
|
||||
for _, dep := range job.Needs {
|
||||
if !completedJobs[dep] {
|
||||
allDone = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if allDone {
|
||||
ready = append(ready, name)
|
||||
}
|
||||
}
|
||||
return ready
|
||||
}
|
||||
Reference in New Issue
Block a user