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