forked from tangled.org/core
Monorepo for Tangled

appview: pulls: add compare across forks

anirudh.fi c2e3b7a7 d6b7f6e6

verified
+27 -7
appview/pages/pages.go
··· 618 618 } 619 619 620 620 type RepoSinglePullParams struct { 621 - LoggedInUser *auth.User 622 - RepoInfo RepoInfo 623 - Active string 624 - DidHandleMap map[string]string 625 - Pull *db.Pull 626 - MergeCheck types.MergeCheckResponse 627 - ResubmitCheck ResubmitResult 621 + LoggedInUser *auth.User 622 + RepoInfo RepoInfo 623 + Active string 624 + DidHandleMap map[string]string 625 + Pull *db.Pull 626 + PullSourceRepo *db.Repo 627 + MergeCheck types.MergeCheckResponse 628 + ResubmitCheck ResubmitResult 628 629 } 629 630 630 631 func (p *Pages) RepoSinglePull(w io.Writer, params RepoSinglePullParams) error { ··· 662 663 663 664 func (p *Pages) PullCompareBranchesFragment(w io.Writer, params PullCompareBranchesParams) error { 664 665 return p.executePlain("fragments/pullCompareBranches", w, params) 666 + } 667 + 668 + type PullCompareForkParams struct { 669 + RepoInfo RepoInfo 670 + Forks []db.Repo 671 + } 672 + 673 + func (p *Pages) PullCompareForkFragment(w io.Writer, params PullCompareForkParams) error { 674 + return p.executePlain("fragments/pullCompareForks", w, params) 675 + } 676 + 677 + type PullCompareForkBranchesParams struct { 678 + RepoInfo RepoInfo 679 + SourceBranches []types.Branch 680 + TargetBranches []types.Branch 681 + } 682 + 683 + func (p *Pages) PullCompareForkBranchesFragment(w io.Writer, params PullCompareForkBranchesParams) error { 684 + return p.executePlain("fragments/pullCompareForksBranches", w, params) 665 685 } 666 686 667 687 type PullResubmitParams struct {
+1 -12
appview/pages/templates/fragments/pullCompareBranches.html
··· 1 1 {{ define "fragments/pullCompareBranches" }} 2 + 2 3 <div id="patch-upload"> 3 4 <label for="targetBranch" class="dark:text-white">configure branches</label> 4 5 <div class="flex flex-wrap gap-2 items-center"> ··· 28 29 </option> 29 30 {{ end }} 30 31 </select> 31 - 32 - <span class="text-sm"> 33 - ... or upload a patch 34 - <button 35 - class="btn text-sm" 36 - hx-get="/{{ .RepoInfo.FullName }}/pulls/new/patch-upload" 37 - hx-swap="outerHTML" 38 - hx-target="#patch-upload" 39 - > 40 - upload patch 41 - </button> 42 - </span> 43 32 </div> 44 33 45 34 </div>
+35
appview/pages/templates/fragments/pullCompareForks.html
··· 1 + {{ define "fragments/pullCompareForks" }} 2 + <div id="patch-upload"> 3 + <label for="forkSelect" class="dark:text-white" 4 + >select a fork to compare</label 5 + > 6 + <div class="flex flex-wrap gap-2 items-center mb-4"> 7 + <select 8 + id="forkSelect" 9 + name="fork" 10 + required 11 + class="p-1 border border-gray-200 bg-white dark:bg-gray-700 dark:text-white dark:border-gray-600" 12 + hx-get="/{{ $.RepoInfo.FullName }}/pulls/new/fork-branches" 13 + hx-target="#branch-selection" 14 + hx-vals='{"fork": this.value}' 15 + hx-swap="innerHTML" 16 + onchange="document.getElementById('hiddenForkInput').value = this.value;" 17 + > 18 + <option disabled selected>select a fork</option> 19 + {{ range .Forks }} 20 + <option value="{{ .Name }}" class="py-1"> 21 + {{ .Name }} 22 + </option> 23 + {{ end }} 24 + </select> 25 + 26 + <input type="hidden" id="hiddenForkInput" name="fork" value=""> 27 + </div> 28 + 29 + <div id="branch-selection" class="mt-2"> 30 + <div class="text-sm text-gray-500 dark:text-gray-400"> 31 + Select a fork first to view available branches 32 + </div> 33 + </div> 34 + </div> 35 + {{ end }}
+31
appview/pages/templates/fragments/pullCompareForksBranches.html
··· 1 + {{ define "fragments/pullCompareForksBranches" }} 2 + <div class="flex flex-wrap gap-2 items-center"> 3 + <select 4 + required 5 + name="targetBranch" 6 + class="p-1 border border-gray-200 bg-white dark:bg-gray-700 dark:text-white dark:border-gray-600" 7 + > 8 + <option disabled selected>target branch</option> 9 + {{ range .TargetBranches }} 10 + <option value="{{ .Reference.Name }}" class="py-1"> 11 + {{ .Reference.Name }} 12 + </option> 13 + {{ end }} 14 + </select> 15 + 16 + {{ i "move-left" "w-5 h-5" }} 17 + 18 + 19 + <select 20 + name="sourceBranch" 21 + class="p-1 border border-gray-200 bg-white dark:bg-gray-700 dark:text-white dark:border-gray-600" 22 + > 23 + <option disabled selected>source branch</option> 24 + {{ range .SourceBranches }} 25 + <option value="{{ .Reference.Name }}" class="py-1"> 26 + {{ .Reference.Name }} 27 + </option> 28 + {{ end }} 29 + </select> 30 + </div> 31 + {{ end }}
+11 -23
appview/pages/templates/fragments/pullPatchUpload.html
··· 1 1 {{ define "fragments/pullPatchUpload" }} 2 - <div id="patch-upload"> 3 - <label for="patch" class="dark:text-white">paste your patch here</label> 4 - <textarea 5 - name="patch" 6 - id="patch" 7 - rows="10" 8 - class="w-full resize-y font-mono dark:bg-gray-700 dark:text-white dark:border-gray-600" 9 - placeholder="Paste your git diff output here." 10 - ></textarea> 11 - 12 - {{ if .RepoInfo.Roles.IsPushAllowed }} 13 - <div class="mt-4 text-sm"> 14 - You can also submit a pull request from a branch. 15 - <button 16 - class="btn text-sm" 17 - hx-get="/{{ .RepoInfo.FullName }}/pulls/new/compare-branches" 18 - hx-swap="outerHTML" 19 - hx-target="#patch-upload" 20 - > 21 - compare branches 22 - </button> 2 + <div id="patch-upload"> 3 + <textarea 4 + name="patch" 5 + id="patch" 6 + rows="12" 7 + class="w-full resize-y font-mono dark:bg-gray-700 dark:text-white dark:border-gray-600" 8 + placeholder="diff --git a/file.txt b/file.txt 9 + index 1234567..abcdefg 100644 10 + --- a/file.txt 11 + +++ b/file.txt" 12 + ></textarea> 23 13 </div> 24 - {{ end }} 25 - </div> 26 14 {{ end }}
+41 -7
appview/pages/templates/repo/pulls/new.html
··· 35 35 ></textarea> 36 36 </div> 37 37 38 - {{ if not .RepoInfo.Roles.IsPushAllowed }} 39 - {{ template "fragments/pullPatchUpload" . }} 40 - {{ else }} 41 - {{ template "fragments/pullCompareBranches" . }} 42 - {{ end }} 43 38 44 - Or, select a fork to create a pull request from. 39 + <label>choose your pull strategy</label> 40 + <nav class="flex space-x-4"> 41 + <button 42 + type="button" 43 + class="px-3 py-2 pb-2 btn" 44 + hx-get="/{{ .RepoInfo.FullName }}/pulls/new/patch-upload" 45 + hx-target="#patch-strategy" 46 + hx-swap="innerHTML" 47 + > 48 + paste patch 49 + </button> 50 + 51 + {{ if .RepoInfo.Roles.IsPushAllowed }} 52 + <button 53 + type="button" 54 + class="px-3 py-2 pb-2 btn" 55 + hx-get="/{{ .RepoInfo.FullName }}/pulls/new/compare-branches" 56 + hx-target="#patch-strategy" 57 + hx-swap="innerHTML" 58 + > 59 + compare branches 60 + </button> 61 + {{ end }} 62 + <button 63 + type="button" 64 + class="px-3 py-2 pb-2 btn" 65 + hx-get="/{{ .RepoInfo.FullName }}/pulls/new/compare-forks" 66 + hx-target="#patch-strategy" 67 + hx-swap="innerHTML" 68 + > 69 + compare forks 70 + </button> 71 + </nav> 72 + 73 + <section id="patch-strategy"> 74 + {{ template "fragments/pullPatchUpload" . }} 75 + </section> 45 76 46 77 <div class="flex justify-start items-center gap-2"> 47 - <button type="submit" class="btn">create pull</button> 78 + <button type="submit" class="btn flex items-center gap-2"> 79 + {{ i "git-pull-request-create" "w-4 h-4" }} 80 + create pull 81 + </button> 48 82 </div> 49 83 50 84 </div>
+3 -6
appview/pages/templates/repo/pulls/pull.html
··· 42 42 <span> 43 43 targeting 44 44 <span class="text-xs rounded bg-gray-100 dark:bg-gray-700 text-black dark:text-white font-mono px-2 mx-1/2 inline-flex items-center"> 45 - {{ .Pull.TargetBranch }} 45 + <a href="/{{ .RepoInfo.FullName }}/tree/{{ .Pull.TargetBranch }}" class="no-underline hover:underline">{{ .Pull.TargetBranch }}</a> 46 46 </span> 47 47 </span> 48 48 {{ if not .Pull.IsPatch }} 49 49 <span>from 50 50 {{ if not .Pull.IsSameRepoBranch }} 51 - <span class="text-xs rounded bg-gray-100 dark:bg-gray-700 text-black dark:text-white font-mono px-2 mx-1/2 inline-flex items-center"> 52 - {{ .Pull.PullSource.Repo }} 53 - </span> 54 - <span class="select-none">/</span> 51 + <a href="/{{ $owner }}/{{ .PullSourceRepo.Name }}" class="no-underline hover:underline">{{ $owner }}/{{ .PullSourceRepo.Name }}</a> 55 52 {{ end }} 56 53 <span class="text-xs rounded bg-gray-100 dark:bg-gray-700 text-black dark:text-white font-mono px-2 mx-1/2 inline-flex items-center"> 57 - {{ .Pull.PullSource.Branch }} 54 + <a href="/{{ $owner }}/{{ .PullSourceRepo.Name }}/tree/{{ .Pull.PullSource.Branch }}" class="no-underline hover:underline">{{ .Pull.PullSource.Branch }}</a> 58 55 </span> 59 56 </span> 60 57 {{ end }}
+118 -7
appview/state/pull.go
··· 120 120 resubmitResult = s.resubmitCheck(f, pull) 121 121 } 122 122 123 + var pullSourceRepo *db.Repo 124 + if pull.PullSource != nil { 125 + if pull.PullSource.Repo != nil { 126 + pullSourceRepo, err = db.GetRepoByAtUri(s.db, pull.PullSource.Repo.String()) 127 + if err != nil { 128 + log.Printf("failed to get repo by at uri: %v", err) 129 + return 130 + } 131 + } 132 + } 133 + 123 134 s.pages.RepoSinglePull(w, pages.RepoSinglePullParams{ 124 - LoggedInUser: user, 125 - RepoInfo: f.RepoInfo(s, user), 126 - DidHandleMap: didHandleMap, 127 - Pull: pull, 128 - MergeCheck: mergeCheckResponse, 129 - ResubmitCheck: resubmitResult, 135 + LoggedInUser: user, 136 + RepoInfo: f.RepoInfo(s, user), 137 + DidHandleMap: didHandleMap, 138 + Pull: pull, 139 + PullSourceRepo: pullSourceRepo, 140 + MergeCheck: mergeCheckResponse, 141 + ResubmitCheck: resubmitResult, 130 142 }) 131 143 } 132 144 ··· 533 545 title := r.FormValue("title") 534 546 body := r.FormValue("body") 535 547 targetBranch := r.FormValue("targetBranch") 536 - fromFork := r.FormValue("fromFork") 548 + fromFork := r.FormValue("fork") 537 549 sourceBranch := r.FormValue("sourceBranch") 538 550 patch := r.FormValue("patch") 539 551 ··· 840 852 s.pages.PullCompareBranchesFragment(w, pages.PullCompareBranchesParams{ 841 853 RepoInfo: f.RepoInfo(s, user), 842 854 Branches: result.Branches, 855 + }) 856 + } 857 + 858 + func (s *State) CompareForksFragment(w http.ResponseWriter, r *http.Request) { 859 + user := s.auth.GetUser(r) 860 + f, err := fullyResolvedRepo(r) 861 + if err != nil { 862 + log.Println("failed to get repo and knot", err) 863 + return 864 + } 865 + 866 + forks, err := db.GetForksByDid(s.db, user.Did) 867 + if err != nil { 868 + log.Println("failed to get forks", err) 869 + return 870 + } 871 + 872 + s.pages.PullCompareForkFragment(w, pages.PullCompareForkParams{ 873 + RepoInfo: f.RepoInfo(s, user), 874 + Forks: forks, 875 + }) 876 + } 877 + 878 + func (s *State) CompareForksBranchesFragment(w http.ResponseWriter, r *http.Request) { 879 + user := s.auth.GetUser(r) 880 + 881 + f, err := fullyResolvedRepo(r) 882 + if err != nil { 883 + log.Println("failed to get repo and knot", err) 884 + return 885 + } 886 + 887 + forkVal := r.URL.Query().Get("fork") 888 + 889 + // fork repo 890 + repo, err := db.GetRepo(s.db, user.Did, forkVal) 891 + if err != nil { 892 + log.Println("failed to get repo", user.Did, forkVal) 893 + return 894 + } 895 + 896 + sourceBranchesClient, err := NewUnsignedClient(repo.Knot, s.config.Dev) 897 + if err != nil { 898 + log.Printf("failed to create unsigned client for %s", repo.Knot) 899 + s.pages.Error503(w) 900 + return 901 + } 902 + 903 + sourceResp, err := sourceBranchesClient.Branches(user.Did, repo.Name) 904 + if err != nil { 905 + log.Println("failed to reach knotserver for source branches", err) 906 + return 907 + } 908 + 909 + sourceBody, err := io.ReadAll(sourceResp.Body) 910 + if err != nil { 911 + log.Println("failed to read source response body", err) 912 + return 913 + } 914 + defer sourceResp.Body.Close() 915 + 916 + var sourceResult types.RepoBranchesResponse 917 + err = json.Unmarshal(sourceBody, &sourceResult) 918 + if err != nil { 919 + log.Println("failed to parse source branches response:", err) 920 + return 921 + } 922 + 923 + targetBranchesClient, err := NewUnsignedClient(f.Knot, s.config.Dev) 924 + if err != nil { 925 + log.Printf("failed to create unsigned client for target knot %s", f.Knot) 926 + s.pages.Error503(w) 927 + return 928 + } 929 + 930 + targetResp, err := targetBranchesClient.Branches(f.OwnerDid(), f.RepoName) 931 + if err != nil { 932 + log.Println("failed to reach knotserver for target branches", err) 933 + return 934 + } 935 + 936 + targetBody, err := io.ReadAll(targetResp.Body) 937 + if err != nil { 938 + log.Println("failed to read target response body", err) 939 + return 940 + } 941 + defer targetResp.Body.Close() 942 + 943 + var targetResult types.RepoBranchesResponse 944 + err = json.Unmarshal(targetBody, &targetResult) 945 + if err != nil { 946 + log.Println("failed to parse target branches response:", err) 947 + return 948 + } 949 + 950 + s.pages.PullCompareForkBranchesFragment(w, pages.PullCompareForkBranchesParams{ 951 + RepoInfo: f.RepoInfo(s, user), 952 + SourceBranches: sourceResult.Branches, 953 + TargetBranches: targetResult.Branches, 843 954 }) 844 955 } 845 956
+2
appview/state/router.go
··· 97 97 r.Get("/", s.NewPull) 98 98 r.Get("/patch-upload", s.PatchUploadFragment) 99 99 r.Get("/compare-branches", s.CompareBranchesFragment) 100 + r.Get("/compare-forks", s.CompareForksFragment) 101 + r.Get("/fork-branches", s.CompareForksBranchesFragment) 100 102 r.Post("/", s.NewPull) 101 103 }) 102 104