This repository has no description
1package pulls
2
3import (
4 "fmt"
5 "net/http"
6 "time"
7
8 "golang.org/x/sync/errgroup"
9 "tangled.org/core/api/tangled"
10 "tangled.org/core/appview/db"
11 "tangled.org/core/appview/knotcompat"
12 "tangled.org/core/appview/models"
13 "tangled.org/core/appview/oauth"
14 "tangled.org/core/appview/reporesolver"
15 "tangled.org/core/tid"
16
17 comatproto "github.com/bluesky-social/indigo/api/atproto"
18 "github.com/bluesky-social/indigo/atproto/syntax"
19)
20
21func (s *Pulls) handlePull(
22 w http.ResponseWriter,
23 r *http.Request,
24 userDid syntax.DID,
25 targetRepo *models.Repo,
26 targetBranch string,
27 sourceRepo *models.Repo,
28 sourceBranch string,
29 title,
30 body string,
31) {
32 l := s.logger.With("handler", "handlePull", "user", userDid)
33 ctx := r.Context()
34
35 client, err := s.oauth.AuthorizedClient(r)
36 if err != nil {
37 l.Error("failed to get authorized client", "err", err)
38 s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
39 return
40 }
41
42 // 1. fetch heads of source & target branches
43 var base, head string
44 {
45 xrpcc := s.knotMirrorXRPC
46 g, gctx := errgroup.WithContext(ctx)
47 g.Go(func() error {
48 // find merge-base between targetBranch & sourceBranch
49 out, err := tangled.GitTempGetMergeBase(gctx, xrpcc, targetBranch, sourceBranch, sourceRepo.RepoDid)
50 if err != nil {
51 return err
52 }
53 base = out.Commit
54 return nil
55 })
56 g.Go(func() error {
57 out, err := tangled.GitTempGetBranch(gctx, xrpcc, sourceBranch, sourceRepo.RepoDid)
58 if err != nil {
59 return err
60 }
61 head = out.Hash
62 return nil
63 })
64 if err := g.Wait(); err != nil {
65 l.Error("failed to fetch branch heads", "err", err)
66 s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
67 return
68 }
69 }
70
71 created := time.Now()
72 pull := &models.Pull{
73 ID: -1, // uninitialized
74 OwnerDid: userDid,
75 Rkey: syntax.RecordKey(tid.TID()),
76 Cid: "", // uninitialized
77 RepoDid: syntax.DID(targetRepo.RepoDid),
78 PullId: 0, // uninitialized
79
80 Title: title,
81 Body: body,
82 TargetBranch: targetBranch,
83 SourceRepo: syntax.DID(sourceRepo.RepoDid),
84 SourceBranch: &sourceBranch,
85 Versions: []models.PullVersion{
86 {
87 ID: 0,
88 Base: base,
89 Head: head,
90 Created: created,
91 },
92 },
93 Created: created,
94 State: models.PullOpen,
95
96 Repo: targetRepo,
97 }
98
99 // 2. call git.keepCommit
100 {
101 client, err := s.oauth.ServiceClient(
102 r,
103 oauth.WithService(sourceRepo.Knot),
104 oauth.WithLxm(tangled.GitKeepCommitNSID),
105 oauth.WithDev(s.config.Core.Dev),
106 )
107 if err != nil {
108 l.Error("failed to comment to knot", "err", err)
109 s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
110 return
111 }
112
113 _, err = tangled.GitKeepCommit(ctx, client, &tangled.GitKeepCommit_Input{
114 Repo: sourceRepo.RepoDid,
115 Record: pull.AtUri().String(),
116 Source: &tangled.GitKeepCommit_Input_Source{
117 GitKeepCommit_Commit: &tangled.GitKeepCommit_Commit{
118 Repo: sourceRepo.RepoDid,
119 Oid: head,
120 },
121 },
122 })
123 if err != nil {
124 l.Error("failed to keep commit", "err", err)
125 s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
126 return
127 }
128 }
129
130 // 3. create PR record
131 record := pull.AsRecord()
132 out, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
133 Collection: tangled.RepoPullNSID,
134 Repo: userDid.String(),
135 Rkey: pull.Rkey.String(),
136 Record: knotcompat.Pull(&record),
137 })
138 pull.Cid = syntax.CID(out.Cid)
139
140 tx, err := s.db.BeginTx(r.Context(), nil)
141 if err != nil {
142 l.Error("failed to start tx", "err", err)
143 s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
144 return
145 }
146 defer tx.Rollback()
147
148 var references []syntax.ATURI
149 if pull.Body != "" {
150 _, references = s.mentionsResolver.Resolve(ctx, pull.Body)
151 }
152
153 if err := db.PutPull(r.Context(), tx, pull, references); err != nil {
154 l.Error("failed to create pull request in database", "err", err)
155 s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
156 return
157 }
158
159 if err = tx.Commit(); err != nil {
160 l.Error("failed to commit transaction for pull request", "err", err)
161 s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
162 return
163 }
164
165 s.notifier.NewPull(r.Context(), pull)
166
167 s.applyCreationLabels(r.Context(), client, userDid, pull, r.Form, targetRepo)
168
169 ownerSlashRepo := reporesolver.GetBaseRepoPath(r, targetRepo)
170 s.pages.HxRedirect(w, fmt.Sprintf("/%s/pulls/%d", ownerSlashRepo, pull.PullId))
171}