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