Infer artifact dependencies from configured inputs

This commit is contained in:
2026-09-07 04:39:49 +00:00
parent 92432232ef
commit 63a86c43f4
12 changed files with 321 additions and 65 deletions

View File

@@ -108,10 +108,9 @@ func validateScriptoriumDependenciesAndInputs(
referencedArtifacts := make(map[string]struct{})
for _, artifactName := range artifactNames {
artifactCfg := artifacts[artifactName]
depSet := make(map[string]struct{}, len(artifactCfg.DependsOn))
for i, depName := range artifactCfg.DependsOn {
for _, depName := range artifactCfg.DependsOn {
trimmedDep := strings.TrimSpace(depName)
field := fmt.Sprintf("pipeline.scriptorium.artifacts.%s.depends_on[%d]", artifactName, i)
field := dependencyOriginField(artifactName, trimmedDep, artifactCfg)
if trimmedDep == "" {
return nil, fmt.Errorf("%s must be non-empty", field)
}
@@ -119,9 +118,8 @@ func validateScriptoriumDependenciesAndInputs(
return nil, fmt.Errorf("%s %q is not a configured artifact key", field, depName)
}
if trimmedDep == artifactName {
return nil, fmt.Errorf("pipeline.scriptorium.artifacts.%s.depends_on must not include itself", artifactName)
return nil, fmt.Errorf("%s must not include itself", field)
}
depSet[trimmedDep] = struct{}{}
referencedArtifacts[trimmedDep] = struct{}{}
}
@@ -146,15 +144,6 @@ func validateScriptoriumDependenciesAndInputs(
return nil, err
}
if referencedArtifact != "" {
if _, ok := depSet[referencedArtifact]; !ok {
return nil, fmt.Errorf(
"pipeline.scriptorium.artifacts.%s.inputs.%s.source %q requires depends_on entry %q",
artifactName,
inputName,
source,
referencedArtifact,
)
}
referencedArtifacts[referencedArtifact] = struct{}{}
}
}
@@ -281,13 +270,17 @@ func ValidateScriptoriumArtifactDependencies(artifacts map[string]ScriptoriumArt
for _, dep := range cfg.DependsOn {
trimmedDep := strings.TrimSpace(dep)
if trimmedDep == "" {
return fmt.Errorf("pipeline.scriptorium.artifacts.%s.depends_on entries must be non-empty", name)
field := dependencyOriginField(name, trimmedDep, cfg)
if field == fmt.Sprintf("pipeline.scriptorium.artifacts.%s.depends_on", name) {
return fmt.Errorf("%s entries must be non-empty", field)
}
return fmt.Errorf("%s must be non-empty", field)
}
if _, ok := artifacts[trimmedDep]; !ok {
return fmt.Errorf("pipeline.scriptorium.artifacts.%s dependency %q is not configured", name, dep)
return fmt.Errorf("%s dependency %q is not configured", dependencyOriginField(name, trimmedDep, cfg), dep)
}
if trimmedDep == name {
return fmt.Errorf("pipeline.scriptorium.artifacts.%s.depends_on must not include itself", name)
return fmt.Errorf("%s must not include itself", dependencyOriginField(name, trimmedDep, cfg))
}
if _, duplicate := seen[trimmedDep]; duplicate {
continue
@@ -303,14 +296,17 @@ func ValidateScriptoriumArtifactDependencies(artifacts map[string]ScriptoriumArt
var visit func(node string) error
visit = func(node string) error {
if visiting[node] {
return fmt.Errorf("pipeline.scriptorium.artifacts dependencies must not contain cycles")
}
if visited[node] {
return nil
}
visiting[node] = true
for _, dep := range graph[node] {
if visiting[dep] {
return fmt.Errorf(
"%s creates a cycle; pipeline.scriptorium.artifacts dependencies must not contain cycles",
dependencyOriginField(node, dep, artifacts[node]),
)
}
if err := visit(dep); err != nil {
return err
}