forked from tangled.org/core
Monorepo for Tangled

workflow: move workflow parsing into compiler

this simplifies the error collection logic a lot.

Signed-off-by: oppiliappan <me@oppi.li>

authored by oppi.li and committed by Tangled bba4923d 99e405a1

Changed files
+65 -49
knotserver
workflow
+6 -10
knotserver/ingester.go
··· 152 152 return err 153 153 } 154 154 155 - var pipeline workflow.Pipeline 155 + var pipeline workflow.RawPipeline 156 156 for _, e := range workflowDir { 157 157 if !e.IsFile { 158 158 continue ··· 164 164 continue 165 165 } 166 166 167 - wf, err := workflow.FromFile(e.Name, contents) 168 - if err != nil { 169 - // TODO: log here, respond to client that is pushing 170 - h.l.Error("failed to parse workflow", "err", err, "path", fpath) 171 - continue 172 - } 173 - 174 - pipeline = append(pipeline, wf) 167 + pipeline = append(pipeline, workflow.RawWorkflow{ 168 + Name: e.Name, 169 + Contents: contents, 170 + }) 175 171 } 176 172 177 173 trigger := tangled.Pipeline_PullRequestTriggerData{ ··· 193 189 }, 194 190 } 195 191 196 - cp := compiler.Compile(pipeline) 192 + cp := compiler.Compile(compiler.Parse(pipeline)) 197 193 eventJson, err := json.Marshal(cp) 198 194 if err != nil { 199 195 return err
+14 -34
knotserver/internal.go
··· 200 200 return err 201 201 } 202 202 203 - pipelineParseErrors := []string{} 204 - 205 - var pipeline workflow.Pipeline 203 + var pipeline workflow.RawPipeline 206 204 for _, e := range workflowDir { 207 205 if !e.IsFile { 208 206 continue ··· 214 212 continue 215 213 } 216 214 217 - wf, err := workflow.FromFile(e.Name, contents) 218 - if err != nil { 219 - h.l.Error("failed to parse workflow", "err", err, "path", fpath) 220 - pipelineParseErrors = append(pipelineParseErrors, fmt.Sprintf("- at %s: %s\n", fpath, err)) 221 - continue 222 - } 223 - 224 - pipeline = append(pipeline, wf) 215 + pipeline = append(pipeline, workflow.RawWorkflow{ 216 + Name: e.Name, 217 + Contents: contents, 218 + }) 225 219 } 226 220 227 221 trigger := tangled.Pipeline_PushTriggerData{ ··· 242 236 }, 243 237 } 244 238 245 - cp := compiler.Compile(pipeline) 239 + cp := compiler.Compile(compiler.Parse(pipeline)) 246 240 eventJson, err := json.Marshal(cp) 247 241 if err != nil { 248 242 return err 249 243 } 250 244 251 245 if pushOptions.verboseCi { 252 - hasDiagnostics := false 253 - if len(pipelineParseErrors) > 0 { 254 - hasDiagnostics = true 255 - *clientMsgs = append(*clientMsgs, "error: failed to parse workflow(s):") 256 - for _, error := range pipelineParseErrors { 257 - *clientMsgs = append(*clientMsgs, error) 258 - } 259 - } 260 - if len(compiler.Diagnostics.Errors) > 0 { 261 - hasDiagnostics = true 262 - *clientMsgs = append(*clientMsgs, "error(s) on pipeline:") 263 - for _, error := range compiler.Diagnostics.Errors { 264 - *clientMsgs = append(*clientMsgs, fmt.Sprintf("- %s:", error)) 265 - } 246 + if compiler.Diagnostics.IsEmpty() { 247 + *clientMsgs = append(*clientMsgs, "success: pipeline compiled with no diagnostics") 266 248 } 267 - if len(compiler.Diagnostics.Warnings) > 0 { 268 - hasDiagnostics = true 269 - *clientMsgs = append(*clientMsgs, "warning(s) on pipeline:") 270 - for _, warning := range compiler.Diagnostics.Warnings { 271 - *clientMsgs = append(*clientMsgs, fmt.Sprintf("- at %s: %s: %s", warning.Path, warning.Type, warning.Reason)) 272 - } 249 + 250 + for _, e := range compiler.Diagnostics.Errors { 251 + *clientMsgs = append(*clientMsgs, e.String()) 273 252 } 274 - if !hasDiagnostics { 275 - *clientMsgs = append(*clientMsgs, "success: pipeline compiled with no diagnostics") 253 + 254 + for _, w := range compiler.Diagnostics.Warnings { 255 + *clientMsgs = append(*clientMsgs, w.String()) 276 256 } 277 257 } 278 258
+45 -5
workflow/compile.go
··· 6 6 "tangled.sh/tangled.sh/core/api/tangled" 7 7 ) 8 8 9 + type RawWorkflow struct { 10 + Name string 11 + Contents []byte 12 + } 13 + 14 + type RawPipeline = []RawWorkflow 15 + 9 16 type Compiler struct { 10 17 Trigger tangled.Pipeline_TriggerMetadata 11 18 Diagnostics Diagnostics 12 19 } 13 20 14 21 type Diagnostics struct { 15 - Errors []error 22 + Errors []Error 16 23 Warnings []Warning 24 + } 25 + 26 + func (d *Diagnostics) IsEmpty() bool { 27 + return len(d.Errors) == 0 && len(d.Warnings) == 0 17 28 } 18 29 19 30 func (d *Diagnostics) Combine(o Diagnostics) { ··· 25 36 d.Warnings = append(d.Warnings, Warning{path, kind, reason}) 26 37 } 27 38 28 - func (d *Diagnostics) AddError(err error) { 29 - d.Errors = append(d.Errors, err) 39 + func (d *Diagnostics) AddError(path string, err error) { 40 + d.Errors = append(d.Errors, Error{path, err}) 30 41 } 31 42 32 43 func (d Diagnostics) IsErr() bool { 33 44 return len(d.Errors) != 0 34 45 } 35 46 47 + type Error struct { 48 + Path string 49 + Error error 50 + } 51 + 52 + func (e Error) String() string { 53 + return fmt.Sprintf("error: %s: %s", e.Path, e.Error.Error()) 54 + } 55 + 36 56 type Warning struct { 37 57 Path string 38 58 Type WarningKind 39 59 Reason string 40 60 } 41 61 62 + func (w Warning) String() string { 63 + return fmt.Sprintf("warning: %s: %s: %s", w.Path, w.Type, w.Reason) 64 + } 65 + 42 66 type WarningKind string 43 67 44 68 var ( ··· 46 70 InvalidConfiguration WarningKind = "invalid configuration" 47 71 ) 48 72 73 + func (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 + 49 89 // convert a repositories' workflow files into a fully compiled pipeline that runners accept 50 90 func (compiler *Compiler) Compile(p Pipeline) tangled.Pipeline { 51 91 cp := tangled.Pipeline{ 52 92 TriggerMetadata: &compiler.Trigger, 53 93 } 54 94 55 - for _, w := range p { 56 - cw := compiler.compileWorkflow(w) 95 + for _, wf := range p { 96 + cw := compiler.compileWorkflow(wf) 57 97 58 98 // empty workflows are not added to the pipeline 59 99 if len(cw.Steps) == 0 {