forked from tangled.org/core
Monorepo for Tangled

appview/pulls: rework pulls handlers to use xrpc calls

Signed-off-by: Anirudh Oppiliappan <anirudh@tangled.sh>

anirudh.fi beb8221f 83026188

verified
Changed files
+230 -85
appview
pulls
+230 -85
appview/pulls/pulls.go
··· 2 2 3 3 import ( 4 4 "database/sql" 5 + "encoding/json" 5 6 "errors" 6 7 "fmt" 7 8 "log" ··· 21 22 "tangled.sh/tangled.sh/core/appview/reporesolver" 22 23 "tangled.sh/tangled.sh/core/appview/xrpcclient" 23 24 "tangled.sh/tangled.sh/core/idresolver" 24 - "tangled.sh/tangled.sh/core/knotclient" 25 25 "tangled.sh/tangled.sh/core/patchutil" 26 26 "tangled.sh/tangled.sh/core/tid" 27 27 "tangled.sh/tangled.sh/core/types" ··· 99 99 mergeCheckResponse := s.mergeCheck(r, f, pull, stack) 100 100 resubmitResult := pages.Unknown 101 101 if user.Did == pull.OwnerDid { 102 - resubmitResult = s.resubmitCheck(f, pull, stack) 102 + resubmitResult = s.resubmitCheck(r, f, pull, stack) 103 103 } 104 104 105 105 s.pages.PullActionsFragment(w, pages.PullActionsParams{ ··· 154 154 mergeCheckResponse := s.mergeCheck(r, f, pull, stack) 155 155 resubmitResult := pages.Unknown 156 156 if user != nil && user.Did == pull.OwnerDid { 157 - resubmitResult = s.resubmitCheck(f, pull, stack) 157 + resubmitResult = s.resubmitCheck(r, f, pull, stack) 158 158 } 159 159 160 160 repoInfo := f.RepoInfo(user) ··· 282 282 return result 283 283 } 284 284 285 - func (s *Pulls) resubmitCheck(f *reporesolver.ResolvedRepo, pull *db.Pull, stack db.Stack) pages.ResubmitResult { 285 + func (s *Pulls) resubmitCheck(r *http.Request, f *reporesolver.ResolvedRepo, pull *db.Pull, stack db.Stack) pages.ResubmitResult { 286 286 if pull.State == db.PullMerged || pull.State == db.PullDeleted || pull.PullSource == nil { 287 287 return pages.Unknown 288 288 } ··· 307 307 repoName = f.Name 308 308 } 309 309 310 - us, err := knotclient.NewUnsignedClient(knot, s.config.Core.Dev) 311 - if err != nil { 312 - log.Printf("failed to setup client for %s; ignoring: %v", knot, err) 313 - return pages.Unknown 310 + scheme := "http" 311 + if !s.config.Core.Dev { 312 + scheme = "https" 313 + } 314 + host := fmt.Sprintf("%s://%s", scheme, knot) 315 + xrpcc := &indigoxrpc.Client{ 316 + Host: host, 314 317 } 315 318 316 - result, err := us.Branch(ownerDid, repoName, pull.PullSource.Branch) 319 + repo := fmt.Sprintf("%s/%s", ownerDid, repoName) 320 + branchResp, err := tangled.RepoBranch(r.Context(), xrpcc, pull.PullSource.Branch, repo) 317 321 if err != nil { 322 + if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil { 323 + log.Println("failed to call XRPC repo.branches", xrpcerr) 324 + return pages.Unknown 325 + } 318 326 log.Println("failed to reach knotserver", err) 319 327 return pages.Unknown 320 328 } 321 329 330 + targetBranch := branchResp 331 + 322 332 latestSourceRev := pull.Submissions[pull.LastRoundNumber()].SourceRev 323 333 324 334 if pull.IsStacked() && stack != nil { ··· 326 336 latestSourceRev = top.Submissions[top.LastRoundNumber()].SourceRev 327 337 } 328 338 329 - if latestSourceRev != result.Branch.Hash { 339 + if latestSourceRev != targetBranch.Hash { 330 340 return pages.ShouldResubmit 331 341 } 332 342 ··· 678 688 679 689 switch r.Method { 680 690 case http.MethodGet: 681 - us, err := knotclient.NewUnsignedClient(f.Knot, s.config.Core.Dev) 691 + scheme := "http" 692 + if !s.config.Core.Dev { 693 + scheme = "https" 694 + } 695 + host := fmt.Sprintf("%s://%s", scheme, f.Knot) 696 + xrpcc := &indigoxrpc.Client{ 697 + Host: host, 698 + } 699 + 700 + repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name) 701 + xrpcBytes, err := tangled.RepoBranches(r.Context(), xrpcc, "", 0, repo) 682 702 if err != nil { 683 - log.Printf("failed to create unsigned client for %s", f.Knot) 684 - s.pages.Error503(w) 703 + if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil { 704 + log.Println("failed to call XRPC repo.branches", xrpcerr) 705 + s.pages.Error503(w) 706 + return 707 + } 708 + log.Println("failed to fetch branches", err) 685 709 return 686 710 } 687 711 688 - result, err := us.Branches(f.OwnerDid(), f.Name) 689 - if err != nil { 690 - log.Println("failed to fetch branches", err) 712 + var result types.RepoBranchesResponse 713 + if err := json.Unmarshal(xrpcBytes, &result); err != nil { 714 + log.Println("failed to decode XRPC response", err) 715 + s.pages.Error503(w) 691 716 return 692 717 } 693 718 ··· 752 777 return 753 778 } 754 779 755 - us, err := knotclient.NewUnsignedClient(f.Knot, s.config.Core.Dev) 756 - if err != nil { 757 - log.Printf("failed to create unsigned client to %s: %v", f.Knot, err) 758 - s.pages.Notice(w, "pull", "Failed to create a pull request. Try again later.") 759 - return 760 - } 780 + // us, err := knotclient.NewUnsignedClient(f.Knot, s.config.Core.Dev) 781 + // if err != nil { 782 + // log.Printf("failed to create unsigned client to %s: %v", f.Knot, err) 783 + // s.pages.Notice(w, "pull", "Failed to create a pull request. Try again later.") 784 + // return 785 + // } 761 786 762 - caps, err := us.Capabilities() 763 - if err != nil { 764 - log.Println("error fetching knot caps", f.Knot, err) 765 - s.pages.Notice(w, "pull", "Failed to create a pull request. Try again later.") 766 - return 787 + // TODO: make capabilities an xrpc call 788 + caps := struct { 789 + PullRequests struct { 790 + FormatPatch bool 791 + BranchSubmissions bool 792 + ForkSubmissions bool 793 + PatchSubmissions bool 794 + } 795 + }{ 796 + PullRequests: struct { 797 + FormatPatch bool 798 + BranchSubmissions bool 799 + ForkSubmissions bool 800 + PatchSubmissions bool 801 + }{ 802 + FormatPatch: true, 803 + BranchSubmissions: true, 804 + ForkSubmissions: true, 805 + PatchSubmissions: true, 806 + }, 767 807 } 808 + 809 + // caps, err := us.Capabilities() 810 + // if err != nil { 811 + // log.Println("error fetching knot caps", f.Knot, err) 812 + // s.pages.Notice(w, "pull", "Failed to create a pull request. Try again later.") 813 + // return 814 + // } 768 815 769 816 if !caps.PullRequests.FormatPatch { 770 817 s.pages.Notice(w, "pull", "This knot doesn't support format-patch. Unfortunately, there is no fallback for now.") ··· 806 853 sourceBranch string, 807 854 isStacked bool, 808 855 ) { 809 - // Generate a patch using /compare 810 - ksClient, err := knotclient.NewUnsignedClient(f.Knot, s.config.Core.Dev) 811 - if err != nil { 812 - log.Printf("failed to create signed client for %s: %s", f.Knot, err) 813 - s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.") 814 - return 856 + scheme := "http" 857 + if !s.config.Core.Dev { 858 + scheme = "https" 859 + } 860 + host := fmt.Sprintf("%s://%s", scheme, f.Knot) 861 + xrpcc := &indigoxrpc.Client{ 862 + Host: host, 815 863 } 816 864 817 - comparison, err := ksClient.Compare(f.OwnerDid(), f.Name, targetBranch, sourceBranch) 865 + repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name) 866 + xrpcBytes, err := tangled.RepoCompare(r.Context(), xrpcc, repo, targetBranch, sourceBranch) 818 867 if err != nil { 868 + if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil { 869 + log.Println("failed to call XRPC repo.compare", xrpcerr) 870 + s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.") 871 + return 872 + } 819 873 log.Println("failed to compare", err) 820 874 s.pages.Notice(w, "pull", err.Error()) 875 + return 876 + } 877 + 878 + var comparison types.RepoFormatPatchResponse 879 + if err := json.Unmarshal(xrpcBytes, &comparison); err != nil { 880 + log.Println("failed to decode XRPC compare response", err) 881 + s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.") 821 882 return 822 883 } 823 884 ··· 869 930 oauth.WithLxm(tangled.RepoHiddenRefNSID), 870 931 oauth.WithDev(s.config.Core.Dev), 871 932 ) 872 - if err != nil { 873 - log.Printf("failed to connect to knot server: %v", err) 874 - s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.") 875 - return 876 - } 877 - 878 - us, err := knotclient.NewUnsignedClient(fork.Knot, s.config.Core.Dev) 879 - if err != nil { 880 - log.Println("failed to create unsigned client:", err) 881 - s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.") 882 - return 883 - } 884 933 885 934 resp, err := tangled.RepoHiddenRef( 886 935 r.Context(), ··· 911 960 // hiddenRef: hidden/feature-1/main (on repo-fork) 912 961 // targetBranch: main (on repo-1) 913 962 // sourceBranch: feature-1 (on repo-fork) 914 - comparison, err := us.Compare(fork.Did, fork.Name, hiddenRef, sourceBranch) 963 + forkScheme := "http" 964 + if !s.config.Core.Dev { 965 + forkScheme = "https" 966 + } 967 + forkHost := fmt.Sprintf("%s://%s", forkScheme, fork.Knot) 968 + forkXrpcc := &indigoxrpc.Client{ 969 + Host: forkHost, 970 + } 971 + 972 + forkRepoId := fmt.Sprintf("%s/%s", fork.Did, fork.Name) 973 + forkXrpcBytes, err := tangled.RepoCompare(r.Context(), forkXrpcc, forkRepoId, hiddenRef, sourceBranch) 915 974 if err != nil { 975 + if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil { 976 + log.Println("failed to call XRPC repo.compare for fork", xrpcerr) 977 + s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.") 978 + return 979 + } 916 980 log.Println("failed to compare across branches", err) 917 981 s.pages.Notice(w, "pull", err.Error()) 918 982 return 919 983 } 920 984 985 + var comparison types.RepoFormatPatchResponse 986 + if err := json.Unmarshal(forkXrpcBytes, &comparison); err != nil { 987 + log.Println("failed to decode XRPC compare response for fork", err) 988 + s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.") 989 + return 990 + } 991 + 921 992 sourceRev := comparison.Rev2 922 993 patch := comparison.Patch 923 994 ··· 1211 1282 return 1212 1283 } 1213 1284 1214 - us, err := knotclient.NewUnsignedClient(f.Knot, s.config.Core.Dev) 1285 + scheme := "http" 1286 + if !s.config.Core.Dev { 1287 + scheme = "https" 1288 + } 1289 + host := fmt.Sprintf("%s://%s", scheme, f.Knot) 1290 + xrpcc := &indigoxrpc.Client{ 1291 + Host: host, 1292 + } 1293 + 1294 + repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name) 1295 + xrpcBytes, err := tangled.RepoBranches(r.Context(), xrpcc, "", 0, repo) 1215 1296 if err != nil { 1216 - log.Printf("failed to create unsigned client for %s", f.Knot) 1217 - s.pages.Error503(w) 1297 + if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil { 1298 + log.Println("failed to call XRPC repo.branches", xrpcerr) 1299 + s.pages.Error503(w) 1300 + return 1301 + } 1302 + log.Println("failed to fetch branches", err) 1218 1303 return 1219 1304 } 1220 1305 1221 - result, err := us.Branches(f.OwnerDid(), f.Name) 1222 - if err != nil { 1223 - log.Println("failed to reach knotserver", err) 1306 + var result types.RepoBranchesResponse 1307 + if err := json.Unmarshal(xrpcBytes, &result); err != nil { 1308 + log.Println("failed to decode XRPC response", err) 1309 + s.pages.Error503(w) 1224 1310 return 1225 1311 } 1226 1312 ··· 1284 1370 return 1285 1371 } 1286 1372 1287 - sourceBranchesClient, err := knotclient.NewUnsignedClient(repo.Knot, s.config.Core.Dev) 1373 + sourceScheme := "http" 1374 + if !s.config.Core.Dev { 1375 + sourceScheme = "https" 1376 + } 1377 + sourceHost := fmt.Sprintf("%s://%s", sourceScheme, repo.Knot) 1378 + sourceXrpcc := &indigoxrpc.Client{ 1379 + Host: sourceHost, 1380 + } 1381 + 1382 + sourceRepo := fmt.Sprintf("%s/%s", forkOwnerDid, repo.Name) 1383 + sourceXrpcBytes, err := tangled.RepoBranches(r.Context(), sourceXrpcc, "", 0, sourceRepo) 1288 1384 if err != nil { 1289 - log.Printf("failed to create unsigned client for %s", repo.Knot) 1290 - s.pages.Error503(w) 1385 + if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil { 1386 + log.Println("failed to call XRPC repo.branches for source", xrpcerr) 1387 + s.pages.Error503(w) 1388 + return 1389 + } 1390 + log.Println("failed to fetch source branches", err) 1291 1391 return 1292 1392 } 1293 1393 1294 - sourceResult, err := sourceBranchesClient.Branches(forkOwnerDid, repo.Name) 1295 - if err != nil { 1296 - log.Println("failed to reach knotserver for source branches", err) 1394 + // Decode source branches 1395 + var sourceBranches types.RepoBranchesResponse 1396 + if err := json.Unmarshal(sourceXrpcBytes, &sourceBranches); err != nil { 1397 + log.Println("failed to decode source branches XRPC response", err) 1398 + s.pages.Error503(w) 1297 1399 return 1298 1400 } 1299 1401 1300 - targetBranchesClient, err := knotclient.NewUnsignedClient(f.Knot, s.config.Core.Dev) 1402 + targetScheme := "http" 1403 + if !s.config.Core.Dev { 1404 + targetScheme = "https" 1405 + } 1406 + targetHost := fmt.Sprintf("%s://%s", targetScheme, f.Knot) 1407 + targetXrpcc := &indigoxrpc.Client{ 1408 + Host: targetHost, 1409 + } 1410 + 1411 + targetRepo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name) 1412 + targetXrpcBytes, err := tangled.RepoBranches(r.Context(), targetXrpcc, "", 0, targetRepo) 1301 1413 if err != nil { 1302 - log.Printf("failed to create unsigned client for target knot %s", f.Knot) 1303 - s.pages.Error503(w) 1414 + if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil { 1415 + log.Println("failed to call XRPC repo.branches for target", xrpcerr) 1416 + s.pages.Error503(w) 1417 + return 1418 + } 1419 + log.Println("failed to fetch target branches", err) 1304 1420 return 1305 1421 } 1306 1422 1307 - targetResult, err := targetBranchesClient.Branches(f.OwnerDid(), f.Name) 1308 - if err != nil { 1309 - log.Println("failed to reach knotserver for target branches", err) 1423 + // Decode target branches 1424 + var targetBranches types.RepoBranchesResponse 1425 + if err := json.Unmarshal(targetXrpcBytes, &targetBranches); err != nil { 1426 + log.Println("failed to decode target branches XRPC response", err) 1427 + s.pages.Error503(w) 1310 1428 return 1311 1429 } 1312 1430 1313 - sourceBranches := sourceResult.Branches 1314 - sort.Slice(sourceBranches, func(i int, j int) bool { 1315 - return sourceBranches[i].Commit.Committer.When.After(sourceBranches[j].Commit.Committer.When) 1431 + sort.Slice(sourceBranches.Branches, func(i int, j int) bool { 1432 + return sourceBranches.Branches[i].Commit.Committer.When.After(sourceBranches.Branches[j].Commit.Committer.When) 1316 1433 }) 1317 1434 1318 1435 s.pages.PullCompareForkBranchesFragment(w, pages.PullCompareForkBranchesParams{ 1319 1436 RepoInfo: f.RepoInfo(user), 1320 - SourceBranches: sourceBranches, 1321 - TargetBranches: targetResult.Branches, 1437 + SourceBranches: sourceBranches.Branches, 1438 + TargetBranches: targetBranches.Branches, 1322 1439 }) 1323 1440 } 1324 1441 ··· 1413 1530 return 1414 1531 } 1415 1532 1416 - ksClient, err := knotclient.NewUnsignedClient(f.Knot, s.config.Core.Dev) 1417 - if err != nil { 1418 - log.Printf("failed to create client for %s: %s", f.Knot, err) 1419 - s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") 1420 - return 1533 + scheme := "http" 1534 + if !s.config.Core.Dev { 1535 + scheme = "https" 1536 + } 1537 + host := fmt.Sprintf("%s://%s", scheme, f.Knot) 1538 + xrpcc := &indigoxrpc.Client{ 1539 + Host: host, 1421 1540 } 1422 1541 1423 - comparison, err := ksClient.Compare(f.OwnerDid(), f.Name, pull.TargetBranch, pull.PullSource.Branch) 1542 + repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name) 1543 + xrpcBytes, err := tangled.RepoCompare(r.Context(), xrpcc, repo, pull.TargetBranch, pull.PullSource.Branch) 1424 1544 if err != nil { 1545 + if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil { 1546 + log.Println("failed to call XRPC repo.compare", xrpcerr) 1547 + s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") 1548 + return 1549 + } 1425 1550 log.Printf("compare request failed: %s", err) 1426 1551 s.pages.Notice(w, "resubmit-error", err.Error()) 1552 + return 1553 + } 1554 + 1555 + var comparison types.RepoFormatPatchResponse 1556 + if err := json.Unmarshal(xrpcBytes, &comparison); err != nil { 1557 + log.Println("failed to decode XRPC compare response", err) 1558 + s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") 1427 1559 return 1428 1560 } 1429 1561 ··· 1463 1595 } 1464 1596 1465 1597 // extract patch by performing compare 1466 - ksClient, err := knotclient.NewUnsignedClient(forkRepo.Knot, s.config.Core.Dev) 1598 + forkScheme := "http" 1599 + if !s.config.Core.Dev { 1600 + forkScheme = "https" 1601 + } 1602 + forkHost := fmt.Sprintf("%s://%s", forkScheme, forkRepo.Knot) 1603 + forkRepoId := fmt.Sprintf("%s/%s", forkRepo.Did, forkRepo.Name) 1604 + forkXrpcBytes, err := tangled.RepoCompare(r.Context(), &indigoxrpc.Client{Host: forkHost}, forkRepoId, pull.TargetBranch, pull.PullSource.Branch) 1467 1605 if err != nil { 1468 - log.Printf("failed to create client for %s: %s", forkRepo.Knot, err) 1606 + if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil { 1607 + log.Println("failed to call XRPC repo.compare for fork", xrpcerr) 1608 + s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") 1609 + return 1610 + } 1611 + log.Printf("failed to compare branches: %s", err) 1612 + s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") 1613 + return 1614 + } 1615 + 1616 + var forkComparison types.RepoFormatPatchResponse 1617 + if err := json.Unmarshal(forkXrpcBytes, &forkComparison); err != nil { 1618 + log.Println("failed to decode XRPC compare response for fork", err) 1469 1619 s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") 1470 1620 return 1471 1621 } ··· 1501 1651 return 1502 1652 } 1503 1653 1504 - hiddenRef := fmt.Sprintf("hidden/%s/%s", pull.PullSource.Branch, pull.TargetBranch) 1505 - comparison, err := ksClient.Compare(forkRepo.Did, forkRepo.Name, hiddenRef, pull.PullSource.Branch) 1506 - if err != nil { 1507 - log.Printf("failed to compare branches: %s", err) 1508 - s.pages.Notice(w, "resubmit-error", err.Error()) 1509 - return 1510 - } 1654 + // Use the fork comparison we already made 1655 + comparison := forkComparison 1511 1656 1512 1657 sourceRev := comparison.Rev2 1513 1658 patch := comparison.Patch