forked from tangled.org/core
this repo has no description
1package repo 2 3import ( 4 "encoding/json" 5 "fmt" 6 "log" 7 "net/http" 8 "slices" 9 "sort" 10 "strings" 11 12 "tangled.sh/tangled.sh/core/appview/commitverify" 13 "tangled.sh/tangled.sh/core/appview/db" 14 "tangled.sh/tangled.sh/core/appview/oauth" 15 "tangled.sh/tangled.sh/core/appview/pages" 16 "tangled.sh/tangled.sh/core/appview/pages/repoinfo" 17 "tangled.sh/tangled.sh/core/appview/reporesolver" 18 "tangled.sh/tangled.sh/core/knotclient" 19 "tangled.sh/tangled.sh/core/types" 20 21 "github.com/go-chi/chi/v5" 22 "github.com/go-enry/go-enry/v2" 23) 24 25func (rp *Repo) RepoIndex(w http.ResponseWriter, r *http.Request) { 26 ref := chi.URLParam(r, "ref") 27 28 f, err := rp.repoResolver.Resolve(r) 29 if err != nil { 30 log.Println("failed to fully resolve repo", err) 31 return 32 } 33 34 us, err := knotclient.NewUnsignedClient(f.Knot, rp.config.Core.Dev) 35 if err != nil { 36 log.Printf("failed to create unsigned client for %s", f.Knot) 37 rp.pages.Error503(w) 38 return 39 } 40 41 result, err := us.Index(f.OwnerDid(), f.Name, ref) 42 if err != nil { 43 rp.pages.Error503(w) 44 log.Println("failed to reach knotserver", err) 45 return 46 } 47 48 tagMap := make(map[string][]string) 49 for _, tag := range result.Tags { 50 hash := tag.Hash 51 if tag.Tag != nil { 52 hash = tag.Tag.Target.String() 53 } 54 tagMap[hash] = append(tagMap[hash], tag.Name) 55 } 56 57 for _, branch := range result.Branches { 58 hash := branch.Hash 59 tagMap[hash] = append(tagMap[hash], branch.Name) 60 } 61 62 sortFiles(result.Files) 63 64 slices.SortFunc(result.Branches, func(a, b types.Branch) int { 65 if a.Name == result.Ref { 66 return -1 67 } 68 if a.IsDefault { 69 return -1 70 } 71 if b.IsDefault { 72 return 1 73 } 74 if a.Commit != nil && b.Commit != nil { 75 if a.Commit.Committer.When.Before(b.Commit.Committer.When) { 76 return 1 77 } else { 78 return -1 79 } 80 } 81 return strings.Compare(a.Name, b.Name) * -1 82 }) 83 84 commitCount := len(result.Commits) 85 branchCount := len(result.Branches) 86 tagCount := len(result.Tags) 87 fileCount := len(result.Files) 88 89 commitCount, branchCount, tagCount = balanceIndexItems(commitCount, branchCount, tagCount, fileCount) 90 commitsTrunc := result.Commits[:min(commitCount, len(result.Commits))] 91 tagsTrunc := result.Tags[:min(tagCount, len(result.Tags))] 92 branchesTrunc := result.Branches[:min(branchCount, len(result.Branches))] 93 94 emails := uniqueEmails(commitsTrunc) 95 emailToDidMap, err := db.GetEmailToDid(rp.db, emails, true) 96 if err != nil { 97 log.Println("failed to get email to did map", err) 98 } 99 100 vc, err := commitverify.GetVerifiedObjectCommits(rp.db, emailToDidMap, commitsTrunc) 101 if err != nil { 102 log.Println(err) 103 } 104 105 user := rp.oauth.GetUser(r) 106 repoInfo := f.RepoInfo(user) 107 108 secret, err := db.GetRegistrationKey(rp.db, f.Knot) 109 if err != nil { 110 log.Printf("failed to get registration key for %s: %s", f.Knot, err) 111 rp.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") 112 } 113 114 signedClient, err := knotclient.NewSignedClient(f.Knot, secret, rp.config.Core.Dev) 115 if err != nil { 116 log.Printf("failed to create signed client for %s: %s", f.Knot, err) 117 return 118 } 119 120 var forkInfo *types.ForkInfo 121 if user != nil && (repoInfo.Roles.IsOwner() || repoInfo.Roles.IsCollaborator()) { 122 forkInfo, err = getForkInfo(repoInfo, rp, f, result.Ref, user, signedClient) 123 if err != nil { 124 log.Printf("Failed to fetch fork information: %v", err) 125 return 126 } 127 } 128 129 // TODO: a bit dirty 130 languageInfo, err := rp.getLanguageInfo(f, signedClient, result.Ref, ref == "") 131 if err != nil { 132 log.Printf("failed to compute language percentages: %s", err) 133 // non-fatal 134 } 135 136 var shas []string 137 for _, c := range commitsTrunc { 138 shas = append(shas, c.Hash.String()) 139 } 140 pipelines, err := getPipelineStatuses(rp.db, repoInfo, shas) 141 if err != nil { 142 log.Printf("failed to fetch pipeline statuses: %s", err) 143 // non-fatal 144 } 145 146 rp.pages.RepoIndexPage(w, pages.RepoIndexParams{ 147 LoggedInUser: user, 148 RepoInfo: repoInfo, 149 TagMap: tagMap, 150 RepoIndexResponse: *result, 151 CommitsTrunc: commitsTrunc, 152 TagsTrunc: tagsTrunc, 153 ForkInfo: forkInfo, 154 BranchesTrunc: branchesTrunc, 155 EmailToDidOrHandle: emailToDidOrHandle(rp, emailToDidMap), 156 VerifiedCommits: vc, 157 Languages: languageInfo, 158 Pipelines: pipelines, 159 }) 160} 161 162func (rp *Repo) getLanguageInfo( 163 f *reporesolver.ResolvedRepo, 164 signedClient *knotclient.SignedClient, 165 currentRef string, 166 isDefaultRef bool, 167) ([]types.RepoLanguageDetails, error) { 168 // first attempt to fetch from db 169 langs, err := db.GetRepoLanguages( 170 rp.db, 171 db.FilterEq("repo_at", f.RepoAt()), 172 db.FilterEq("ref", currentRef), 173 ) 174 175 if err != nil || langs == nil { 176 // non-fatal, fetch langs from ks 177 ls, err := signedClient.RepoLanguages(f.OwnerDid(), f.Name, currentRef) 178 if err != nil { 179 return nil, err 180 } 181 if ls == nil { 182 return nil, nil 183 } 184 185 for l, s := range ls.Languages { 186 langs = append(langs, db.RepoLanguage{ 187 RepoAt: f.RepoAt(), 188 Ref: currentRef, 189 IsDefaultRef: isDefaultRef, 190 Language: l, 191 Bytes: s, 192 }) 193 } 194 195 // update appview's cache 196 err = db.InsertRepoLanguages(rp.db, langs) 197 if err != nil { 198 // non-fatal 199 log.Println("failed to cache lang results", err) 200 } 201 } 202 203 var total int64 204 for _, l := range langs { 205 total += l.Bytes 206 } 207 208 var languageStats []types.RepoLanguageDetails 209 for _, l := range langs { 210 percentage := float32(l.Bytes) / float32(total) * 100 211 color := enry.GetColor(l.Language) 212 languageStats = append(languageStats, types.RepoLanguageDetails{ 213 Name: l.Language, 214 Percentage: percentage, 215 Color: color, 216 }) 217 } 218 219 sort.Slice(languageStats, func(i, j int) bool { 220 if languageStats[i].Name == enry.OtherLanguage { 221 return false 222 } 223 if languageStats[j].Name == enry.OtherLanguage { 224 return true 225 } 226 if languageStats[i].Percentage != languageStats[j].Percentage { 227 return languageStats[i].Percentage > languageStats[j].Percentage 228 } 229 return languageStats[i].Name < languageStats[j].Name 230 }) 231 232 return languageStats, nil 233} 234 235func getForkInfo( 236 repoInfo repoinfo.RepoInfo, 237 rp *Repo, 238 f *reporesolver.ResolvedRepo, 239 currentRef string, 240 user *oauth.User, 241 signedClient *knotclient.SignedClient, 242) (*types.ForkInfo, error) { 243 if user == nil { 244 return nil, nil 245 } 246 247 forkInfo := types.ForkInfo{ 248 IsFork: repoInfo.Source != nil, 249 Status: types.UpToDate, 250 } 251 252 if !forkInfo.IsFork { 253 forkInfo.IsFork = false 254 return &forkInfo, nil 255 } 256 257 us, err := knotclient.NewUnsignedClient(repoInfo.Source.Knot, rp.config.Core.Dev) 258 if err != nil { 259 log.Printf("failed to create unsigned client for %s", repoInfo.Source.Knot) 260 return nil, err 261 } 262 263 result, err := us.Branches(repoInfo.Source.Did, repoInfo.Source.Name) 264 if err != nil { 265 log.Println("failed to reach knotserver", err) 266 return nil, err 267 } 268 269 if !slices.ContainsFunc(result.Branches, func(branch types.Branch) bool { 270 return branch.Name == currentRef 271 }) { 272 forkInfo.Status = types.MissingBranch 273 return &forkInfo, nil 274 } 275 276 newHiddenRefResp, err := signedClient.NewHiddenRef(user.Did, repoInfo.Name, currentRef, currentRef) 277 if err != nil || newHiddenRefResp.StatusCode != http.StatusNoContent { 278 log.Printf("failed to update tracking branch: %s", err) 279 return nil, err 280 } 281 282 hiddenRef := fmt.Sprintf("hidden/%s/%s", currentRef, currentRef) 283 284 var status types.AncestorCheckResponse 285 forkSyncableResp, err := signedClient.RepoForkAheadBehind(user.Did, string(f.RepoAt()), repoInfo.Name, currentRef, hiddenRef) 286 if err != nil { 287 log.Printf("failed to check if fork is ahead/behind: %s", err) 288 return nil, err 289 } 290 291 if err := json.NewDecoder(forkSyncableResp.Body).Decode(&status); err != nil { 292 log.Printf("failed to decode fork status: %s", err) 293 return nil, err 294 } 295 296 forkInfo.Status = status.Status 297 return &forkInfo, nil 298}