This repository has no description
0

Configure Feed

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

core / appview / pulls / actions.go
4.8 kB 160 lines
1package pulls 2 3import ( 4 "context" 5 "fmt" 6 "net/http" 7 "strconv" 8 9 "tangled.org/core/api/tangled" 10 "tangled.org/core/appview/db" 11 "tangled.org/core/appview/models" 12 "tangled.org/core/appview/pages" 13 "tangled.org/core/xrpc/xrpcclient" 14 15 "github.com/bluesky-social/indigo/atproto/syntax" 16 "github.com/go-chi/chi/v5" 17) 18 19// htmx fragment 20func (s *Pulls) PullActions(w http.ResponseWriter, r *http.Request) { 21 l := s.logger.With("handler", "PullActions") 22 23 switch r.Method { 24 case http.MethodGet: 25 user := s.oauth.GetMultiAccountUser(r) 26 if user != nil { 27 l = l.With("user", user.Did) 28 } 29 30 f, err := s.repoResolver.Resolve(r) 31 if err != nil { 32 l.Error("failed to get repo and knot", "err", err) 33 return 34 } 35 36 pull, ok := r.Context().Value("pull").(*models.Pull) 37 if !ok { 38 l.Error("failed to get pull") 39 s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.") 40 return 41 } 42 l = l.With("pull_id", pull.PullId, "pull_owner", pull.OwnerDid) 43 44 versionNumber, err := strconv.Atoi(chi.URLParam(r, "version")) 45 if err != nil { 46 versionNumber = pull.LatestVersionNumber() 47 } 48 if versionNumber >= len(pull.Versions) { 49 http.Error(w, "bad round id", http.StatusBadRequest) 50 l.Error("failed to parse round id", "err", err, "round_number", versionNumber) 51 return 52 } 53 54 isLastVersion := versionNumber == pull.LatestVersionNumber() 55 56 var workflowsChanged bool 57 var changedWorkflows []string 58 hasPipeline := false 59 if isLastVersion && f.Spindle != "" { 60 pipelines := fetchPipelines(r.Context(), l, f, []string{pull.LatestSha()}) 61 _, hasPipeline = pipelines[pull.LatestSha()] 62 63 if pull.IsForkBased() && !hasPipeline { 64 latest := pull.LatestVersion() 65 changedWorkflows, err = func(ctx context.Context) ([]string, error) { 66 base, err := s.resolveRev(ctx, pull.RepoDid, pull.TargetBranch) 67 if err != nil { 68 return nil, fmt.Errorf("failed to resolve target branch: %w", err) 69 } 70 return s.changedWorkflowFiles(ctx, pull.RepoDid, base, pull.SourceRepo, latest.Head) 71 }(r.Context()) 72 if err != nil { 73 l.Error("failed to inspect latest round's patch for workflow changes", "err", err) 74 } 75 workflowsChanged = len(changedWorkflows) > 0 76 } 77 } 78 79 // only the last round's buttons and banners use merge/resubmit checks 80 var mergeCheckParams pages.MergeCheckParams 81 var resubmitResult = pages.Unknown 82 if isLastVersion { 83 mergeCheckParams = s.composeMergeCheck(r.Context(), f, pull.TargetBranch, pull.SourceRepo, pull.LatestVersion().Head) 84 if user != nil && syntax.DID(user.Did) == pull.OwnerDid { 85 resubmitResult = s.resubmitCheck(r.Context(), pull) 86 } 87 } 88 89 s.pages.PullActionsFragment(w, pages.PullActionsParams{ 90 BaseParams: pages.BaseParamsFromContext(r.Context()), 91 RepoInfo: s.repoResolver.GetRepoInfo(r, user), 92 Pull: pull, 93 RoundNumber: versionNumber, 94 MergeCheck: mergeCheckParams, 95 ResubmitCheck: resubmitResult, 96 BranchDeleteStatus: s.branchDeleteStatus(r, f, pull), 97 98 WorkflowsChanged: workflowsChanged, 99 ChangedWorkflowFiles: changedWorkflows, 100 HasPipeline: hasPipeline, 101 }) 102 return 103 } 104} 105 106func (s *Pulls) branchDeleteStatus(r *http.Request, repo *models.Repo, pull *models.Pull) *pages.BranchDeleteStatus { 107 if pull.State != models.PullMerged || pull.SourceBranch == nil { 108 return nil 109 } 110 111 user := s.oauth.GetMultiAccountUser(r) 112 if user == nil { 113 return nil 114 } 115 116 branch := *pull.SourceBranch 117 118 if syntax.DID(repo.RepoDid) != pull.SourceRepo { 119 var err error 120 repo, err = db.GetRepoByDid(s.db, pull.SourceRepo.String()) 121 if err != nil { 122 return nil 123 } 124 } 125 126 // user can only delete branch if they are a collaborator in the repo that the branch belongs to 127 if !s.acl.HasRepoPermission(r.Context(), repo, user.Did, "repo:push") { 128 return nil 129 } 130 131 xrpcc := s.knotMirrorXRPC 132 resp, err := tangled.GitTempGetBranch(r.Context(), xrpcc, branch, repo.RepoDid) 133 if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil { 134 s.logger.Error("failed to get branch", "xrpcerr", xrpcerr, "err", err) 135 return nil 136 } 137 138 return &pages.BranchDeleteStatus{ 139 Repo: repo, 140 Branch: resp.Name, 141 } 142} 143 144func (s *Pulls) resubmitCheck(ctx context.Context, pull *models.Pull) pages.ResubmitResult { 145 if pull.State == models.PullMerged || pull.State == models.PullAbandoned || pull.SourceBranch == nil { 146 return pages.Unknown 147 } 148 149 sourceSha, err := s.resolveRev(ctx, pull.SourceRepo, *pull.SourceBranch) 150 if err != nil { 151 s.logger.Error("failed to resolve source branch", "err", err, "pull_id", pull.PullId, "branch", *pull.SourceBranch) 152 return pages.Unknown 153 } 154 155 if pull.LatestVersion().Head != sourceSha { 156 return pages.ShouldResubmit 157 } 158 159 return pages.ShouldNotResubmit 160}