Monorepo for Tangled tangled.org
2

Configure Feed

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

1package pulls 2 3import ( 4 "fmt" 5 "net/http" 6 "time" 7 8 "tangled.org/core/api/tangled" 9 "tangled.org/core/appview/db" 10 "tangled.org/core/appview/models" 11 "tangled.org/core/appview/oauth" 12 "tangled.org/core/appview/reporesolver" 13 "tangled.org/core/appview/xrpcclient" 14 "tangled.org/core/orm" 15 16 "github.com/bluesky-social/indigo/atproto/syntax" 17) 18 19func (s *Pulls) MergePull(w http.ResponseWriter, r *http.Request) { 20 l := s.logger.With("handler", "MergePull") 21 22 user := s.oauth.GetMultiAccountUser(r) 23 if user != nil { 24 l = l.With("user", user.Did) 25 } 26 27 f, err := s.repoResolver.Resolve(r) 28 if err != nil { 29 l.Error("failed to resolve repo", "err", err) 30 s.pages.Notice(w, "pull-merge-error", "Failed to merge pull request. Try again later.") 31 return 32 } 33 l = l.With("repo_at", f.RepoAt().String()) 34 35 pull, ok := r.Context().Value("pull").(*models.Pull) 36 if !ok { 37 l.Error("failed to get pull") 38 s.pages.Notice(w, "pull-merge-error", "Failed to merge patch. Try again later.") 39 return 40 } 41 l = l.With("pull_id", pull.PullId, "target_branch", pull.TargetBranch) 42 43 stack, ok := r.Context().Value("stack").(models.Stack) 44 if !ok { 45 l.Error("failed to get stack") 46 s.pages.Notice(w, "pull-merge-error", "Failed to merge patch. Try again later.") 47 return 48 } 49 50 // combine patches of substack 51 subStack := stack.Below(pull) 52 // collect the portion of the stack that is mergeable 53 pullsToMerge := subStack.Mergeable() 54 l = l.With("pulls_to_merge", len(pullsToMerge)) 55 56 patch := pullsToMerge.CombinedPatch() 57 58 ident, err := s.idResolver.ResolveIdent(r.Context(), pull.OwnerDid) 59 if err != nil { 60 l.Error("failed to resolve identity", "err", err, "owner_did", pull.OwnerDid) 61 w.WriteHeader(http.StatusNotFound) 62 return 63 } 64 65 email, err := db.GetPrimaryEmail(s.db, pull.OwnerDid) 66 if err != nil { 67 l.Warn("failed to get primary email", "err", err, "owner_did", pull.OwnerDid) 68 } 69 70 authorName := ident.Handle.String() 71 mergeInput := &tangled.RepoMerge_Input{ 72 Did: f.Did, 73 Name: f.Name, 74 Branch: pull.TargetBranch, 75 Patch: patch, 76 CommitMessage: &pull.Title, 77 AuthorName: &authorName, 78 } 79 80 if pull.Body != "" { 81 mergeInput.CommitBody = &pull.Body 82 } 83 84 if email.Address != "" { 85 mergeInput.AuthorEmail = &email.Address 86 } 87 88 client, err := s.oauth.ServiceClient( 89 r, 90 oauth.WithService(f.Knot), 91 oauth.WithLxm(tangled.RepoMergeNSID), 92 oauth.WithDev(s.config.Core.Dev), 93 oauth.WithTimeout(time.Second*20), // merge is quite slow on large repos, like witchsky 94 ) 95 if err != nil { 96 l.Error("failed to connect to knot server", "err", err, "knot", f.Knot) 97 s.pages.Notice(w, "pull-merge-error", "Failed to merge pull request. Try again later.") 98 return 99 } 100 101 err = tangled.RepoMerge(r.Context(), client, mergeInput) 102 if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil { 103 s.logger.Error("failed to merge", "xrpcerr", xrpcerr, "err", err) 104 s.pages.Notice(w, "pull-merge-error", xrpcerr.Error()) 105 return 106 } 107 108 tx, err := s.db.Begin() 109 if err != nil { 110 l.Error("failed to start transaction", "err", err) 111 s.pages.Notice(w, "pull-merge-error", "Failed to merge pull request. Try again later.") 112 return 113 } 114 defer tx.Rollback() 115 116 var atUris []syntax.ATURI 117 for _, p := range pullsToMerge { 118 atUris = append(atUris, p.AtUri()) 119 p.State = models.PullMerged 120 } 121 err = db.MergePulls(tx, orm.FilterEq("repo_did", string(f.RepoDid)), orm.FilterIn("at_uri", atUris)) 122 if err != nil { 123 l.Error("failed to update pull request status in database", "err", err) 124 s.pages.Notice(w, "pull-merge-error", "Failed to merge pull request. Try again later.") 125 return 126 } 127 128 err = tx.Commit() 129 if err != nil { 130 // TODO: this is unsound, we should also revert the merge from the knotserver here 131 l.Error("failed to commit merge transaction", "err", err) 132 s.pages.Notice(w, "pull-merge-error", "Failed to merge pull request. Try again later.") 133 return 134 } 135 136 // notify about the pull merge 137 for _, p := range pullsToMerge { 138 s.notifier.NewPullState(r.Context(), syntax.DID(user.Did), p) 139 } 140 141 ownerSlashRepo := reporesolver.GetBaseRepoPath(r, f) 142 s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d", ownerSlashRepo, pull.PullId)) 143}