Implement ordered pipeline step resolution
This commit is contained in:
@@ -106,30 +106,60 @@ func validatePipelineProfiles(profiles map[string]pipeline.PipelineProfile) erro
|
||||
if err := validateReferenceMap(id, "", profile.References); err != nil {
|
||||
return err
|
||||
}
|
||||
seenLanes := make(map[string]struct{}, len(profile.Artifacts))
|
||||
for rawLaneID, lane := range profile.Artifacts {
|
||||
laneID := strings.TrimSpace(rawLaneID)
|
||||
if laneID == "" {
|
||||
return fmt.Errorf("pipeline %q artifact lane id must not be empty", id)
|
||||
explicitSteps := profile.Steps != nil
|
||||
if len(profile.Artifacts) > 0 && explicitSteps {
|
||||
return fmt.Errorf("pipeline %q must not declare both artifacts and steps", id)
|
||||
}
|
||||
steps := profile.Steps
|
||||
if !explicitSteps {
|
||||
steps = []pipeline.PipelineStepProfile{{ID: "default", Artifacts: profile.Artifacts}}
|
||||
}
|
||||
if explicitSteps && len(steps) == 0 {
|
||||
return fmt.Errorf("pipeline %q must declare at least one ordered step", id)
|
||||
}
|
||||
seenSteps := make(map[string]struct{}, len(steps))
|
||||
seenLanes := make(map[string]struct{})
|
||||
for index, step := range steps {
|
||||
stepID := strings.TrimSpace(step.ID)
|
||||
if stepID == "" {
|
||||
return fmt.Errorf("pipeline %q step[%d] id must not be empty", id, index)
|
||||
}
|
||||
if _, ok := seenLanes[laneID]; ok {
|
||||
return fmt.Errorf("pipeline %q artifact lane id %q is duplicated after trimming", id, laneID)
|
||||
if _, ok := seenSteps[stepID]; ok {
|
||||
return fmt.Errorf("pipeline %q step id %q is duplicated after trimming", id, stepID)
|
||||
}
|
||||
seenLanes[laneID] = struct{}{}
|
||||
if err := validateReferenceMap(id, laneID, lane.References); err != nil {
|
||||
return err
|
||||
seenSteps[stepID] = struct{}{}
|
||||
if explicitSteps {
|
||||
if err := validateReferenceMapForContext(id, "", "step "+stepID, step.References, true); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := validateBinding(id, laneID, "extract", lane.Extract, true); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validateBinding(id, laneID, "merge", lane.Merge, true); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validateBinding(id, laneID, "normalize", lane.Normalize, true); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(lane.Validators) > 0 {
|
||||
return fmt.Errorf("pipeline %q lane %q validators are not supported at artifact lane level; use extract.validators, merge.validators, or normalize.validators", id, laneID)
|
||||
for rawLaneID, lane := range step.Artifacts {
|
||||
laneID := strings.TrimSpace(rawLaneID)
|
||||
if laneID == "" {
|
||||
return fmt.Errorf("pipeline %q artifact lane id must not be empty", id)
|
||||
}
|
||||
if _, ok := seenLanes[laneID]; ok {
|
||||
if !explicitSteps {
|
||||
return fmt.Errorf("pipeline %q artifact lane id %q is duplicated after trimming", id, laneID)
|
||||
}
|
||||
return fmt.Errorf("pipeline %q artifact lane id %q is duplicated across steps", id, laneID)
|
||||
}
|
||||
seenLanes[laneID] = struct{}{}
|
||||
if err := validateReferenceMapForContext(id, laneID, "", lane.References, true); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validateBinding(id, laneID, "extract", lane.Extract, true); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validateBinding(id, laneID, "merge", lane.Merge, true); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validateBinding(id, laneID, "normalize", lane.Normalize, true); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(lane.Validators) > 0 {
|
||||
return fmt.Errorf("pipeline %q lane %q validators are not supported at artifact lane level; use extract.validators, merge.validators, or normalize.validators", id, laneID)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -164,7 +194,7 @@ func validateBinding(
|
||||
}
|
||||
return fmt.Errorf("pipeline %q %s references are not supported", pipelineID, slot)
|
||||
}
|
||||
return validateReferenceMapForContext(pipelineID, laneID, slot, binding.References)
|
||||
return validateReferenceMapForContext(pipelineID, laneID, slot, binding.References, true)
|
||||
}
|
||||
|
||||
func validateValidatorOverride(pipelineID string, laneID string, slot string, override pipeline.ValidatorOverride) error {
|
||||
@@ -197,13 +227,13 @@ func validateValidatorOverride(pipelineID string, laneID string, slot string, ov
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateReferenceMap(pipelineID string, laneID string, references map[string]string) error {
|
||||
return validateReferenceMapForContext(pipelineID, laneID, "", references)
|
||||
func validateReferenceMap(pipelineID string, laneID string, references map[string]pipeline.ReferenceSource) error {
|
||||
return validateReferenceMapForContext(pipelineID, laneID, "", references, false)
|
||||
}
|
||||
|
||||
func validateReferenceMapForContext(pipelineID string, laneID string, slot string, references map[string]string) error {
|
||||
func validateReferenceMapForContext(pipelineID string, laneID string, slot string, references map[string]pipeline.ReferenceSource, generatedAllowed bool) error {
|
||||
seen := make(map[string]struct{}, len(references))
|
||||
for rawSlotName, rawSource := range references {
|
||||
for rawSlotName, source := range references {
|
||||
slotName := strings.TrimSpace(rawSlotName)
|
||||
if slotName == "" {
|
||||
return fmt.Errorf("%s reference slot name must not be empty", referenceContext(pipelineID, laneID, slot))
|
||||
@@ -212,7 +242,19 @@ func validateReferenceMapForContext(pipelineID string, laneID string, slot strin
|
||||
return fmt.Errorf("%s reference slot %q is duplicated after trimming", referenceContext(pipelineID, laneID, slot), slotName)
|
||||
}
|
||||
seen[slotName] = struct{}{}
|
||||
if strings.TrimSpace(rawSource) == "" {
|
||||
if source.Artifact != nil {
|
||||
if !generatedAllowed {
|
||||
return fmt.Errorf("%s reference slot %q must use an external path", referenceContext(pipelineID, laneID, slot), slotName)
|
||||
}
|
||||
if strings.TrimSpace(source.Artifact.Step) == "" || strings.TrimSpace(source.Artifact.Lane) == "" {
|
||||
return fmt.Errorf("%s reference slot %q artifact selector step and lane must not be empty", referenceContext(pipelineID, laneID, slot), slotName)
|
||||
}
|
||||
if strings.TrimSpace(source.Path) != "" {
|
||||
return fmt.Errorf("%s reference slot %q must contain exactly one source form", referenceContext(pipelineID, laneID, slot), slotName)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if strings.TrimSpace(source.Path) == "" {
|
||||
return fmt.Errorf("%s reference slot %q source must not be empty", referenceContext(pipelineID, laneID, slot), slotName)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user