-
Notifications
You must be signed in to change notification settings - Fork 18
/
validate.go
103 lines (87 loc) · 2.69 KB
/
validate.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
package parser
import (
"fmt"
"github.com/cirruslabs/cirrus-ci-agent/api"
"github.com/cirruslabs/cirrus-cli/pkg/parser/parsererror"
"github.com/cirruslabs/cirrus-cli/pkg/parser/task"
"strings"
)
func commandInstructionName(command *api.Command) string {
switch command.Instruction.(type) {
case *api.Command_ExitInstruction:
return "exit"
case *api.Command_ScriptInstruction:
return "script"
case *api.Command_BackgroundScriptInstruction:
return "background script"
case *api.Command_CacheInstruction:
return "cache"
case *api.Command_UploadCacheInstruction:
return "upload cache"
case *api.Command_CloneInstruction:
return "clone"
case *api.Command_FileInstruction:
return "file"
case *api.Command_ArtifactsInstruction:
return "artifacts"
}
return "unknown"
}
func validateTask(task *api.Task) error {
alreadySeenNames := make(map[string]string)
for _, command := range task.Commands {
if seenInstructionName, seen := alreadySeenNames[command.Name]; seen {
return fmt.Errorf("%w: task '%s' %s and %s instructions have identical name",
parsererror.ErrParsing, task.Name, seenInstructionName, commandInstructionName(command))
}
alreadySeenNames[command.Name] = commandInstructionName(command)
}
return nil
}
func validateDependenciesDeep(tasks []task.ParseableTaskLike) error {
satisfiedIDs := make(map[int64]struct{})
for {
// Collect tasks that still have some dependencies unsatisfied
var unsatisfiedTasks []task.ParseableTaskLike
for _, task := range tasks {
if _, ok := satisfiedIDs[task.ID()]; !ok {
unsatisfiedTasks = append(unsatisfiedTasks, task)
}
}
// Try to resolve these dependencies
var newlySatisfiedTasks []task.ParseableTaskLike
for _, unsatisfiedTask := range unsatisfiedTasks {
satisfied := true
for _, dependencyID := range unsatisfiedTask.DependsOnIDs() {
if _, ok := satisfiedIDs[dependencyID]; !ok {
satisfied = false
break
}
}
if satisfied {
newlySatisfiedTasks = append(newlySatisfiedTasks, unsatisfiedTask)
}
}
if len(newlySatisfiedTasks) == 0 {
// We're probably done or there's a missing/circular dependency exist
break
} else {
// Remember tasks that are now resolved
for _, newlySatisfiedTask := range newlySatisfiedTasks {
satisfiedIDs[newlySatisfiedTask.ID()] = struct{}{}
}
}
}
// All tasks satisfied?
if len(satisfiedIDs) != len(tasks) {
var unsatisfiedNames []string
for _, task := range tasks {
if _, ok := satisfiedIDs[task.ID()]; !ok {
unsatisfiedNames = append(unsatisfiedNames, task.Name())
}
}
return fmt.Errorf("%w: error in dependencies between tasks: %v",
parsererror.ErrParsing, strings.Join(unsatisfiedNames, ", "))
}
return nil
}