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