forked from tangled.org/core
Monorepo for Tangled — https://tangled.org
1package repo 2 3import ( 4 "encoding/json" 5 "fmt" 6 "net/http" 7 "net/url" 8 "strings" 9 10 "tangled.org/core/api/tangled" 11 "tangled.org/core/appview/pages" 12 xrpcclient "tangled.org/core/appview/xrpcclient" 13 "tangled.org/core/patchutil" 14 "tangled.org/core/types" 15 16 indigoxrpc "github.com/bluesky-social/indigo/xrpc" 17 "github.com/go-chi/chi/v5" 18) 19 20func (rp *Repo) CompareNew(w http.ResponseWriter, r *http.Request) { 21 l := rp.logger.With("handler", "RepoCompareNew") 22 23 user := rp.oauth.GetUser(r) 24 f, err := rp.repoResolver.Resolve(r) 25 if err != nil { 26 l.Error("failed to get repo and knot", "err", err) 27 return 28 } 29 30 scheme := "http" 31 if !rp.config.Core.Dev { 32 scheme = "https" 33 } 34 host := fmt.Sprintf("%s://%s", scheme, f.Knot) 35 xrpcc := &indigoxrpc.Client{ 36 Host: host, 37 } 38 39 repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name) 40 branchBytes, err := tangled.RepoBranches(r.Context(), xrpcc, "", 0, repo) 41 if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil { 42 l.Error("failed to call XRPC repo.branches", "err", xrpcerr) 43 rp.pages.Error503(w) 44 return 45 } 46 47 var branchResult types.RepoBranchesResponse 48 if err := json.Unmarshal(branchBytes, &branchResult); err != nil { 49 l.Error("failed to decode XRPC branches response", "err", err) 50 rp.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.") 51 return 52 } 53 branches := branchResult.Branches 54 55 sortBranches(branches) 56 57 var defaultBranch string 58 for _, b := range branches { 59 if b.IsDefault { 60 defaultBranch = b.Name 61 } 62 } 63 64 base := defaultBranch 65 head := defaultBranch 66 67 params := r.URL.Query() 68 queryBase := params.Get("base") 69 queryHead := params.Get("head") 70 if queryBase != "" { 71 base = queryBase 72 } 73 if queryHead != "" { 74 head = queryHead 75 } 76 77 tagBytes, err := tangled.RepoTags(r.Context(), xrpcc, "", 0, repo) 78 if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil { 79 l.Error("failed to call XRPC repo.tags", "err", xrpcerr) 80 rp.pages.Error503(w) 81 return 82 } 83 84 var tags types.RepoTagsResponse 85 if err := json.Unmarshal(tagBytes, &tags); err != nil { 86 l.Error("failed to decode XRPC tags response", "err", err) 87 rp.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.") 88 return 89 } 90 91 repoinfo := f.RepoInfo(user) 92 93 rp.pages.RepoCompareNew(w, pages.RepoCompareNewParams{ 94 LoggedInUser: user, 95 RepoInfo: repoinfo, 96 Branches: branches, 97 Tags: tags.Tags, 98 Base: base, 99 Head: head, 100 }) 101} 102 103func (rp *Repo) Compare(w http.ResponseWriter, r *http.Request) { 104 l := rp.logger.With("handler", "RepoCompare") 105 106 user := rp.oauth.GetUser(r) 107 f, err := rp.repoResolver.Resolve(r) 108 if err != nil { 109 l.Error("failed to get repo and knot", "err", err) 110 return 111 } 112 113 var diffOpts types.DiffOpts 114 if d := r.URL.Query().Get("diff"); d == "split" { 115 diffOpts.Split = true 116 } 117 118 // if user is navigating to one of 119 // /compare/{base}/{head} 120 // /compare/{base}...{head} 121 base := chi.URLParam(r, "base") 122 head := chi.URLParam(r, "head") 123 if base == "" && head == "" { 124 rest := chi.URLParam(r, "*") // master...feature/xyz 125 parts := strings.SplitN(rest, "...", 2) 126 if len(parts) == 2 { 127 base = parts[0] 128 head = parts[1] 129 } 130 } 131 132 base, _ = url.PathUnescape(base) 133 head, _ = url.PathUnescape(head) 134 135 if base == "" || head == "" { 136 l.Error("invalid comparison") 137 rp.pages.Error404(w) 138 return 139 } 140 141 scheme := "http" 142 if !rp.config.Core.Dev { 143 scheme = "https" 144 } 145 host := fmt.Sprintf("%s://%s", scheme, f.Knot) 146 xrpcc := &indigoxrpc.Client{ 147 Host: host, 148 } 149 150 repo := fmt.Sprintf("%s/%s", f.OwnerDid(), f.Name) 151 152 branchBytes, err := tangled.RepoBranches(r.Context(), xrpcc, "", 0, repo) 153 if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil { 154 l.Error("failed to call XRPC repo.branches", "err", xrpcerr) 155 rp.pages.Error503(w) 156 return 157 } 158 159 var branches types.RepoBranchesResponse 160 if err := json.Unmarshal(branchBytes, &branches); err != nil { 161 l.Error("failed to decode XRPC branches response", "err", err) 162 rp.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.") 163 return 164 } 165 166 tagBytes, err := tangled.RepoTags(r.Context(), xrpcc, "", 0, repo) 167 if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil { 168 l.Error("failed to call XRPC repo.tags", "err", xrpcerr) 169 rp.pages.Error503(w) 170 return 171 } 172 173 var tags types.RepoTagsResponse 174 if err := json.Unmarshal(tagBytes, &tags); err != nil { 175 l.Error("failed to decode XRPC tags response", "err", err) 176 rp.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.") 177 return 178 } 179 180 compareBytes, err := tangled.RepoCompare(r.Context(), xrpcc, repo, base, head) 181 if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil { 182 l.Error("failed to call XRPC repo.compare", "err", xrpcerr) 183 rp.pages.Error503(w) 184 return 185 } 186 187 var formatPatch types.RepoFormatPatchResponse 188 if err := json.Unmarshal(compareBytes, &formatPatch); err != nil { 189 l.Error("failed to decode XRPC compare response", "err", err) 190 rp.pages.Notice(w, "compare-error", "Failed to produce comparison. Try again later.") 191 return 192 } 193 194 var diff types.NiceDiff 195 if formatPatch.CombinedPatchRaw != "" { 196 diff = patchutil.AsNiceDiff(formatPatch.CombinedPatchRaw, base) 197 } else { 198 diff = patchutil.AsNiceDiff(formatPatch.FormatPatchRaw, base) 199 } 200 201 repoinfo := f.RepoInfo(user) 202 203 rp.pages.RepoCompare(w, pages.RepoCompareParams{ 204 LoggedInUser: user, 205 RepoInfo: repoinfo, 206 Branches: branches.Branches, 207 Tags: tags.Tags, 208 Base: base, 209 Head: head, 210 Diff: &diff, 211 DiffOpts: diffOpts, 212 }) 213 214}