validateCallWorkflow validates that the call-workflow configuration is correct. It checks that each workflow exists, declares a workflow_call trigger, and is not a self-reference.
(data *WorkflowData, workflowPath string)
| 17 | // It checks that each workflow exists, declares a workflow_call trigger, and is not |
| 18 | // a self-reference. |
| 19 | func (c *Compiler) validateCallWorkflow(data *WorkflowData, workflowPath string) error { |
| 20 | callWorkflowValidationLog.Print("Starting call-workflow validation") |
| 21 | |
| 22 | if data.SafeOutputs == nil || data.SafeOutputs.CallWorkflow == nil { |
| 23 | callWorkflowValidationLog.Print("No call-workflow configuration found") |
| 24 | return nil |
| 25 | } |
| 26 | |
| 27 | config := data.SafeOutputs.CallWorkflow |
| 28 | |
| 29 | if len(config.Workflows) == 0 { |
| 30 | return errors.New("call-workflow: must specify at least one workflow in the list\n\nExample configuration in workflow frontmatter:\nsafe-outputs:\n call-workflow:\n workflows: [workflow-name-1, workflow-name-2]\n\nWorkflow names should match the filename without the .md extension") |
| 31 | } |
| 32 | |
| 33 | // Check for duplicate workflow names — each name must appear exactly once |
| 34 | seen := make(map[string]int, len(config.Workflows)) |
| 35 | for i, name := range config.Workflows { |
| 36 | if prev, exists := seen[name]; exists { |
| 37 | return fmt.Errorf("call-workflow: duplicate workflow name '%s' at index %d (first seen at index %d)\n\nEach workflow may appear only once in the list", name, i, prev) |
| 38 | } |
| 39 | seen[name] = i |
| 40 | } |
| 41 | |
| 42 | // Get the current workflow name for self-reference check |
| 43 | currentWorkflowName := getCurrentWorkflowName(workflowPath) |
| 44 | callWorkflowValidationLog.Printf("Current workflow name: %s", currentWorkflowName) |
| 45 | collector := NewErrorCollector(c.failFast) |
| 46 | for _, workflowName := range config.Workflows { |
| 47 | callWorkflowValidationLog.Printf("Validating workflow: %s", workflowName) |
| 48 | if err := validateNotSelfReference(workflowName, currentWorkflowName); err != nil { |
| 49 | if returnErr := collector.Add(err); returnErr != nil { |
| 50 | return returnErr |
| 51 | } |
| 52 | continue |
| 53 | } |
| 54 | fileResult, err := findWorkflowFile(workflowName, workflowPath) |
| 55 | if err != nil { |
| 56 | findErr := fmt.Errorf("call-workflow: error finding workflow '%s': %w", workflowName, err) |
| 57 | if returnErr := collector.Add(findErr); returnErr != nil { |
| 58 | return returnErr |
| 59 | } |
| 60 | continue |
| 61 | } |
| 62 | if err := validateWorkflowFileExists(fileResult, workflowName, workflowPath); err != nil { |
| 63 | if returnErr := collector.Add(err); returnErr != nil { |
| 64 | return returnErr |
| 65 | } |
| 66 | continue |
| 67 | } |
| 68 | if err := validateWorkflowSupportsCallTrigger(workflowName, fileResult); err != nil { |
| 69 | if returnErr := collector.Add(err); returnErr != nil { |
| 70 | return returnErr |
| 71 | } |
| 72 | continue |
| 73 | } |
| 74 | callWorkflowValidationLog.Printf("Workflow '%s' is valid for call-workflow", workflowName) |
| 75 | } |
| 76 | callWorkflowValidationLog.Printf("Call workflow validation completed: error_count=%d, total_workflows=%d", collector.Count(), len(config.Workflows)) |