85 lines
2.4 KiB
Go
85 lines
2.4 KiB
Go
package agenttask
|
|
|
|
import (
|
|
"regexp"
|
|
"sort"
|
|
"strings"
|
|
)
|
|
|
|
type dependencyStatus string
|
|
|
|
const (
|
|
dependencyReady dependencyStatus = "ready"
|
|
dependencyWaiting dependencyStatus = "waiting"
|
|
dependencyMissing dependencyStatus = "missing"
|
|
dependencyAmbiguous dependencyStatus = "ambiguous"
|
|
dependencyBlocked dependencyStatus = "blocked"
|
|
)
|
|
|
|
type dependencyResult struct {
|
|
Status dependencyStatus
|
|
Ref string
|
|
}
|
|
|
|
var dependencySeparator = regexp.MustCompile(`[^a-z0-9]+`)
|
|
|
|
func normalizeDependencyRef(value string) string {
|
|
value = strings.ToLower(strings.TrimSpace(value))
|
|
value = strings.Trim(value, "`'\"")
|
|
value = dependencySeparator.ReplaceAllString(value, "-")
|
|
return strings.Trim(value, "-")
|
|
}
|
|
|
|
// evaluateDependencies intentionally considers only ExplicitPredecessors.
|
|
// Directory ordinals and declared/unknown/overlapping write sets never create
|
|
// a dependency.
|
|
func evaluateDependencies(
|
|
unit WorkUnit,
|
|
workflow ProjectWorkflowSnapshot,
|
|
works map[WorkUnitID]WorkRecord,
|
|
) dependencyResult {
|
|
if len(unit.ExplicitPredecessors) == 0 {
|
|
return dependencyResult{Status: dependencyReady}
|
|
}
|
|
for _, predecessor := range unit.ExplicitPredecessors {
|
|
ref := normalizeDependencyRef(predecessor.Ref)
|
|
matches := make([]WorkUnit, 0, 1)
|
|
for _, candidate := range workflow.Units {
|
|
if candidate.ID == unit.ID {
|
|
continue
|
|
}
|
|
if normalizeDependencyRef(string(candidate.ID)) == ref {
|
|
matches = append(matches, candidate)
|
|
continue
|
|
}
|
|
for _, alias := range candidate.Aliases {
|
|
if normalizeDependencyRef(alias) == ref {
|
|
matches = append(matches, candidate)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
sort.Slice(matches, func(left, right int) bool {
|
|
return matches[left].ID < matches[right].ID
|
|
})
|
|
switch len(matches) {
|
|
case 0:
|
|
return dependencyResult{Status: dependencyMissing, Ref: predecessor.Ref}
|
|
case 1:
|
|
default:
|
|
return dependencyResult{Status: dependencyAmbiguous, Ref: predecessor.Ref}
|
|
}
|
|
match := matches[0]
|
|
record, tracked := works[match.ID]
|
|
if match.Completed || tracked && record.State == WorkStateCompleted {
|
|
continue
|
|
}
|
|
if tracked && (record.State == WorkStateBlocked ||
|
|
record.State == WorkStateTerminalDeferred ||
|
|
record.State == WorkStateStopped) {
|
|
return dependencyResult{Status: dependencyBlocked, Ref: predecessor.Ref}
|
|
}
|
|
return dependencyResult{Status: dependencyWaiting, Ref: predecessor.Ref}
|
|
}
|
|
return dependencyResult{Status: dependencyReady}
|
|
}
|