workflow: move workflow parsing into compiler #487

merged
opened by oppi.li targeting master from push-vnxxyxursomy

this simplifies the error collection logic a lot.

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

Changed files
+65 -49
knotserver
workflow
+6 -10
knotserver/ingester.go
···
return err
}
-
var pipeline workflow.Pipeline
+
var pipeline workflow.RawPipeline
for _, e := range workflowDir {
if !e.IsFile {
continue
···
continue
}
-
wf, err := workflow.FromFile(e.Name, contents)
-
if err != nil {
-
// TODO: log here, respond to client that is pushing
-
h.l.Error("failed to parse workflow", "err", err, "path", fpath)
-
continue
-
}
-
-
pipeline = append(pipeline, wf)
+
pipeline = append(pipeline, workflow.RawWorkflow{
+
Name: e.Name,
+
Contents: contents,
+
})
}
trigger := tangled.Pipeline_PullRequestTriggerData{
···
},
}
-
cp := compiler.Compile(pipeline)
+
cp := compiler.Compile(compiler.Parse(pipeline))
eventJson, err := json.Marshal(cp)
if err != nil {
return err
+14 -34
knotserver/internal.go
···
return err
}
-
pipelineParseErrors := []string{}
-
-
var pipeline workflow.Pipeline
+
var pipeline workflow.RawPipeline
for _, e := range workflowDir {
if !e.IsFile {
continue
···
continue
}
-
wf, err := workflow.FromFile(e.Name, contents)
-
if err != nil {
-
h.l.Error("failed to parse workflow", "err", err, "path", fpath)
-
pipelineParseErrors = append(pipelineParseErrors, fmt.Sprintf("- at %s: %s\n", fpath, err))
-
continue
-
}
-
-
pipeline = append(pipeline, wf)
+
pipeline = append(pipeline, workflow.RawWorkflow{
+
Name: e.Name,
+
Contents: contents,
+
})
}
trigger := tangled.Pipeline_PushTriggerData{
···
},
}
-
cp := compiler.Compile(pipeline)
+
cp := compiler.Compile(compiler.Parse(pipeline))
eventJson, err := json.Marshal(cp)
if err != nil {
return err
}
if pushOptions.verboseCi {
-
hasDiagnostics := false
-
if len(pipelineParseErrors) > 0 {
-
hasDiagnostics = true
-
*clientMsgs = append(*clientMsgs, "error: failed to parse workflow(s):")
-
for _, error := range pipelineParseErrors {
-
*clientMsgs = append(*clientMsgs, error)
-
}
-
}
-
if len(compiler.Diagnostics.Errors) > 0 {
-
hasDiagnostics = true
-
*clientMsgs = append(*clientMsgs, "error(s) on pipeline:")
-
for _, error := range compiler.Diagnostics.Errors {
-
*clientMsgs = append(*clientMsgs, fmt.Sprintf("- %s:", error))
-
}
+
if compiler.Diagnostics.IsEmpty() {
+
*clientMsgs = append(*clientMsgs, "success: pipeline compiled with no diagnostics")
}
-
if len(compiler.Diagnostics.Warnings) > 0 {
-
hasDiagnostics = true
-
*clientMsgs = append(*clientMsgs, "warning(s) on pipeline:")
-
for _, warning := range compiler.Diagnostics.Warnings {
-
*clientMsgs = append(*clientMsgs, fmt.Sprintf("- at %s: %s: %s", warning.Path, warning.Type, warning.Reason))
-
}
+
+
for _, e := range compiler.Diagnostics.Errors {
+
*clientMsgs = append(*clientMsgs, e.String())
}
-
if !hasDiagnostics {
-
*clientMsgs = append(*clientMsgs, "success: pipeline compiled with no diagnostics")
+
+
for _, w := range compiler.Diagnostics.Warnings {
+
*clientMsgs = append(*clientMsgs, w.String())
}
}
+45 -5
workflow/compile.go
···
"tangled.sh/tangled.sh/core/api/tangled"
)
+
type RawWorkflow struct {
+
Name string
+
Contents []byte
+
}
+
+
type RawPipeline = []RawWorkflow
+
type Compiler struct {
Trigger tangled.Pipeline_TriggerMetadata
Diagnostics Diagnostics
}
type Diagnostics struct {
-
Errors []error
+
Errors []Error
Warnings []Warning
}
+
func (d *Diagnostics) IsEmpty() bool {
+
return len(d.Errors) == 0 && len(d.Warnings) == 0
+
}
+
func (d *Diagnostics) Combine(o Diagnostics) {
d.Errors = append(d.Errors, o.Errors...)
d.Warnings = append(d.Warnings, o.Warnings...)
···
d.Warnings = append(d.Warnings, Warning{path, kind, reason})
}
-
func (d *Diagnostics) AddError(err error) {
-
d.Errors = append(d.Errors, err)
+
func (d *Diagnostics) AddError(path string, err error) {
+
d.Errors = append(d.Errors, Error{path, err})
}
func (d Diagnostics) IsErr() bool {
return len(d.Errors) != 0
}
+
type Error struct {
+
Path string
+
Error error
+
}
+
+
func (e Error) String() string {
+
return fmt.Sprintf("error: %s: %s", e.Path, e.Error.Error())
+
}
+
type Warning struct {
Path string
Type WarningKind
Reason string
}
+
func (w Warning) String() string {
+
return fmt.Sprintf("warning: %s: %s: %s", w.Path, w.Type, w.Reason)
+
}
+
type WarningKind string
var (
···
InvalidConfiguration WarningKind = "invalid configuration"
)
+
func (compiler *Compiler) Parse(p RawPipeline) Pipeline {
+
var pp Pipeline
+
+
for _, w := range p {
+
wf, err := FromFile(w.Name, w.Contents)
+
if err != nil {
+
compiler.Diagnostics.AddError(w.Name, err)
+
continue
+
}
+
+
pp = append(pp, wf)
+
}
+
+
return pp
+
}
+
// convert a repositories' workflow files into a fully compiled pipeline that runners accept
func (compiler *Compiler) Compile(p Pipeline) tangled.Pipeline {
cp := tangled.Pipeline{
TriggerMetadata: &compiler.Trigger,
}
-
for _, w := range p {
-
cw := compiler.compileWorkflow(w)
+
for _, wf := range p {
+
cw := compiler.compileWorkflow(wf)
// empty workflows are not added to the pipeline
if len(cw.Steps) == 0 {