forked from tangled.org/core
this repo has no description
1package workflow 2 3import ( 4 "fmt" 5 6 "tangled.sh/tangled.sh/core/api/tangled" 7) 8 9type RawWorkflow struct { 10 Name string 11 Contents []byte 12} 13 14type RawPipeline = []RawWorkflow 15 16type Compiler struct { 17 Trigger tangled.Pipeline_TriggerMetadata 18 Diagnostics Diagnostics 19} 20 21type Diagnostics struct { 22 Errors []Error 23 Warnings []Warning 24} 25 26func (d *Diagnostics) IsEmpty() bool { 27 return len(d.Errors) == 0 && len(d.Warnings) == 0 28} 29 30func (d *Diagnostics) Combine(o Diagnostics) { 31 d.Errors = append(d.Errors, o.Errors...) 32 d.Warnings = append(d.Warnings, o.Warnings...) 33} 34 35func (d *Diagnostics) AddWarning(path string, kind WarningKind, reason string) { 36 d.Warnings = append(d.Warnings, Warning{path, kind, reason}) 37} 38 39func (d *Diagnostics) AddError(path string, err error) { 40 d.Errors = append(d.Errors, Error{path, err}) 41} 42 43func (d Diagnostics) IsErr() bool { 44 return len(d.Errors) != 0 45} 46 47type Error struct { 48 Path string 49 Error error 50} 51 52func (e Error) String() string { 53 return fmt.Sprintf("error: %s: %s", e.Path, e.Error.Error()) 54} 55 56type Warning struct { 57 Path string 58 Type WarningKind 59 Reason string 60} 61 62func (w Warning) String() string { 63 return fmt.Sprintf("warning: %s: %s: %s", w.Path, w.Type, w.Reason) 64} 65 66type WarningKind string 67 68var ( 69 WorkflowSkipped WarningKind = "workflow skipped" 70 InvalidConfiguration WarningKind = "invalid configuration" 71) 72 73func (compiler *Compiler) Parse(p RawPipeline) Pipeline { 74 var pp Pipeline 75 76 for _, w := range p { 77 wf, err := FromFile(w.Name, w.Contents) 78 if err != nil { 79 compiler.Diagnostics.AddError(w.Name, err) 80 continue 81 } 82 83 pp = append(pp, wf) 84 } 85 86 return pp 87} 88 89// convert a repositories' workflow files into a fully compiled pipeline that runners accept 90func (compiler *Compiler) Compile(p Pipeline) tangled.Pipeline { 91 cp := tangled.Pipeline{ 92 TriggerMetadata: &compiler.Trigger, 93 } 94 95 for _, wf := range p { 96 cw := compiler.compileWorkflow(wf) 97 98 // empty workflows are not added to the pipeline 99 if len(cw.Steps) == 0 { 100 continue 101 } 102 103 cp.Workflows = append(cp.Workflows, &cw) 104 } 105 106 return cp 107} 108 109func (compiler *Compiler) compileWorkflow(w Workflow) tangled.Pipeline_Workflow { 110 cw := tangled.Pipeline_Workflow{} 111 112 if !w.Match(compiler.Trigger) { 113 compiler.Diagnostics.AddWarning( 114 w.Name, 115 WorkflowSkipped, 116 fmt.Sprintf("did not match trigger %s", compiler.Trigger.Kind), 117 ) 118 return cw 119 } 120 121 if len(w.Steps) == 0 { 122 compiler.Diagnostics.AddWarning( 123 w.Name, 124 WorkflowSkipped, 125 "empty workflow", 126 ) 127 return cw 128 } 129 130 // validate clone options 131 compiler.analyzeCloneOptions(w) 132 133 cw.Name = w.Name 134 cw.Dependencies = w.Dependencies.AsRecord() 135 for _, s := range w.Steps { 136 step := tangled.Pipeline_Step{ 137 Command: s.Command, 138 Name: s.Name, 139 } 140 for k, v := range s.Environment { 141 e := &tangled.Pipeline_Pair{ 142 Key: k, 143 Value: v, 144 } 145 step.Environment = append(step.Environment, e) 146 } 147 cw.Steps = append(cw.Steps, &step) 148 } 149 for k, v := range w.Environment { 150 e := &tangled.Pipeline_Pair{ 151 Key: k, 152 Value: v, 153 } 154 cw.Environment = append(cw.Environment, e) 155 } 156 157 o := w.CloneOpts.AsRecord() 158 cw.Clone = &o 159 160 return cw 161} 162 163func (compiler *Compiler) analyzeCloneOptions(w Workflow) { 164 if w.CloneOpts.Skip && w.CloneOpts.IncludeSubmodules { 165 compiler.Diagnostics.AddWarning( 166 w.Name, 167 InvalidConfiguration, 168 "cannot apply `clone.skip` and `clone.submodules`", 169 ) 170 } 171 172 if w.CloneOpts.Skip && w.CloneOpts.Depth > 0 { 173 compiler.Diagnostics.AddWarning( 174 w.Name, 175 InvalidConfiguration, 176 "cannot apply `clone.skip` and `clone.depth`", 177 ) 178 } 179}