This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

core / appview / pulls / workflow_change.go
3.5 kB 114 lines
1package pulls 2 3import ( 4 "encoding/json" 5 "fmt" 6 "net/http" 7 "slices" 8 "time" 9 10 indigoxrpc "github.com/bluesky-social/indigo/xrpc" 11 12 "tangled.org/core/api/tangled" 13 "tangled.org/core/appview/db" 14 "tangled.org/core/appview/models" 15 "tangled.org/core/hostutil" 16 "tangled.org/core/types" 17) 18 19// spindle upgrades may change how a commit is resolved and hashed (even if very rare...) 20const ( 21 workflowCacheSize = 4096 22 workflowCacheTTL = time.Hour 23) 24 25func (s *Pulls) describeWorkflowAt(r *http.Request, f *models.Repo, sha, sourceRepo string) (*tangled.CiDescribeWorkflowDefinition_Output, error) { 26 key := fmt.Sprintf("%s|%s|%s|%s", f.Spindle, f.RepoDid, sha, sourceRepo) 27 if cached, ok := s.workflowDescCache.Get(key); ok { 28 return cached, nil 29 } 30 31 spindleUrl, err := hostutil.EnsureHttpScheme(f.Spindle) 32 if err != nil { 33 return nil, err 34 } 35 client := &indigoxrpc.Client{Host: spindleUrl} 36 out, err := tangled.CiDescribeWorkflowDefinition(r.Context(), client, f.RepoDid, sha, sourceRepo) 37 if err != nil { 38 return nil, err 39 } 40 41 s.workflowDescCache.Add(key, out) 42 return out, nil 43} 44 45// returns the merge-base of the pull's source and target branches. 46// 47// older rounds resolve it live on the fork's knot, which tracks the 48// target branch as refs/hidden/<source>/<target>. 49func (s *Pulls) mergeBase(r *http.Request, pull *models.Pull) (string, error) { 50 if mb := pull.LatestSubmission().MergeBase; mb != "" { 51 return mb, nil 52 } 53 54 sourceBranch := pull.PullSource.Branch 55 hiddenRef := fmt.Sprintf("hidden/%s/%s", sourceBranch, pull.TargetBranch) 56 57 key := fmt.Sprintf("%s|%s|%s", pull.PullSource.RepoDid, sourceBranch, pull.TargetBranch) 58 if cached, ok := s.mergeBaseCache.Get(key); ok { 59 return cached, nil 60 } 61 62 forkRepo, err := db.GetRepoByDid(s.db, pull.PullSource.RepoDid.String()) 63 if err != nil { 64 return "", fmt.Errorf("resolving fork repo: %w", err) 65 } 66 compareBytes, err := tangled.RepoCompare(r.Context(), s.knotClient(forkRepo.Knot), forkRepo.RepoIdentifier(), hiddenRef, sourceBranch) 67 if err != nil { 68 return "", fmt.Errorf("comparing %s...%s: %w", hiddenRef, sourceBranch, err) 69 } 70 var comparison types.RepoFormatPatchResponse 71 if err := json.Unmarshal(compareBytes, &comparison); err != nil { 72 return "", fmt.Errorf("decoding comparison: %w", err) 73 } 74 if comparison.MergeBase == "" { 75 return "", fmt.Errorf("no merge base between %s and %s", hiddenRef, sourceBranch) 76 } 77 78 s.mergeBaseCache.Add(key, comparison.MergeBase) 79 return comparison.MergeBase, nil 80} 81 82// compares the spindle's workflow-definition fingerprints at the 83// pull head and its merge-base with the target branch. 84func (s *Pulls) workflowChangeFromSpindle(r *http.Request, f *models.Repo, pull *models.Pull) (bool, []string, error) { 85 sourceRepo := pull.PullSource.RepoDid.String() 86 87 head, err := s.describeWorkflowAt(r, f, pull.LatestSha(), sourceRepo) 88 if err != nil { 89 return false, nil, err 90 } 91 // a definition not derived from the repo cannot change between commits 92 if !head.Derived { 93 return false, nil, nil 94 } 95 96 baseSha, err := s.mergeBase(r, pull) 97 if err != nil { 98 return false, nil, err 99 } 100 base, err := s.describeWorkflowAt(r, f, baseSha, sourceRepo) 101 if err != nil { 102 return false, nil, err 103 } 104 if !base.Derived { 105 return false, nil, nil 106 } 107 if head.Hash != nil && base.Hash != nil && *head.Hash == *base.Hash { 108 return false, nil, nil 109 } 110 111 names := slices.Concat(head.Workflows, base.Workflows) 112 slices.Sort(names) 113 return true, slices.Compact(names), nil 114}