···
"github.com/go-chi/chi/v5"
"tangled.sh/tangled.sh/core/api/tangled"
18
+
"tangled.sh/tangled.sh/core/appview/auth"
"tangled.sh/tangled.sh/core/appview/db"
"tangled.sh/tangled.sh/core/appview/pages"
"tangled.sh/tangled.sh/core/types"
comatproto "github.com/bluesky-social/indigo/api/atproto"
24
+
"github.com/bluesky-social/indigo/atproto/syntax"
lexutil "github.com/bluesky-social/indigo/lex/util"
···
func (s *State) resubmitCheck(f *FullyResolvedRepo, pull *db.Pull) pages.ResubmitResult {
189
-
if pull.State == db.PullMerged {
194
+
if pull.State == db.PullMerged || pull.PullSource == nil {
193
-
if pull.PullSource == nil {
194
-
return pages.Unknown
198
+
var knot, ownerDid, repoName string
200
+
if pull.PullSource.Repo != nil {
201
+
// fork-based pulls
202
+
sourceRepo, err := db.GetRepoByAtUri(s.db, pull.PullSource.Repo.String())
204
+
log.Println("failed to get source repo", err)
205
+
return pages.Unknown
208
+
knot = sourceRepo.Knot
209
+
ownerDid = sourceRepo.Did
210
+
repoName = sourceRepo.Name
212
+
// pulls within the same repo
214
+
ownerDid = f.OwnerDid()
215
+
repoName = f.RepoName
197
-
us, err := NewUnsignedClient(f.Knot, s.config.Dev)
218
+
us, err := NewUnsignedClient(knot, s.config.Dev)
199
-
log.Printf("failed to setup signed client for %s; ignoring: %v", f.Knot, err)
220
+
log.Printf("failed to setup client for %s; ignoring: %v", knot, err)
203
-
resp, err := us.Branch(f.OwnerDid(), f.RepoName, pull.PullSource.Branch)
224
+
resp, err := us.Branch(ownerDid, repoName, pull.PullSource.Branch)
log.Println("failed to reach knotserver", err)
···
body, err := io.ReadAll(resp.Body)
211
-
log.Printf("Error reading response body: %v", err)
232
+
log.Printf("error reading response body: %v", err)
235
+
defer resp.Body.Close()
var result types.RepoBranchResponse
216
-
err = json.Unmarshal(body, &result)
238
+
if err := json.Unmarshal(body, &result); err != nil {
log.Println("failed to parse response:", err)
222
-
if pull.Submissions[pull.LastRoundNumber()].SourceRev != result.Branch.Hash {
223
-
log.Println(pull.Submissions[pull.LastRoundNumber()].SourceRev, result.Branch.Hash)
243
+
latestSubmission := pull.Submissions[pull.LastRoundNumber()]
244
+
if latestSubmission.SourceRev != result.Branch.Hash {
return pages.ShouldResubmit
226
-
return pages.ShouldNotResubmit
248
+
return pages.ShouldNotResubmit
func (s *State) RepoPullPatch(w http.ResponseWriter, r *http.Request) {
···
492
+
forks, err := db.GetForksByDid(s.db, user.Did)
494
+
log.Println("failed to get forks", err)
us, err := NewUnsignedClient(f.Knot, s.config.Dev)
···
s.pages.RepoNewPull(w, pages.RepoNewPullParams{
RepoInfo: f.RepoInfo(s, user),
Branches: result.Branches,
505
-
isPushAllowed := f.RepoInfo(s, user).Roles.IsPushAllowed()
title := r.FormValue("title")
body := r.FormValue("body")
targetBranch := r.FormValue("targetBranch")
536
+
fromFork := r.FormValue("fromFork")
sourceBranch := r.FormValue("sourceBranch")
patch := r.FormValue("patch")
512
-
isBranchBased := isPushAllowed && (sourceBranch != "")
540
+
isPushAllowed := f.RepoInfo(s, user).Roles.IsPushAllowed()
541
+
isBranchBased := isPushAllowed && sourceBranch != "" && fromFork == ""
isPatchBased := patch != ""
543
+
isForkBased := fromFork != "" && sourceBranch != ""
515
-
if !isBranchBased && !isPatchBased {
545
+
if !isBranchBased && !isPatchBased && !isForkBased {
s.pages.Notice(w, "pull", "Neither source branch nor patch supplied.")
···
530
-
// TODO: check if knot has this capability
531
-
var sourceRev string
532
-
var pullSource *db.PullSource
533
-
var recordPullSource *tangled.RepoPull_Source
535
-
pullSource = &db.PullSource{
536
-
Branch: sourceBranch,
538
-
recordPullSource = &tangled.RepoPull_Source{
539
-
Branch: sourceBranch,
541
-
// generate a patch using /compare
542
-
ksClient, err := NewUnsignedClient(f.Knot, s.config.Dev)
544
-
log.Printf("failed to create signed client for %s: %s", f.Knot, err)
545
-
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
561
+
s.handleBranchBasedPull(w, r, f, user, title, body, targetBranch, sourceBranch)
562
+
} else if isPatchBased {
563
+
s.handlePatchBasedPull(w, r, f, user, title, body, targetBranch, patch)
564
+
} else if isForkBased {
565
+
s.handleForkBasedPull(w, r, f, user, fromFork, title, body, targetBranch, sourceBranch)
549
-
resp, err := ksClient.Compare(f.OwnerDid(), f.RepoName, targetBranch, sourceBranch)
550
-
switch resp.StatusCode {
553
-
s.pages.Notice(w, "pull", "Branch based pull requests are not supported on this knot.")
571
+
func (s *State) handleBranchBasedPull(w http.ResponseWriter, r *http.Request, f *FullyResolvedRepo, user *auth.User, title, body, targetBranch, sourceBranch string) {
572
+
pullSource := &db.PullSource{
573
+
Branch: sourceBranch,
575
+
recordPullSource := &tangled.RepoPull_Source{
576
+
Branch: sourceBranch,
579
+
// Generate a patch using /compare
580
+
ksClient, err := NewUnsignedClient(f.Knot, s.config.Dev)
582
+
log.Printf("failed to create signed client for %s: %s", f.Knot, err)
583
+
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
556
-
respBody, err := io.ReadAll(resp.Body)
558
-
log.Println("failed to compare across branches")
559
-
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
561
-
defer resp.Body.Close()
587
+
resp, err := ksClient.Compare(f.OwnerDid(), f.RepoName, targetBranch, sourceBranch)
588
+
switch resp.StatusCode {
591
+
s.pages.Notice(w, "pull", "Branch based pull requests are not supported on this knot.")
563
-
var diffTreeResponse types.RepoDiffTreeResponse
564
-
err = json.Unmarshal(respBody, &diffTreeResponse)
566
-
log.Println("failed to unmarshal diff tree response", err)
567
-
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
595
+
respBody, err := io.ReadAll(resp.Body)
597
+
log.Println("failed to compare across branches")
598
+
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
601
+
defer resp.Body.Close()
570
-
sourceRev = diffTreeResponse.DiffTree.Rev2
571
-
patch = diffTreeResponse.DiffTree.Patch
603
+
var diffTreeResponse types.RepoDiffTreeResponse
604
+
err = json.Unmarshal(respBody, &diffTreeResponse)
606
+
log.Println("failed to unmarshal diff tree response", err)
607
+
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
574
-
// Validate patch format
575
-
if !isPatchValid(patch) {
576
-
s.pages.Notice(w, "pull", "Invalid patch format. Please provide a valid diff.")
611
+
sourceRev := diffTreeResponse.DiffTree.Rev2
612
+
patch := diffTreeResponse.DiffTree.Patch
580
-
tx, err := s.db.BeginTx(r.Context(), nil)
582
-
log.Println("failed to start tx")
583
-
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
586
-
defer tx.Rollback()
614
+
if !isPatchValid(patch) {
615
+
s.pages.Notice(w, "pull", "Invalid patch format. Please provide a valid diff.")
589
-
initialSubmission := db.PullSubmission{
591
-
SourceRev: sourceRev,
593
-
err = db.NewPull(tx, &db.Pull{
596
-
TargetBranch: targetBranch,
597
-
OwnerDid: user.Did,
600
-
Submissions: []*db.PullSubmission{
601
-
&initialSubmission,
603
-
PullSource: pullSource,
606
-
log.Println("failed to create pull request", err)
607
-
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
610
-
client, _ := s.auth.AuthorizedClient(r)
611
-
pullId, err := db.NextPullId(s.db, f.RepoAt)
613
-
log.Println("failed to get pull id", err)
614
-
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
619
+
s.createPullRequest(w, r, f, user, title, body, targetBranch, patch, sourceRev, pullSource, recordPullSource)
618
-
atResp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
619
-
Collection: tangled.RepoPullNSID,
622
-
Record: &lexutil.LexiconTypeDecoder{
623
-
Val: &tangled.RepoPull{
625
-
PullId: int64(pullId),
626
-
TargetRepo: string(f.RepoAt),
627
-
TargetBranch: targetBranch,
629
-
Source: recordPullSource,
622
+
func (s *State) handlePatchBasedPull(w http.ResponseWriter, r *http.Request, f *FullyResolvedRepo, user *auth.User, title, body, targetBranch, patch string) {
623
+
if !isPatchValid(patch) {
624
+
s.pages.Notice(w, "pull", "Invalid patch format. Please provide a valid diff.")
634
-
err = db.SetPullAt(s.db, f.RepoAt, pullId, atResp.Uri)
636
-
log.Println("failed to get pull id", err)
637
-
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
628
+
s.createPullRequest(w, r, f, user, title, body, targetBranch, patch, "", nil, nil)
641
-
s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d", f.OwnerSlashRepo(), pullId))
631
+
func (s *State) handleForkBasedPull(w http.ResponseWriter, r *http.Request, f *FullyResolvedRepo, user *auth.User, forkRepo string, title, body, targetBranch, sourceBranch string) {
632
+
fork, err := db.GetForkByDid(s.db, user.Did, forkRepo)
633
+
if errors.Is(err, sql.ErrNoRows) {
634
+
s.pages.Notice(w, "pull", "No such fork.")
636
+
} else if err != nil {
637
+
log.Println("failed to fetch fork:", err)
638
+
s.pages.Notice(w, "pull", "Failed to fetch fork.")
642
+
secret, err := db.GetRegistrationKey(s.db, fork.Knot)
644
+
log.Println("failed to fetch registration key:", err)
645
+
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
649
+
sc, err := NewSignedClient(fork.Knot, secret, s.config.Dev)
651
+
log.Println("failed to create signed client:", err)
652
+
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
656
+
us, err := NewUnsignedClient(fork.Knot, s.config.Dev)
658
+
log.Println("failed to create unsigned client:", err)
659
+
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
663
+
resp, err := sc.NewHiddenRef(user.Did, fork.Name, sourceBranch, targetBranch)
665
+
log.Println("failed to create hidden ref:", err, resp.StatusCode)
666
+
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
670
+
switch resp.StatusCode {
673
+
s.pages.Notice(w, "pull", "Branch based pull requests are not supported on this knot.")
677
+
hiddenRef := url.QueryEscape(fmt.Sprintf("hidden/%s/%s", sourceBranch, targetBranch))
678
+
// We're now comparing the sourceBranch (on the fork) against the hiddenRef which is tracking
679
+
// the targetBranch on the target repository. This code is a bit confusing, but here's an example:
680
+
// hiddenRef: hidden/feature-1/main (on repo-fork)
681
+
// targetBranch: main (on repo-1)
682
+
// sourceBranch: feature-1 (on repo-fork)
683
+
diffResp, err := us.Compare(user.Did, fork.Name, hiddenRef, sourceBranch)
685
+
log.Println("failed to compare across branches", err)
686
+
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
690
+
respBody, err := io.ReadAll(diffResp.Body)
692
+
log.Println("failed to read response body", err)
693
+
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
697
+
defer resp.Body.Close()
699
+
var diffTreeResponse types.RepoDiffTreeResponse
700
+
err = json.Unmarshal(respBody, &diffTreeResponse)
702
+
log.Println("failed to unmarshal diff tree response", err)
703
+
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
707
+
sourceRev := diffTreeResponse.DiffTree.Rev2
708
+
patch := diffTreeResponse.DiffTree.Patch
710
+
if !isPatchValid(patch) {
711
+
s.pages.Notice(w, "pull", "Invalid patch format. Please provide a valid diff.")
715
+
forkAtUri, err := syntax.ParseATURI(fork.AtUri)
717
+
log.Println("failed to parse fork AT URI", err)
718
+
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
722
+
s.createPullRequest(w, r, f, user, title, body, targetBranch, patch, sourceRev, &db.PullSource{
723
+
Branch: sourceBranch,
725
+
}, &tangled.RepoPull_Source{Branch: sourceBranch, Repo: &fork.AtUri})
728
+
func (s *State) createPullRequest(w http.ResponseWriter, r *http.Request, f *FullyResolvedRepo, user *auth.User, title, body, targetBranch, patch, sourceRev string, pullSource *db.PullSource, recordPullSource *tangled.RepoPull_Source) {
729
+
tx, err := s.db.BeginTx(r.Context(), nil)
731
+
log.Println("failed to start tx")
732
+
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
735
+
defer tx.Rollback()
738
+
initialSubmission := db.PullSubmission{
740
+
SourceRev: sourceRev,
742
+
err = db.NewPull(tx, &db.Pull{
745
+
TargetBranch: targetBranch,
746
+
OwnerDid: user.Did,
749
+
Submissions: []*db.PullSubmission{
750
+
&initialSubmission,
752
+
PullSource: pullSource,
755
+
log.Println("failed to create pull request", err)
756
+
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
759
+
client, _ := s.auth.AuthorizedClient(r)
760
+
pullId, err := db.NextPullId(s.db, f.RepoAt)
762
+
log.Println("failed to get pull id", err)
763
+
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
767
+
atResp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
768
+
Collection: tangled.RepoPullNSID,
771
+
Record: &lexutil.LexiconTypeDecoder{
772
+
Val: &tangled.RepoPull{
774
+
PullId: int64(pullId),
775
+
TargetRepo: string(f.RepoAt),
776
+
TargetBranch: targetBranch,
778
+
Source: recordPullSource,
783
+
err = db.SetPullAt(s.db, f.RepoAt, pullId, atResp.Uri)
785
+
log.Println("failed to get pull id", err)
786
+
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
790
+
s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d", f.OwnerSlashRepo(), pullId))
func (s *State) PatchUploadFragment(w http.ResponseWriter, r *http.Request) {
···
var recordPullSource *tangled.RepoPull_Source
726
-
// this pull is a branch based pull
873
+
var ownerDid, repoName, knotName string
874
+
var isSameRepo bool = pull.IsSameRepoBranch()
875
+
sourceBranch := pull.PullSource.Branch
876
+
targetBranch := pull.TargetBranch
877
+
recordPullSource = &tangled.RepoPull_Source{
878
+
Branch: sourceBranch,
isPushAllowed := f.RepoInfo(s, user).Roles.IsPushAllowed()
728
-
if pull.IsSameRepoBranch() && isPushAllowed {
729
-
sourceBranch := pull.PullSource.Branch
730
-
targetBranch := pull.TargetBranch
731
-
recordPullSource = &tangled.RepoPull_Source{
732
-
Branch: sourceBranch,
882
+
if isSameRepo && isPushAllowed {
883
+
ownerDid = f.OwnerDid()
884
+
repoName = f.RepoName
886
+
} else if !isSameRepo {
887
+
sourceRepo, err := db.GetRepoByAtUri(s.db, pull.PullSource.Repo.String())
889
+
log.Println("failed to get source repo", err)
890
+
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
893
+
ownerDid = sourceRepo.Did
894
+
repoName = sourceRepo.Name
895
+
knotName = sourceRepo.Knot
898
+
if sourceBranch != "" && knotName != "" {
// extract patch by performing compare
735
-
ksClient, err := NewUnsignedClient(f.Knot, s.config.Dev)
900
+
ksClient, err := NewUnsignedClient(knotName, s.config.Dev)
902
+
log.Printf("failed to create client for %s: %s", knotName, err)
903
+
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
908
+
secret, err := db.GetRegistrationKey(s.db, knotName)
910
+
log.Printf("failed to get registration key for %s: %s", knotName, err)
911
+
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
914
+
// update the hidden tracking branch to latest
915
+
signedClient, err := NewSignedClient(knotName, secret, s.config.Dev)
917
+
log.Printf("failed to create signed client for %s: %s", knotName, err)
918
+
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
921
+
resp, err := signedClient.NewHiddenRef(ownerDid, repoName, sourceBranch, targetBranch)
922
+
if err != nil || resp.StatusCode != http.StatusNoContent {
923
+
log.Printf("failed to update tracking branch: %s", err)
924
+
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
929
+
var compareResp *http.Response
931
+
hiddenRef := url.QueryEscape(fmt.Sprintf("hidden/%s/%s", sourceBranch, targetBranch))
932
+
compareResp, err = ksClient.Compare(ownerDid, repoName, hiddenRef, sourceBranch)
934
+
compareResp, err = ksClient.Compare(ownerDid, repoName, targetBranch, sourceBranch)
737
-
log.Printf("failed to create signed client for %s: %s", f.Knot, err)
937
+
log.Printf("failed to compare branches: %s", err)
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
941
+
defer compareResp.Body.Close()
742
-
resp, err := ksClient.Compare(f.OwnerDid(), f.RepoName, targetBranch, sourceBranch)
743
-
switch resp.StatusCode {
943
+
switch compareResp.StatusCode {
s.pages.Notice(w, "pull", "Branch based pull requests are not supported on this knot.")
749
-
respBody, err := io.ReadAll(resp.Body)
950
+
respBody, err := io.ReadAll(compareResp.Body)
log.Println("failed to compare across branches")
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
754
-
defer resp.Body.Close()
956
+
defer compareResp.Body.Close()
var diffTreeResponse types.RepoDiffTreeResponse
err = json.Unmarshal(respBody, &diffTreeResponse)
log.Println("failed to unmarshal diff tree response", err)
s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.")
sourceRev = diffTreeResponse.DiffTree.Rev2
···
782
-
// Validate patch format
if !isPatchValid(patch) {
s.pages.Notice(w, "resubmit-error", "Invalid patch format. Please provide a valid diff.")