This repository has no description
1package pulls
2
3import (
4 "context"
5 "fmt"
6 "net/http"
7 "time"
8
9 "tangled.org/core/api/tangled"
10 "tangled.org/core/appview/db"
11 "tangled.org/core/appview/models"
12 "tangled.org/core/appview/oauth"
13 "tangled.org/core/appview/reporesolver"
14
15 comatproto "github.com/bluesky-social/indigo/api/atproto"
16 "github.com/bluesky-social/indigo/atproto/syntax"
17 lexutil "github.com/bluesky-social/indigo/lex/util"
18)
19
20func (s *Pulls) ResubmitPull(w http.ResponseWriter, r *http.Request) {
21 l := s.logger.With("handler", "ResubmitPull")
22 noticeId := "pull-action-error"
23
24 user := s.oauth.GetMultiAccountUser(r)
25 if user != nil {
26 l = l.With("user", user.Did)
27 }
28
29 repo, err := s.repoResolver.Resolve(r)
30 if err != nil {
31 l.Error("failed to get repo and knot", "err", err)
32 return
33 }
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, noticeId, "Failed to get PR. Try again later.")
39 return
40 }
41 l = l.With("pull_id", pull.PullId, "pull_owner", pull.OwnerDid)
42
43 if pull.OwnerDid != syntax.DID(user.Did) {
44 s.pages.Notice(w, noticeId, "Unauthorized user. Try again later.")
45 return
46 }
47
48 if pull.SourceBranch == nil {
49 // can't resubmit if source is unknown. fail earlier
50 s.pages.Notice(w, noticeId, "PR source branch is unknown.")
51 return
52 }
53 sourceBranch := *pull.SourceBranch
54
55 var sourceRepo *models.Repo
56 if pull.SourceRepo == syntax.DID(repo.RepoDid) {
57 sourceRepo = repo
58 } else {
59 var err error
60 sourceRepo, err = db.GetRepoByDid(s.db, pull.SourceRepo.String())
61 if err != nil {
62 s.pages.Notice(w, noticeId, fmt.Sprintf("Unknown source repository: %q", pull.SourceRepo))
63 return
64 }
65 }
66
67 ctx := r.Context()
68
69 var base, head string
70 {
71 xrpcc := s.knotMirrorXRPC
72 branch, err := tangled.GitTempGetBranch(ctx, xrpcc, sourceBranch, pull.RepoDid.String())
73 if err != nil {
74 s.pages.Notice(w, noticeId, "Failed to get source branch")
75 return
76 }
77 out, err := tangled.GitTempGetMergeBase(ctx, xrpcc, pull.TargetBranch, branch.Hash, pull.RepoDid.String())
78 if err != nil {
79 s.pages.Notice(w, noticeId, "Failed to compute merge-base.")
80 return
81 }
82
83 head = branch.Hash
84 base = out.Commit
85 }
86
87 newVersion := models.PullVersion{
88 ID: pull.LatestVersionNumber() + 1,
89 Base: base,
90 Head: head,
91 Created: time.Now(),
92 }
93 pull.Versions = append(pull.Versions, newVersion)
94
95 // keep new version head in Knot
96 {
97 client, err := s.oauth.ServiceClient(
98 r,
99 oauth.WithService(sourceRepo.Knot),
100 oauth.WithLxm(tangled.GitKeepCommitNSID),
101 oauth.WithDev(s.config.Core.Dev),
102 )
103 if err != nil {
104 l.Error("failed to create service auth", "err", err)
105 s.pages.Notice(w, noticeId, "Failed to create service auth. Try again later.")
106 return
107 }
108
109 _, err = tangled.GitKeepCommit(ctx, client, &tangled.GitKeepCommit_Input{
110 Repo: sourceRepo.RepoDid,
111 Record: pull.AtUri().String(),
112 Source: &tangled.GitKeepCommit_Input_Source{
113 GitKeepCommit_Commit: &tangled.GitKeepCommit_Commit{
114 Repo: sourceRepo.RepoDid,
115 Oid: head,
116 },
117 },
118 })
119 if err != nil {
120 l.Error("failed to keep commit", "err", err)
121 s.pages.Notice(w, noticeId, "Failed to resubmit pull request. Try again later.")
122 return
123 }
124 }
125
126 // update PDS record
127 {
128 client, err := s.oauth.AuthorizedClient(r)
129 if err != nil {
130 s.pages.Notice(w, noticeId, "Unauthorized user. Try again later.")
131 return
132 }
133
134 // NOTE: some old PR records are missing CID
135 if err := s.ensurePullCid(ctx, client, pull); err != nil {
136 s.pages.Notice(w, noticeId, "Failed to get existing PR record. Is PR deleted from PDS?")
137 return
138 }
139
140 record := pull.AsRecord()
141 exCid := pull.Cid.String()
142 out, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
143 Collection: tangled.RepoPullNSID,
144 Repo: pull.OwnerDid.String(),
145 Rkey: pull.Rkey.String(),
146 SwapRecord: &exCid,
147 Record: &lexutil.LexiconTypeDecoder{Val: &record},
148 })
149 if err != nil {
150 l.Error("failed to create PDS record", "err", err)
151 s.pages.Notice(w, noticeId, "Failed to resubmit pull request. Try again later.")
152 return
153 }
154 pull.Cid = syntax.CID(out.Cid)
155 }
156
157 if err := db.SubmitPullVersion(ctx, s.db, pull.AtUri(), newVersion); err != nil {
158 l.Error("failed to update PR in DB", "err", err)
159 s.pages.Notice(w, noticeId, "Failed to resubmit pull request. Try again later.")
160 return
161 }
162
163 s.notifier.ResubmitPull(r.Context(), pull)
164
165 ownerSlashRepo := reporesolver.GetBaseRepoPath(r, repo)
166 s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d", ownerSlashRepo, pull.PullId))
167}
168
169func (s *Pulls) ensurePullCid(ctx context.Context, client lexutil.LexClient, pull *models.Pull) error {
170 out, err := comatproto.RepoGetRecord(ctx, client, "", tangled.RepoPullNSID, pull.OwnerDid.String(), pull.Rkey.String())
171 if err != nil {
172 return err
173 }
174 if out.Cid == nil {
175 return fmt.Errorf("record CID is empty")
176 }
177 pull.Cid = syntax.CID(*out.Cid)
178 return nil
179}