This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

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