From 1dc5d2cd37621ccade19dcdeaefc3940fc2285a0 Mon Sep 17 00:00:00 2001 From: BrookJeynes Date: Tue, 13 May 2025 17:28:18 +1000 Subject: [PATCH] appview: repo: retrieve fork info on repo page load --- appview/pages/pages.go | 1 + appview/state/repo.go | 115 +++++++++++++++++++++++++++++++++++------ types/repo.go | 4 ++ 3 files changed, 103 insertions(+), 17 deletions(-) diff --git a/appview/pages/pages.go b/appview/pages/pages.go index 027af18..f74cddb 100644 --- a/appview/pages/pages.go +++ b/appview/pages/pages.go @@ -424,6 +424,7 @@ type RepoIndexParams struct { CommitsTrunc []*object.Commit TagsTrunc []*types.TagReference BranchesTrunc []types.Branch + ForkInfo ForkInfo types.RepoIndexResponse HTMLReadme template.HTML Raw bool diff --git a/appview/state/repo.go b/appview/state/repo.go index 29c583c..791f909 100644 --- a/appview/state/repo.go +++ b/appview/state/repo.go @@ -25,6 +25,7 @@ import ( "tangled.sh/tangled.sh/core/appview/pages/markup" "tangled.sh/tangled.sh/core/appview/pages/repoinfo" "tangled.sh/tangled.sh/core/appview/pagination" + "tangled.sh/tangled.sh/core/knotserver" "tangled.sh/tangled.sh/core/types" "github.com/bluesky-social/indigo/atproto/data" @@ -123,44 +124,124 @@ func (s *State) RepoIndex(w http.ResponseWriter, r *http.Request) { user := s.oauth.GetUser(r) repoInfo := f.RepoInfo(s, user) + forkInfo, err := GetForkInfo(repoInfo, s, f, us, w, user) + if err != nil { + log.Printf("Failed to fetch fork information: %v", err) + return + } + + s.pages.RepoIndexPage(w, pages.RepoIndexParams{ + LoggedInUser: user, + RepoInfo: repoInfo, + TagMap: tagMap, + RepoIndexResponse: result, + CommitsTrunc: commitsTrunc, + TagsTrunc: tagsTrunc, + ForkInfo: *forkInfo, + BranchesTrunc: branchesTrunc, + EmailToDidOrHandle: EmailToDidOrHandle(s, emails), + }) + return +} + +func GetForkInfo( + repoInfo repoinfo.RepoInfo, + s *State, + f *FullyResolvedRepo, + us *knotclient.UnsignedClient, + w http.ResponseWriter, + user *oauth.User, +) (*pages.ForkInfo, error) { + forkInfo := pages.ForkInfo{ + IsFork: repoInfo.Source != nil, + Status: pages.UpToDate, + } + secret, err := db.GetRegistrationKey(s.db, f.Knot) if err != nil { log.Printf("failed to get registration key for %s: %s", f.Knot, err) s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") } - // update the hidden tracking branch to latest + if !forkInfo.IsFork { + forkInfo.IsFork = false + return &forkInfo, nil + } + + resp, err := us.Branches(repoInfo.Source.Did, repoInfo.Source.Name) + if err != nil { + log.Println("failed to reach knotserver", err) + return nil, err + } + + body, err := io.ReadAll(resp.Body) + if err != nil { + log.Printf("Error reading forkResponse forkBody: %v", err) + return nil, err + } + + var result types.RepoBranchesResponse + err = json.Unmarshal(body, &result) + if err != nil { + log.Println("failed to parse forkResponse:", err) + return nil, err + } + + var contains = false + for _, branch := range result.Branches { + if branch.Name == f.Ref { + contains = true + break + } + } + + if contains == false { + forkInfo.Status = pages.MissingBranch + return &forkInfo, nil + } + signedClient, err := knotclient.NewSignedClient(f.Knot, secret, s.config.Core.Dev) if err != nil { log.Printf("failed to create signed client for %s: %s", f.Knot, err) - s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") + return nil, err } newHiddenRefResp, err := signedClient.NewHiddenRef(user.Did, repoInfo.Name, f.Ref, f.Ref) if err != nil || newHiddenRefResp.StatusCode != http.StatusNoContent { log.Printf("failed to update tracking branch: %s", err) - s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") + return nil, err } hiddenRef := fmt.Sprintf("hidden/%s/%s", f.Ref, f.Ref) comparison, err := us.Compare(user.Did, repoInfo.Name, f.Ref, hiddenRef) if err != nil { - log.Printf("failed to compare branches: %s", err) - s.pages.Notice(w, "resubmit-error", err.Error()) + log.Printf("failed to compare branches '%s' and '%s': %s", f.Ref, hiddenRef, err) + return nil, err } - log.Println(comparison) - s.pages.RepoIndexPage(w, pages.RepoIndexParams{ - LoggedInUser: user, - RepoInfo: repoInfo, - TagMap: tagMap, - RepoIndexResponse: result, - CommitsTrunc: commitsTrunc, - TagsTrunc: tagsTrunc, - BranchesTrunc: branchesTrunc, - EmailToDidOrHandle: EmailToDidOrHandle(s, emails), - }) - return + if len(comparison.FormatPatch) == 0 { + return &forkInfo, nil + } + + var isAncestor types.AncestorCheckResponse + forkSyncableResp, err := signedClient.RepoForkSyncable(user.Did, string(f.RepoAt), repoInfo.Name, f.Ref, hiddenRef) + if err != nil { + log.Printf("failed to check if fork is syncable: %s", err) + return nil, err + } + + if err := json.NewDecoder(forkSyncableResp.Body).Decode(&isAncestor); err != nil { + log.Printf("failed to decode 'isAncestor': %s", err) + return nil, err + } + + if isAncestor.IsAncestor { + forkInfo.Status = pages.FastForwardable + } else { + forkInfo.Status = pages.Conflict + } + + return &forkInfo, nil } func (s *State) RepoLog(w http.ResponseWriter, r *http.Request) { diff --git a/types/repo.go b/types/repo.go index 3d5dbd2..99a2692 100644 --- a/types/repo.go +++ b/types/repo.go @@ -90,3 +90,7 @@ type RepoBlobResponse struct { Lines int `json:"lines,omitempty"` SizeHint uint64 `json:"size_hint,omitempty"` } + +type AncestorCheckResponse struct { + IsAncestor bool `json:"isAncestor"` +} -- 2.51.2