diff --git a/appview/db/pipeline.go b/appview/db/pipeline.go --- a/appview/db/pipeline.go +++ b/appview/db/pipeline.go @@ -6,133 +6,11 @@ "slices" "strings" "time" - "github.com/bluesky-social/indigo/atproto/syntax" - "github.com/go-git/go-git/v5/plumbing" - spindle "tangled.org/core/spindle/models" - "tangled.org/core/workflow" + "tangled.org/core/appview/models" ) -type Pipeline struct { - Id int - Rkey string - Knot string - RepoOwner syntax.DID - RepoName string - TriggerId int - Sha string - Created time.Time - - // populate when querying for reverse mappings - Trigger *Trigger - Statuses map[string]WorkflowStatus -} - -type WorkflowStatus struct { - Data []PipelineStatus -} - -func (w WorkflowStatus) Latest() PipelineStatus { - return w.Data[len(w.Data)-1] -} - -// time taken by this workflow to reach an "end state" -func (w WorkflowStatus) TimeTaken() time.Duration { - var start, end *time.Time - for _, s := range w.Data { - if s.Status.IsStart() { - start = &s.Created - } - if s.Status.IsFinish() { - end = &s.Created - } - } - - if start != nil && end != nil && end.After(*start) { - return end.Sub(*start) - } - - return 0 -} - -func (p Pipeline) Counts() map[string]int { - m := make(map[string]int) - for _, w := range p.Statuses { - m[w.Latest().Status.String()] += 1 - } - return m -} - -func (p Pipeline) TimeTaken() time.Duration { - var s time.Duration - for _, w := range p.Statuses { - s += w.TimeTaken() - } - return s -} - -func (p Pipeline) Workflows() []string { - var ws []string - for v := range p.Statuses { - ws = append(ws, v) - } - slices.Sort(ws) - return ws -} - -// if we know that a spindle has picked up this pipeline, then it is Responding -func (p Pipeline) IsResponding() bool { - return len(p.Statuses) != 0 -} - -type Trigger struct { - Id int - Kind workflow.TriggerKind - - // push trigger fields - PushRef *string - PushNewSha *string - PushOldSha *string - - // pull request trigger fields - PRSourceBranch *string - PRTargetBranch *string - PRSourceSha *string - PRAction *string -} - -func (t *Trigger) IsPush() bool { - return t != nil && t.Kind == workflow.TriggerKindPush -} - -func (t *Trigger) IsPullRequest() bool { - return t != nil && t.Kind == workflow.TriggerKindPullRequest -} - -func (t *Trigger) TargetRef() string { - if t.IsPush() { - return plumbing.ReferenceName(*t.PushRef).Short() - } else if t.IsPullRequest() { - return *t.PRTargetBranch - } - - return "" -} - -type PipelineStatus struct { - ID int - Spindle string - Rkey string - PipelineKnot string - PipelineRkey string - Created time.Time - Workflow string - Status spindle.StatusKind - Error *string - ExitCode int -} - -func GetPipelines(e Execer, filters ...filter) ([]Pipeline, error) { - var pipelines []Pipeline +func GetPipelines(e Execer, filters ...filter) ([]models.Pipeline, error) { + var pipelines []models.Pipeline var conditions []string var args []any @@ -156,7 +34,7 @@ } defer rows.Close() for rows.Next() { - var pipeline Pipeline + var pipeline models.Pipeline var createdAt string err = rows.Scan( &pipeline.Id, @@ -185,7 +63,7 @@ return pipelines, nil } -func AddPipeline(e Execer, pipeline Pipeline) error { +func AddPipeline(e Execer, pipeline models.Pipeline) error { args := []any{ pipeline.Rkey, pipeline.Knot, @@ -216,7 +94,7 @@ return err } -func AddTrigger(e Execer, trigger Trigger) (int64, error) { +func AddTrigger(e Execer, trigger models.Trigger) (int64, error) { args := []any{ trigger.Kind, trigger.PushRef, @@ -252,7 +130,7 @@ return res.LastInsertId() } -func AddPipelineStatus(e Execer, status PipelineStatus) error { +func AddPipelineStatus(e Execer, status models.PipelineStatus) error { args := []any{ status.Spindle, status.Rkey, @@ -290,7 +168,7 @@ } // this is a mega query, but the most useful one: // get N pipelines, for each one get the latest status of its N workflows -func GetPipelineStatuses(e Execer, filters ...filter) ([]Pipeline, error) { +func GetPipelineStatuses(e Execer, filters ...filter) ([]models.Pipeline, error) { var conditions []string var args []any for _, filter := range filters { @@ -335,10 +213,10 @@ return nil, err } defer rows.Close() - pipelines := make(map[string]Pipeline) + pipelines := make(map[string]models.Pipeline) for rows.Next() { - var p Pipeline - var t Trigger + var p models.Pipeline + var t models.Trigger var created string err := rows.Scan( @@ -370,7 +248,7 @@ } t.Id = p.TriggerId p.Trigger = &t - p.Statuses = make(map[string]WorkflowStatus) + p.Statuses = make(map[string]models.WorkflowStatus) k := fmt.Sprintf("%s/%s", p.Knot, p.Rkey) pipelines[k] = p @@ -409,7 +287,7 @@ } defer rows.Close() for rows.Next() { - var ps PipelineStatus + var ps models.PipelineStatus var created string err := rows.Scan( @@ -442,7 +320,7 @@ continue } statuses, _ := pipeline.Statuses[ps.Workflow] if !ok { - pipeline.Statuses[ps.Workflow] = WorkflowStatus{} + pipeline.Statuses[ps.Workflow] = models.WorkflowStatus{} } // append @@ -453,10 +331,10 @@ pipeline.Statuses[ps.Workflow] = statuses pipelines[key] = pipeline } - var all []Pipeline + var all []models.Pipeline for _, p := range pipelines { for _, s := range p.Statuses { - slices.SortFunc(s.Data, func(a, b PipelineStatus) int { + slices.SortFunc(s.Data, func(a, b models.PipelineStatus) int { if a.Created.After(b.Created) { return 1 } @@ -476,7 +354,7 @@ all = append(all, p) } // sort pipelines by date - slices.SortFunc(all, func(a, b Pipeline) int { + slices.SortFunc(all, func(a, b models.Pipeline) int { if a.Created.After(b.Created) { return -1 } diff --git a/appview/models/pipeline.go b/appview/models/pipeline.go new file mode 100644 --- /dev/null +++ b/appview/models/pipeline.go @@ -0,0 +1,130 @@ +package models + +import ( + "slices" + "time" + + "github.com/bluesky-social/indigo/atproto/syntax" + "github.com/go-git/go-git/v5/plumbing" + spindle "tangled.org/core/spindle/models" + "tangled.org/core/workflow" +) + +type Pipeline struct { + Id int + Rkey string + Knot string + RepoOwner syntax.DID + RepoName string + TriggerId int + Sha string + Created time.Time + + // populate when querying for reverse mappings + Trigger *Trigger + Statuses map[string]WorkflowStatus +} + +type WorkflowStatus struct { + Data []PipelineStatus +} + +func (w WorkflowStatus) Latest() PipelineStatus { + return w.Data[len(w.Data)-1] +} + +// time taken by this workflow to reach an "end state" +func (w WorkflowStatus) TimeTaken() time.Duration { + var start, end *time.Time + for _, s := range w.Data { + if s.Status.IsStart() { + start = &s.Created + } + if s.Status.IsFinish() { + end = &s.Created + } + } + + if start != nil && end != nil && end.After(*start) { + return end.Sub(*start) + } + + return 0 +} + +func (p Pipeline) Counts() map[string]int { + m := make(map[string]int) + for _, w := range p.Statuses { + m[w.Latest().Status.String()] += 1 + } + return m +} + +func (p Pipeline) TimeTaken() time.Duration { + var s time.Duration + for _, w := range p.Statuses { + s += w.TimeTaken() + } + return s +} + +func (p Pipeline) Workflows() []string { + var ws []string + for v := range p.Statuses { + ws = append(ws, v) + } + slices.Sort(ws) + return ws +} + +// if we know that a spindle has picked up this pipeline, then it is Responding +func (p Pipeline) IsResponding() bool { + return len(p.Statuses) != 0 +} + +type Trigger struct { + Id int + Kind workflow.TriggerKind + + // push trigger fields + PushRef *string + PushNewSha *string + PushOldSha *string + + // pull request trigger fields + PRSourceBranch *string + PRTargetBranch *string + PRSourceSha *string + PRAction *string +} + +func (t *Trigger) IsPush() bool { + return t != nil && t.Kind == workflow.TriggerKindPush +} + +func (t *Trigger) IsPullRequest() bool { + return t != nil && t.Kind == workflow.TriggerKindPullRequest +} + +func (t *Trigger) TargetRef() string { + if t.IsPush() { + return plumbing.ReferenceName(*t.PushRef).Short() + } else if t.IsPullRequest() { + return *t.PRTargetBranch + } + + return "" +} + +type PipelineStatus struct { + ID int + Spindle string + Rkey string + PipelineKnot string + PipelineRkey string + Created time.Time + Workflow string + Status spindle.StatusKind + Error *string + ExitCode int +} diff --git a/appview/pages/pages.go b/appview/pages/pages.go --- a/appview/pages/pages.go +++ b/appview/pages/pages.go @@ -588,7 +588,7 @@ Raw bool EmailToDidOrHandle map[string]string VerifiedCommits commitverify.VerifiedCommits Languages []types.RepoLanguageDetails - Pipelines map[string]db.Pipeline + Pipelines map[string]models.Pipeline NeedsKnotUpgrade bool types.RepoIndexResponse } @@ -631,7 +631,7 @@ types.RepoLogResponse Active string EmailToDidOrHandle map[string]string VerifiedCommits commitverify.VerifiedCommits - Pipelines map[string]db.Pipeline + Pipelines map[string]models.Pipeline } func (p *Pages) RepoLog(w io.Writer, params RepoLogParams) error { @@ -644,7 +644,7 @@ LoggedInUser *oauth.User RepoInfo repoinfo.RepoInfo Active string EmailToDidOrHandle map[string]string - Pipeline *db.Pipeline + Pipeline *models.Pipeline DiffOpts types.DiffOpts // singular because it's always going to be just one @@ -1023,7 +1023,7 @@ Pulls []*models.Pull Active string FilteringBy models.PullState Stacks map[string]models.Stack - Pipelines map[string]db.Pipeline + Pipelines map[string]models.Pipeline } func (p *Pages) RepoPulls(w io.Writer, params RepoPullsParams) error { @@ -1058,7 +1058,7 @@ Stack models.Stack AbandonedPulls []*models.Pull MergeCheck types.MergeCheckResponse ResubmitCheck ResubmitResult - Pipelines map[string]db.Pipeline + Pipelines map[string]models.Pipeline OrderedReactionKinds []db.ReactionKind Reactions map[db.ReactionKind]int @@ -1260,7 +1260,7 @@ type PipelinesParams struct { LoggedInUser *oauth.User RepoInfo repoinfo.RepoInfo - Pipelines []db.Pipeline + Pipelines []models.Pipeline Active string } @@ -1292,7 +1292,7 @@ type WorkflowParams struct { LoggedInUser *oauth.User RepoInfo repoinfo.RepoInfo - Pipeline db.Pipeline + Pipeline models.Pipeline Workflow string LogUrl string Active string diff --git a/appview/pulls/pulls.go b/appview/pulls/pulls.go --- a/appview/pulls/pulls.go +++ b/appview/pulls/pulls.go @@ -160,7 +160,7 @@ } repoInfo := f.RepoInfo(user) - m := make(map[string]db.Pipeline) + m := make(map[string]models.Pipeline) var shas []string for _, s := range pull.Submissions { @@ -552,7 +552,7 @@ if err != nil { log.Printf("failed to fetch pipeline statuses: %s", err) // non-fatal } - m := make(map[string]db.Pipeline) + m := make(map[string]models.Pipeline) for _, p := range ps { m[p.Sha] = p } diff --git a/appview/repo/repo.go b/appview/repo/repo.go --- a/appview/repo/repo.go +++ b/appview/repo/repo.go @@ -400,7 +400,7 @@ if err != nil { log.Println(err) // non-fatal } - var pipeline *db.Pipeline + var pipeline *models.Pipeline if p, ok := pipelines[result.Diff.Commit.This]; ok { pipeline = &p } diff --git a/appview/repo/repo_util.go b/appview/repo/repo_util.go --- a/appview/repo/repo_util.go +++ b/appview/repo/repo_util.go @@ -10,6 +10,7 @@ "sort" "strings" "tangled.org/core/appview/db" + "tangled.org/core/appview/models" "tangled.org/core/appview/pages/repoinfo" "tangled.org/core/types" @@ -143,8 +144,8 @@ func getPipelineStatuses( d *db.DB, repoInfo repoinfo.RepoInfo, shas []string, -) (map[string]db.Pipeline, error) { - m := make(map[string]db.Pipeline) +) (map[string]models.Pipeline, error) { + m := make(map[string]models.Pipeline) if len(shas) == 0 { return m, nil diff --git a/appview/state/knotstream.go b/appview/state/knotstream.go --- a/appview/state/knotstream.go +++ b/appview/state/knotstream.go @@ -208,7 +208,7 @@ return fmt.Errorf("repo does not have a spindle configured yet: nsid %s, rkey %s", msg.Nsid, msg.Rkey) } // trigger info - var trigger db.Trigger + var trigger models.Trigger var sha string trigger.Kind = workflow.TriggerKind(record.TriggerMetadata.Kind) switch trigger.Kind { @@ -235,7 +235,7 @@ if err != nil { return fmt.Errorf("failed to add trigger entry: %w", err) } - pipeline := db.Pipeline{ + pipeline := models.Pipeline{ Rkey: msg.Rkey, Knot: source.Key(), RepoOwner: syntax.DID(record.TriggerMetadata.Repo.Did), diff --git a/appview/state/spindlestream.go b/appview/state/spindlestream.go --- a/appview/state/spindlestream.go +++ b/appview/state/spindlestream.go @@ -13,6 +13,7 @@ "tangled.org/core/api/tangled" "tangled.org/core/appview/cache" "tangled.org/core/appview/config" "tangled.org/core/appview/db" + "tangled.org/core/appview/models" ec "tangled.org/core/eventconsumer" "tangled.org/core/eventconsumer/cursor" "tangled.org/core/log" @@ -89,7 +90,7 @@ if t, err := time.Parse(time.RFC3339, record.CreatedAt); err == nil && created.After(t) { created = t } - status := db.PipelineStatus{ + status := models.PipelineStatus{ Spindle: source.Key(), Rkey: msg.Rkey, PipelineKnot: strings.TrimPrefix(pipelineUri.Authority().String(), "did:web:"),