cloudflare-native port of the tangled knot server
1package types
2
3import (
4 "fmt"
5 "strings"
6 "time"
7
8 "tangled.org/core/api/tangled"
9)
10
11type StatusKind string
12
13func (s StatusKind) String() string {
14 return string(s)
15}
16
17func (s StatusKind) IsFinish() bool {
18 switch string(s) {
19 case "failed", "timeout", "cancelled", "success":
20 return true
21 }
22 return false
23}
24
25func (s StatusKind) IsStart() bool {
26 switch string(s) {
27 case "pending", "running":
28 return true
29 }
30 return false
31}
32
33type WorkflowStatus struct {
34 *tangled.CiPipeline_Workflow
35 PipelineCreatedAt *string
36}
37
38func (w WorkflowStatus) Latest() WorkflowStatus {
39 return w
40}
41
42func (w WorkflowStatus) Error() string {
43 if w.CiPipeline_Workflow == nil || w.CiPipeline_Workflow.Error == nil {
44 return ""
45 }
46 return *w.CiPipeline_Workflow.Error
47}
48
49func (w WorkflowStatus) Status() StatusKind {
50 if w.CiPipeline_Workflow == nil {
51 return ""
52 }
53 return StatusKind(w.CiPipeline_Workflow.Status)
54}
55
56func (w WorkflowStatus) TimeTaken() time.Duration {
57 if w.CiPipeline_Workflow == nil || w.StartedAt == nil || w.FinishedAt == nil || *w.StartedAt == "" || *w.FinishedAt == "" {
58 return 0
59 }
60 t1, err1 := time.Parse(time.RFC3339, *w.StartedAt)
61 t2, err2 := time.Parse(time.RFC3339, *w.FinishedAt)
62 if err1 == nil && err2 == nil && t2.After(t1) {
63 return t2.Sub(t1)
64 }
65 return 0
66}
67
68func (w WorkflowStatus) Created() time.Time {
69 var timeStr string
70 if w.CiPipeline_Workflow != nil && w.StartedAt != nil && *w.StartedAt != "" {
71 timeStr = *w.StartedAt
72 } else if w.CiPipeline_Workflow != nil && w.FinishedAt != nil && *w.FinishedAt != "" {
73 timeStr = *w.FinishedAt
74 } else if w.PipelineCreatedAt != nil && *w.PipelineCreatedAt != "" {
75 timeStr = *w.PipelineCreatedAt
76 } else {
77 return time.Time{}
78 }
79
80 t, err := time.Parse(time.RFC3339, timeStr)
81 if err != nil {
82 return time.Time{}
83 }
84 return t
85}
86
87type Trigger struct {
88 *tangled.CiPipeline_Trigger
89}
90
91func (t Trigger) IsPush() bool {
92 return t.CiPipeline_Trigger != nil && t.CiPipeline_Trigger.CiTrigger_Push != nil
93}
94
95func (t Trigger) IsPullRequest() bool {
96 return t.CiPipeline_Trigger != nil && t.CiPipeline_Trigger.CiTrigger_PullRequest != nil
97}
98
99func (t Trigger) IsManual() bool {
100 return t.CiPipeline_Trigger != nil && t.CiPipeline_Trigger.CiTrigger_Manual != nil
101}
102
103func (t Trigger) TargetRef() string {
104 if t.CiPipeline_Trigger == nil {
105 return ""
106 }
107 if t.CiPipeline_Trigger.CiTrigger_Push != nil {
108 ref := t.CiPipeline_Trigger.CiTrigger_Push.Ref
109 if strings.HasPrefix(ref, "refs/heads/") {
110 return strings.TrimPrefix(ref, "refs/heads/")
111 }
112 if strings.HasPrefix(ref, "refs/tags/") {
113 return strings.TrimPrefix(ref, "refs/tags/")
114 }
115 return ref
116 }
117 if t.CiPipeline_Trigger.CiTrigger_PullRequest != nil {
118 return t.CiPipeline_Trigger.CiTrigger_PullRequest.TargetBranch
119 }
120 return ""
121}
122
123func (t Trigger) PRSourceBranch() string {
124 if t.CiPipeline_Trigger == nil || t.CiPipeline_Trigger.CiTrigger_PullRequest == nil {
125 return ""
126 }
127 sb := t.CiPipeline_Trigger.CiTrigger_PullRequest.SourceBranch
128 if sb == nil {
129 return ""
130 }
131 return *sb
132}
133
134type Pipeline struct {
135 *tangled.CiPipeline
136}
137
138func (p Pipeline) Valid() bool {
139 return p.CiPipeline != nil
140}
141
142func (p Pipeline) Id() string {
143 if p.CiPipeline == nil {
144 return ""
145 }
146 return p.CiPipeline.Id
147}
148
149func (p Pipeline) Statuses() map[string]WorkflowStatus {
150 m := make(map[string]WorkflowStatus)
151 if p.CiPipeline != nil {
152 for _, w := range p.CiPipeline.Workflows {
153 m[w.Name] = WorkflowStatus{
154 CiPipeline_Workflow: w,
155 PipelineCreatedAt: p.CreatedAt,
156 }
157 }
158 }
159 return m
160}
161
162func (p Pipeline) Counts() map[string]int {
163 m := make(map[string]int)
164 if p.CiPipeline != nil {
165 for _, w := range p.CiPipeline.Workflows {
166 m[w.Status]++
167 }
168 }
169 return m
170}
171
172// InProgress reports whether any workflow in the pipeline is still pending or
173// running (i.e. the pipeline has not fully settled).
174func (p Pipeline) InProgress() bool {
175 counts := p.Counts()
176 return counts["pending"] > 0 || counts["running"] > 0
177}
178
179func (p Pipeline) ShortStatusSummary() string {
180 if p.CiPipeline == nil {
181 return ""
182 }
183 counts := p.Counts()
184 total := len(p.CiPipeline.Workflows)
185 successes := counts["success"]
186 return fmt.Sprintf("%d/%d", successes, total)
187}
188
189func (p Pipeline) LongStatusSummary() string {
190 if p.CiPipeline == nil {
191 return ""
192 }
193 counts := p.Counts()
194 total := len(p.CiPipeline.Workflows)
195 var parts []string
196 states := []string{"success", "failed", "timeout", "cancelled", "running", "pending"}
197 for _, state := range states {
198 if c, ok := counts[state]; ok {
199 parts = append(parts, fmt.Sprintf("%d/%d %s", c, total, state))
200 }
201 }
202 return strings.Join(parts, ", ")
203}
204
205func (p Pipeline) TimeTaken() time.Duration {
206 if p.CiPipeline == nil {
207 return 0
208 }
209 var s time.Duration
210 for _, w := range p.CiPipeline.Workflows {
211 s += WorkflowStatus{CiPipeline_Workflow: w}.TimeTaken()
212 }
213 return s
214}
215
216func (p Pipeline) Created() time.Time {
217 if p.CiPipeline == nil || p.CreatedAt == nil || *p.CreatedAt == "" {
218 return time.Time{}
219 }
220 t, err := time.Parse(time.RFC3339, *p.CreatedAt)
221 if err != nil {
222 return time.Time{}
223 }
224 return t
225}
226
227func (p Pipeline) Trigger() Trigger {
228 if p.CiPipeline == nil {
229 return Trigger{nil}
230 }
231 return Trigger{p.CiPipeline.Trigger}
232}
233
234func (p Pipeline) IsResponding() bool {
235 return p.CiPipeline != nil && len(p.CiPipeline.Workflows) > 0
236}
237
238func (p Pipeline) Sha() string {
239 if p.CiPipeline == nil {
240 return ""
241 }
242 return p.CiPipeline.Commit
243}
244
245func (p Pipeline) Workflows() []string {
246 var ws []string
247 if p.CiPipeline != nil {
248 for _, w := range p.CiPipeline.Workflows {
249 ws = append(ws, w.Name)
250 }
251 }
252 return ws
253}