This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

core / appview / pulls / pull2.go
8.0 kB 280 lines
1package pulls 2 3import ( 4 "context" 5 "fmt" 6 "net/http" 7 "strconv" 8 9 "github.com/bluesky-social/indigo/atproto/syntax" 10 "github.com/bluesky-social/indigo/lex/util" 11 indigoxrpc "github.com/bluesky-social/indigo/xrpc" 12 "github.com/go-chi/chi/v5" 13 "golang.org/x/sync/errgroup" 14 "tangled.org/core/api/tangled" 15 "tangled.org/core/appview/models" 16 "tangled.org/core/appview/pages" 17 "tangled.org/core/types" 18) 19 20// NOTE: parsing object in middleware is bad pattern 21// you will have to check if object exist in context "just in case" 22// so it's better to make helper function that can read the url pattern instead. 23 24 25// A -- B -- C 26// (master) (pr/123/0) 27// 28// A -- B -- C 29// \ (pr/123/0) 30// `-- D <- B' <- C' 31// (master) (pr/123/1) 32 33// 1. rebase B<-C to D 34// 2. compare tree of C and D 35 36// PullInterDiff is router for /pulls/{pull}/{version}..{version}/{change} 37// 38// Examples: 39// - /pulls/123/0..2/all 40// - /pulls/123/0..2/nrpytyzw 41func (s *Pulls) PullInterDiff(w http.ResponseWriter, r *http.Request) { 42 l := s.logger.With("handler", "PullRound") 43 ctx := r.Context() 44 45 pull, ok := r.Context().Value("pull").(*models.Pull) 46 if !ok { 47 l.Error("failed to get pull") 48 s.pages.Error500(w) 49 return 50 } 51 52 var ( 53 version1 = 0 54 version2 = 0 55 changeId = chi.URLParam(r, "*") 56 ) 57 if changeId == "all" { 58 changeId = "" 59 } 60 61 // defer render 62 var params pages.PullInterdiffParams 63 params.Pull = pull 64 params.Version1 = version1 65 params.Version2 = version2 66 params.ChangeId = changeId 67 defer s.pages.PullInterdiff(w, params) 68 69 // 1. resolve target branch -> (branch, commit) 70 xrpcc := &indigoxrpc.Client{Host: s.config.KnotMirror.Url} 71 branch, err := tangled.GitTempGetBranch(ctx, xrpcc, pull.TargetBranch, pull.RepoDid.String()) 72 if err != nil { 73 panic("unimplemented") 74 } 75 76 base := branch.Hash 77 head1 := "" // pull.Versions[version1].Head 78 head2 := "" // pull.Versions[version2].Head 79 80 // 1. log commits from base..head1 and base..head2 81 var commits1, commits2 []types.Commit 82 g, gctx := errgroup.WithContext(ctx) 83 g.Go(func() error { 84 commits1, err = getTempListCommits(gctx, xrpcc, pull.SourceRepoDid(), base, head1) 85 return err 86 }) 87 g.Go(func() error { 88 commits2, err = getTempListCommits(gctx, xrpcc, pull.SourceRepoDid(), base, head2) 89 return err 90 }) 91 if err := g.Wait(); err != nil { 92 params.ErrorMsg = "something something" 93 panic("unimplemented") 94 } 95 96 if changeId != "" { 97 // interdiff by change-id 98 var old, new *types.Commit 99 for _, commit := range commits1 { 100 if commit.ChangeId == changeId { 101 old = &commit 102 break 103 } 104 } 105 for _, commit := range commits2 { 106 if commit.ChangeId == changeId { 107 new = &commit 108 break 109 } 110 } 111 _, _ = old, new 112 panic("unimplemented") 113 } else { 114 // interdiff of two commit ranges 115 panic("unimplemented") 116 } 117} 118 119// PullDiff is router for /pulls/{pull}/{version}/{commit}..{commit} 120// 121// Examples: 122// - /pulls/123/latest 123// - /pulls/123/2/head 124// - /pulls/123/2/base..head 125// - /pulls/123/2/a53ab251e..d8add468c 126// - /pulls/123/2/d8add468c 127func (s *Pulls) PullDiff(w http.ResponseWriter, r *http.Request) { 128 l := s.logger.With("handler", "PullRound") 129 ctx := r.Context() 130 131 pull, ok := r.Context().Value("pull").(*models.Pull) 132 if !ok { 133 l.Error("failed to get pull") 134 s.pages.Error500(w) 135 return 136 } 137 138 var err error 139 140 var version int 141 var versionRaw = chi.URLParam(r, "version") 142 if versionRaw == "latest" { 143 version = pull.LastRoundNumber() 144 } else { 145 version, err = strconv.Atoi(versionRaw) 146 if err != nil { 147 // invalid version number. redirect 148 http.Redirect(w, r, 149 fmt.Sprintf("/%s/pulls/%d/latest", pull.Repo.RepoIdentifier(), pull.ID), 150 http.StatusSeeOther, 151 ) 152 return 153 } 154 } 155 156 var range_ = chi.URLParam(r, "*") 157 base, head, err := parseRevRange(range_) 158 if err != nil { 159 http.Redirect(w, r, 160 fmt.Sprintf("/%s/pulls/%d/%s", pull.Repo.RepoIdentifier(), pull.ID, versionRaw), 161 http.StatusSeeOther, 162 ) 163 return 164 } 165 166 // defer render 167 var params pages.PullDiffParams 168 params.Pull = pull 169 params.Version = version 170 defer s.pages.PullDiff(w, params) 171 172 // 1. resolve target branch -> (branch, commit) 173 xrpcc := &indigoxrpc.Client{Host: s.config.KnotMirror.Url} 174 branch, err := tangled.GitTempGetBranch(ctx, xrpcc, pull.TargetBranch, pull.RepoDid.String()) 175 if err != nil { 176 l.Warn("Failed to resolve target branch", "branch", pull.TargetBranch, "err", err) 177 params.ErrorMsg = fmt.Sprintf("Failed to resolve target branch %q", pull.TargetBranch) 178 return 179 } 180 181 if base == "base" { 182 base = branch.Hash 183 } 184 if head == "head" { 185 head = pull.HEAD() 186 } 187 188 sourceRepoDid := pull.SourceRepoDid() 189 190 // 2. list diverged commits using knotmirror (BASE..HEAD) -> ([]commit) 191 // - knotmirror needs on-demand fetch implementation for this 192 commits, err := getTempListCommits(ctx, xrpcc, sourceRepoDid, base, head) 193 if err != nil { 194 panic("unimplemented") 195 } 196 197 // 3. list every commits in UI. They will be lazy-loaded 198 params.Commits = commits 199} 200 201// htmx fragment. render diff between commits 202func (s *Pulls) PullDiffFragment(w http.ResponseWriter, r *http.Request) { 203 // var ( 204 // base = r.URL.Query().Get("base") // base commit ID 205 // head = r.URL.Query().Get("head") // head commit ID 206 // unified = r.URL.Query().Get("view") == "unified" 207 // ) 208 209 // 1. get commit object 210 // 2. get diff between parent..commit (knotmirror), parse that diff 211 // 3. fetch each file entries (& run syntax highlight) <- skip this part for stage 1. we will do this at stage 2. 212 // 4. render diff 213} 214 215// htmx fragment. render interdiff between changes 216func (s *Pulls) PullInterdiffFragment(w http.ResponseWriter, r *http.Request) { 217 // var ( 218 // base1 = r.URL.Query().Get("base1") // base1 commit ID 219 // base2 = r.URL.Query().Get("base2") // base2 commit ID 220 // head1 = r.URL.Query().Get("head1") // head1 commit ID 221 // head2 = r.URL.Query().Get("head2") // head1 commit ID 222 // unified = r.URL.Query().Get("view") == "unified" 223 // ) 224 225 // 1. compute interdiff. (knotmirror) 226 // 2. return rich diff data. (knotmirror) 227 // 3. load old/new blobs & run syntax highlight 228 // 4. render diff 229} 230 231// parseRevRange parses <head>..<base> string. 232// base and head will default to "base" and "head" when omitted. 233func parseRevRange(range_ string) (base string, head string, err error) { 234 panic("unimplemented") 235} 236 237// parseVersionRange parses <version>..<version> string. 238// Each versions will default to "base" and "latest" when omitted. 239func parseVersionRange(range_ string) (base string, head string, err error) { 240 panic("unimplemented") 241} 242 243func getTempListCommits(ctx context.Context, xrpcc util.LexClient, repo syntax.DID, base, head string) ([]types.Commit, error) { 244 panic("unimplemented") 245 // raw, err := tangled.GitTempListCommits(ctx, xrpcc, "", 1000, head, repo.String()) 246 // if err != nil { 247 // return nil, err 248 // } 249 // 250 // var xrpcResp types.RepoLogResponse 251 // if err := json.Unmarshal(raw, &xrpcResp); err != nil { 252 // return nil, fmt.Errorf("failed to decode XRPC response: %w", err) 253 // } 254 // 255 // return xrpcResp.Commits, nil 256} 257 258// htmx fragment. render interdiff between commits 259func (s *Pulls) PullInterDiffFragment(w http.ResponseWriter, r *http.Request) { 260 panic("unimplemented") 261} 262 263// gitmirror 264// - git.ListCommitsSinceMergeBase(repo, base, head) 265// - git.Diff(repo, base, head, mode) 266// - git.Interdiff(repo, 267 268// for interdiff, we want: from{start,end}, to{start,end} 269// 1. squash from.start ~ from.end into one commit 270// 2. rebase that commit to to.start.parent() 271// 3. diff from_squashed.tree and to.end.tree 272 273// we want git log BASE..HEAD (only commits in HEAD) diverged=false 274// and git diff BASE...HEAD (changes from HEAD since merge-base) absolute=false 275 276// commands.go:56 picks comparison type: 277// - diff BASE..HEAD (COMPARISON_TYPE_ONLY_IN_HEAD) = direct 278// - diff BASE...HEAD (COMPARISON_TYPE_INTERSECTION) = merge-base. Server resolves merge-base via g.MergeBase() first (diff.go:43) then diffs. 279// we want second one. we should compute merge-base first. 280// we can have