this repo has no description
1package models
2
3import (
4 "fmt"
5 "slices"
6 "strings"
7 "time"
8
9 "github.com/bluesky-social/indigo/atproto/syntax"
10 "github.com/go-git/go-git/v5/plumbing"
11 spindle "tangled.org/core/spindle/models"
12 "tangled.org/core/workflow"
13)
14
15type Pipeline struct {
16 Id int
17 Rkey string
18 Knot string
19 RepoOwner syntax.DID
20 RepoName string
21 TriggerId int
22 Sha string
23 Created time.Time
24
25 // populate when querying for reverse mappings
26 Trigger *Trigger
27 Statuses map[string]WorkflowStatus
28}
29
30type WorkflowStatus struct {
31 Data []PipelineStatus
32}
33
34func (w WorkflowStatus) Latest() PipelineStatus {
35 return w.Data[len(w.Data)-1]
36}
37
38// time taken by this workflow to reach an "end state"
39func (w WorkflowStatus) TimeTaken() time.Duration {
40 var start, end *time.Time
41 for _, s := range w.Data {
42 if s.Status.IsStart() {
43 start = &s.Created
44 }
45 if s.Status.IsFinish() {
46 end = &s.Created
47 }
48 }
49
50 if start != nil && end != nil && end.After(*start) {
51 return end.Sub(*start)
52 }
53
54 return 0
55}
56
57// produces short summary of successes:
58// - "0/4" when zero successes of 4 workflows
59// - "4/4" when all successes of 4 workflows
60// - "0/0" when no workflows run in this pipeline
61func (p Pipeline) ShortStatusSummary() string {
62 counts := make(map[spindle.StatusKind]int)
63 for _, w := range p.Statuses {
64 counts[w.Latest().Status] += 1
65 }
66
67 total := len(p.Statuses)
68 successes := counts[spindle.StatusKindSuccess]
69
70 return fmt.Sprintf("%d/%d", successes, total)
71}
72
73// produces a string of the form "3/4 success, 2/4 failed, 1/4 pending"
74func (p Pipeline) LongStatusSummary() string {
75 counts := make(map[spindle.StatusKind]int)
76 for _, w := range p.Statuses {
77 counts[w.Latest().Status] += 1
78 }
79
80 total := len(p.Statuses)
81
82 var result []string
83 // finish states first, followed by start states
84 states := append(spindle.FinishStates[:], spindle.StartStates[:]...)
85 for _, state := range states {
86 if count, ok := counts[state]; ok {
87 result = append(result, fmt.Sprintf("%d/%d %s", count, total, state.String()))
88 }
89 }
90
91 return strings.Join(result, ", ")
92}
93
94func (p Pipeline) Counts() map[string]int {
95 m := make(map[string]int)
96 for _, w := range p.Statuses {
97 m[w.Latest().Status.String()] += 1
98 }
99 return m
100}
101
102func (p Pipeline) TimeTaken() time.Duration {
103 var s time.Duration
104 for _, w := range p.Statuses {
105 s += w.TimeTaken()
106 }
107 return s
108}
109
110func (p Pipeline) Workflows() []string {
111 var ws []string
112 for v := range p.Statuses {
113 ws = append(ws, v)
114 }
115 slices.Sort(ws)
116 return ws
117}
118
119// if we know that a spindle has picked up this pipeline, then it is Responding
120func (p Pipeline) IsResponding() bool {
121 return len(p.Statuses) != 0
122}
123
124type Trigger struct {
125 Id int
126 Kind workflow.TriggerKind
127
128 // push trigger fields
129 PushRef *string
130 PushNewSha *string
131 PushOldSha *string
132
133 // pull request trigger fields
134 PRSourceBranch *string
135 PRTargetBranch *string
136 PRSourceSha *string
137 PRAction *string
138}
139
140func (t *Trigger) IsPush() bool {
141 return t != nil && t.Kind == workflow.TriggerKindPush
142}
143
144func (t *Trigger) IsPullRequest() bool {
145 return t != nil && t.Kind == workflow.TriggerKindPullRequest
146}
147
148func (t *Trigger) TargetRef() string {
149 if t.IsPush() {
150 return plumbing.ReferenceName(*t.PushRef).Short()
151 } else if t.IsPullRequest() {
152 return *t.PRTargetBranch
153 }
154
155 return ""
156}
157
158type PipelineStatus struct {
159 ID int
160 Spindle string
161 Rkey string
162 PipelineKnot string
163 PipelineRkey string
164 Created time.Time
165 Workflow string
166 Status spindle.StatusKind
167 Error *string
168 ExitCode int
169}