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