forked from tangled.org/core
Monorepo for Tangled — https://tangled.org
at spindle-workflow 86 lines 3.0 kB view raw
1package workflow 2 3import ( 4 "fmt" 5 6 "gopkg.in/yaml.v3" 7 "tangled.org/core/api/tangled" 8 "tangled.org/core/spindle/models" 9) 10 11// WorkflowData wraps the parsed engine schema and original workflow 12// for engines that need access to both during initialization 13type WorkflowData struct { 14 Schema interface{} 15 OriginalWorkflow tangled.Pipeline_Workflow 16 CommonEnv map[string]string // Workflow-level environment variables (common to all engines) 17} 18 19// CommonWorkflowFields represents the standard fields present in all workflow YAMLs. 20// Engines should embed this in their schema structs using `yaml:",inline"` to avoid 21// duplicate YAML unmarshaling. 22type CommonWorkflowFields struct { 23 Engine string `yaml:"engine"` 24 Environment map[string]string `yaml:"environment"` // Workflow-level environment variables 25 Steps []struct { 26 Name string `yaml:"name"` 27 Command string `yaml:"command"` 28 Environment map[string]string `yaml:"environment"` // Per-step environment (overrides/extends workflow-level) 29 } `yaml:"steps"` 30} 31 32// ParseWorkflow parses a workflow YAML using the specified engine's schema 33// and returns a fully initialized workflow ready for execution. 34func ParseWorkflow(twf tangled.Pipeline_Workflow, tpl tangled.Pipeline, engine models.Engine) (*models.Workflow, error) { 35 // 1. Get engine-specific schema struct (should embed CommonWorkflowFields) 36 schema := engine.NewWorkflowSchema() 37 38 // 2. Unmarshal the YAML into the engine's schema ONCE 39 // This captures both common fields (via inline embedding) and engine-specific fields 40 if err := yaml.Unmarshal([]byte(twf.Raw), schema); err != nil { 41 return nil, fmt.Errorf("failed to parse workflow into engine schema: %w", err) 42 } 43 44 // 3. Extract common fields from the schema (via type assertion to interface with embedded fields) 45 commonFields, ok := schema.(interface { 46 GetCommonFields() *CommonWorkflowFields 47 }) 48 if !ok { 49 return nil, fmt.Errorf("engine schema must implement GetCommonFields() method") 50 } 51 common := commonFields.GetCommonFields() 52 53 // 4. Convert common steps to models.Step 54 steps := make([]models.Step, len(common.Steps)) 55 for i, s := range common.Steps { 56 steps[i] = models.DefaultStep{ 57 StepName: s.Name, 58 StepCommand: s.Command, 59 StepKind: models.StepKindUser, 60 StepEnvironment: s.Environment, 61 } 62 } 63 64 // 5. Create the workflow with parsed data 65 // Store both the schema and original workflow for engines that need access to 66 // fields like Clone options 67 workflowData := &WorkflowData{ 68 Schema: schema, 69 OriginalWorkflow: twf, 70 CommonEnv: common.Environment, 71 } 72 73 wf := &models.Workflow{ 74 Steps: steps, 75 Name: twf.Name, 76 Data: workflowData, 77 } 78 79 // 6. Let the engine initialize the workflow 80 // This is where engines can prepend setup steps, transform data, etc. 81 if err := engine.InitWorkflow(wf, tpl); err != nil { 82 return nil, fmt.Errorf("engine initialization failed: %w", err) 83 } 84 85 return wf, nil 86}