forked from tangled.org/core
Monorepo for Tangled — https://tangled.org
at test-ci 5.9 kB view raw
1package repo 2 3import ( 4 "encoding/json" 5 "fmt" 6 "log" 7 "net/http" 8 "slices" 9 "strings" 10 11 "tangled.sh/tangled.sh/core/appview/commitverify" 12 "tangled.sh/tangled.sh/core/appview/db" 13 "tangled.sh/tangled.sh/core/appview/oauth" 14 "tangled.sh/tangled.sh/core/appview/pages" 15 "tangled.sh/tangled.sh/core/appview/pages/repoinfo" 16 "tangled.sh/tangled.sh/core/appview/reporesolver" 17 "tangled.sh/tangled.sh/core/knotclient" 18 "tangled.sh/tangled.sh/core/types" 19 20 "github.com/go-chi/chi/v5" 21) 22 23func (rp *Repo) RepoIndex(w http.ResponseWriter, r *http.Request) { 24 ref := chi.URLParam(r, "ref") 25 f, err := rp.repoResolver.Resolve(r) 26 if err != nil { 27 log.Println("failed to fully resolve repo", err) 28 return 29 } 30 31 us, err := knotclient.NewUnsignedClient(f.Knot, rp.config.Core.Dev) 32 if err != nil { 33 log.Printf("failed to create unsigned client for %s", f.Knot) 34 rp.pages.Error503(w) 35 return 36 } 37 38 result, err := us.Index(f.OwnerDid(), f.RepoName, ref) 39 if err != nil { 40 rp.pages.Error503(w) 41 log.Println("failed to reach knotserver", err) 42 return 43 } 44 45 tagMap := make(map[string][]string) 46 for _, tag := range result.Tags { 47 hash := tag.Hash 48 if tag.Tag != nil { 49 hash = tag.Tag.Target.String() 50 } 51 tagMap[hash] = append(tagMap[hash], tag.Name) 52 } 53 54 for _, branch := range result.Branches { 55 hash := branch.Hash 56 tagMap[hash] = append(tagMap[hash], branch.Name) 57 } 58 59 slices.SortFunc(result.Branches, func(a, b types.Branch) int { 60 if a.Name == result.Ref { 61 return -1 62 } 63 if a.IsDefault { 64 return -1 65 } 66 if b.IsDefault { 67 return 1 68 } 69 if a.Commit != nil && b.Commit != nil { 70 if a.Commit.Committer.When.Before(b.Commit.Committer.When) { 71 return 1 72 } else { 73 return -1 74 } 75 } 76 return strings.Compare(a.Name, b.Name) * -1 77 }) 78 79 commitCount := len(result.Commits) 80 branchCount := len(result.Branches) 81 tagCount := len(result.Tags) 82 fileCount := len(result.Files) 83 84 commitCount, branchCount, tagCount = balanceIndexItems(commitCount, branchCount, tagCount, fileCount) 85 commitsTrunc := result.Commits[:min(commitCount, len(result.Commits))] 86 tagsTrunc := result.Tags[:min(tagCount, len(result.Tags))] 87 branchesTrunc := result.Branches[:min(branchCount, len(result.Branches))] 88 89 emails := uniqueEmails(commitsTrunc) 90 emailToDidMap, err := db.GetEmailToDid(rp.db, emails, true) 91 if err != nil { 92 log.Println("failed to get email to did map", err) 93 } 94 95 vc, err := commitverify.GetVerifiedObjectCommits(rp.db, emailToDidMap, commitsTrunc) 96 if err != nil { 97 log.Println(err) 98 } 99 100 user := rp.oauth.GetUser(r) 101 repoInfo := f.RepoInfo(user) 102 103 secret, err := db.GetRegistrationKey(rp.db, f.Knot) 104 if err != nil { 105 log.Printf("failed to get registration key for %s: %s", f.Knot, err) 106 rp.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") 107 } 108 109 signedClient, err := knotclient.NewSignedClient(f.Knot, secret, rp.config.Core.Dev) 110 if err != nil { 111 log.Printf("failed to create signed client for %s: %s", f.Knot, err) 112 return 113 } 114 115 var forkInfo *types.ForkInfo 116 if user != nil && (repoInfo.Roles.IsOwner() || repoInfo.Roles.IsCollaborator()) { 117 forkInfo, err = getForkInfo(repoInfo, rp, f, user, signedClient) 118 if err != nil { 119 log.Printf("Failed to fetch fork information: %v", err) 120 return 121 } 122 } 123 124 repoLanguages, err := signedClient.RepoLanguages(f.OwnerDid(), f.RepoName, ref) 125 if err != nil { 126 log.Printf("failed to compute language percentages: %s", err) 127 // non-fatal 128 } 129 130 var shas []string 131 for _, c := range commitsTrunc { 132 shas = append(shas, c.Hash.String()) 133 } 134 pipelines, err := rp.getPipelineStatuses(repoInfo, shas) 135 if err != nil { 136 log.Printf("failed to fetch pipeline statuses: %s", err) 137 // non-fatal 138 } 139 140 rp.pages.RepoIndexPage(w, pages.RepoIndexParams{ 141 LoggedInUser: user, 142 RepoInfo: repoInfo, 143 TagMap: tagMap, 144 RepoIndexResponse: *result, 145 CommitsTrunc: commitsTrunc, 146 TagsTrunc: tagsTrunc, 147 ForkInfo: forkInfo, 148 BranchesTrunc: branchesTrunc, 149 EmailToDidOrHandle: emailToDidOrHandle(rp, emailToDidMap), 150 VerifiedCommits: vc, 151 Languages: repoLanguages, 152 Pipelines: pipelines, 153 }) 154 return 155} 156 157func getForkInfo( 158 repoInfo repoinfo.RepoInfo, 159 rp *Repo, 160 f *reporesolver.ResolvedRepo, 161 user *oauth.User, 162 signedClient *knotclient.SignedClient, 163) (*types.ForkInfo, error) { 164 if user == nil { 165 return nil, nil 166 } 167 168 forkInfo := types.ForkInfo{ 169 IsFork: repoInfo.Source != nil, 170 Status: types.UpToDate, 171 } 172 173 if !forkInfo.IsFork { 174 forkInfo.IsFork = false 175 return &forkInfo, nil 176 } 177 178 us, err := knotclient.NewUnsignedClient(repoInfo.Source.Knot, rp.config.Core.Dev) 179 if err != nil { 180 log.Printf("failed to create unsigned client for %s", repoInfo.Source.Knot) 181 return nil, err 182 } 183 184 result, err := us.Branches(repoInfo.Source.Did, repoInfo.Source.Name) 185 if err != nil { 186 log.Println("failed to reach knotserver", err) 187 return nil, err 188 } 189 190 if !slices.ContainsFunc(result.Branches, func(branch types.Branch) bool { 191 return branch.Name == f.Ref 192 }) { 193 forkInfo.Status = types.MissingBranch 194 return &forkInfo, nil 195 } 196 197 newHiddenRefResp, err := signedClient.NewHiddenRef(user.Did, repoInfo.Name, f.Ref, f.Ref) 198 if err != nil || newHiddenRefResp.StatusCode != http.StatusNoContent { 199 log.Printf("failed to update tracking branch: %s", err) 200 return nil, err 201 } 202 203 hiddenRef := fmt.Sprintf("hidden/%s/%s", f.Ref, f.Ref) 204 205 var status types.AncestorCheckResponse 206 forkSyncableResp, err := signedClient.RepoForkAheadBehind(user.Did, string(f.RepoAt), repoInfo.Name, f.Ref, hiddenRef) 207 if err != nil { 208 log.Printf("failed to check if fork is ahead/behind: %s", err) 209 return nil, err 210 } 211 212 if err := json.NewDecoder(forkSyncableResp.Body).Decode(&status); err != nil { 213 log.Printf("failed to decode fork status: %s", err) 214 return nil, err 215 } 216 217 forkInfo.Status = status.Status 218 return &forkInfo, nil 219}