loading up the forgejo repo on tangled to test page performance
0
fork

Configure Feed

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

Merge pull request '[gitea] week 2024-34 cherry pick (gitea/main -> forgejo)' (#4998) from earl-warren/wcp/2024-34 into forgejo

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/4998
Reviewed-by: Michael Kriese <michael.kriese@gmx.de>

+273 -38
+7 -16
models/asymkey/ssh_key.go
··· 229 229 230 230 // PublicKeysAreExternallyManaged returns whether the provided KeyID represents an externally managed Key 231 231 func PublicKeysAreExternallyManaged(ctx context.Context, keys []*PublicKey) ([]bool, error) { 232 - sources := make([]*auth.Source, 0, 5) 232 + sourceCache := make(map[int64]*auth.Source, len(keys)) 233 233 externals := make([]bool, len(keys)) 234 - keyloop: 234 + 235 235 for i, key := range keys { 236 236 if key.LoginSourceID == 0 { 237 237 externals[i] = false 238 - continue keyloop 238 + continue 239 239 } 240 240 241 - var source *auth.Source 242 - 243 - sourceloop: 244 - for _, s := range sources { 245 - if s.ID == key.LoginSourceID { 246 - source = s 247 - break sourceloop 248 - } 249 - } 250 - 251 - if source == nil { 241 + source, ok := sourceCache[key.LoginSourceID] 242 + if !ok { 252 243 var err error 253 244 source, err = auth.GetSourceByID(ctx, key.LoginSourceID) 254 245 if err != nil { 255 246 if auth.IsErrSourceNotExist(err) { 256 247 externals[i] = false 257 - sources[i] = &auth.Source{ 248 + sourceCache[key.LoginSourceID] = &auth.Source{ 258 249 ID: key.LoginSourceID, 259 250 } 260 - continue keyloop 251 + continue 261 252 } 262 253 return nil, err 263 254 }
+10
models/asymkey/ssh_key_test.go
··· 12 12 "strings" 13 13 "testing" 14 14 15 + "code.gitea.io/gitea/models/db" 16 + "code.gitea.io/gitea/models/unittest" 15 17 "code.gitea.io/gitea/modules/setting" 16 18 17 19 "github.com/42wim/sshsig" ··· 501 503 t.Fatal("expected error") 502 504 } 503 505 } 506 + 507 + func Test_PublicKeysAreExternallyManaged(t *testing.T) { 508 + key1 := unittest.AssertExistsAndLoadBean(t, &PublicKey{ID: 1}) 509 + externals, err := PublicKeysAreExternallyManaged(db.DefaultContext, []*PublicKey{key1}) 510 + require.NoError(t, err) 511 + assert.Len(t, externals, 1) 512 + assert.False(t, externals[0]) 513 + }
+38 -7
models/git/lfs_lock.go
··· 6 6 import ( 7 7 "context" 8 8 "errors" 9 + "fmt" 9 10 "strings" 10 11 "time" 11 12 ··· 21 22 22 23 // LFSLock represents a git lfs lock of repository. 23 24 type LFSLock struct { 24 - ID int64 `xorm:"pk autoincr"` 25 - RepoID int64 `xorm:"INDEX NOT NULL"` 26 - OwnerID int64 `xorm:"INDEX NOT NULL"` 27 - Path string `xorm:"TEXT"` 28 - Created time.Time `xorm:"created"` 25 + ID int64 `xorm:"pk autoincr"` 26 + RepoID int64 `xorm:"INDEX NOT NULL"` 27 + OwnerID int64 `xorm:"INDEX NOT NULL"` 28 + Owner *user_model.User `xorm:"-"` 29 + Path string `xorm:"TEXT"` 30 + Created time.Time `xorm:"created"` 29 31 } 30 32 31 33 func init() { ··· 37 39 l.Path = util.PathJoinRel(l.Path) 38 40 } 39 41 42 + // LoadAttributes loads attributes of the lock. 43 + func (l *LFSLock) LoadAttributes(ctx context.Context) error { 44 + // Load owner 45 + if err := l.LoadOwner(ctx); err != nil { 46 + return fmt.Errorf("load owner: %w", err) 47 + } 48 + 49 + return nil 50 + } 51 + 52 + // LoadOwner loads owner of the lock. 53 + func (l *LFSLock) LoadOwner(ctx context.Context) error { 54 + if l.Owner != nil { 55 + return nil 56 + } 57 + 58 + owner, err := user_model.GetUserByID(ctx, l.OwnerID) 59 + if err != nil { 60 + if user_model.IsErrUserNotExist(err) { 61 + l.Owner = user_model.NewGhostUser() 62 + return nil 63 + } 64 + return err 65 + } 66 + l.Owner = owner 67 + 68 + return nil 69 + } 70 + 40 71 // CreateLFSLock creates a new lock. 41 72 func CreateLFSLock(ctx context.Context, repo *repo_model.Repository, lock *LFSLock) (*LFSLock, error) { 42 73 dbCtx, committer, err := db.TxContext(ctx) ··· 94 125 } 95 126 96 127 // GetLFSLockByRepoID returns a list of locks of repository. 97 - func GetLFSLockByRepoID(ctx context.Context, repoID int64, page, pageSize int) ([]*LFSLock, error) { 128 + func GetLFSLockByRepoID(ctx context.Context, repoID int64, page, pageSize int) (LFSLockList, error) { 98 129 e := db.GetEngine(ctx) 99 130 if page >= 0 && pageSize > 0 { 100 131 start := 0 ··· 103 134 } 104 135 e.Limit(pageSize, start) 105 136 } 106 - lfsLocks := make([]*LFSLock, 0, pageSize) 137 + lfsLocks := make(LFSLockList, 0, pageSize) 107 138 return lfsLocks, e.Find(&lfsLocks, &LFSLock{RepoID: repoID}) 108 139 } 109 140
+54
models/git/lfs_lock_list.go
··· 1 + // Copyright 2024 The Gitea Authors. All rights reserved. 2 + // SPDX-License-Identifier: MIT 3 + 4 + package git 5 + 6 + import ( 7 + "context" 8 + "fmt" 9 + 10 + "code.gitea.io/gitea/models/db" 11 + user_model "code.gitea.io/gitea/models/user" 12 + "code.gitea.io/gitea/modules/container" 13 + ) 14 + 15 + // LFSLockList is a list of LFSLock 16 + type LFSLockList []*LFSLock 17 + 18 + // LoadAttributes loads the attributes for the given locks 19 + func (locks LFSLockList) LoadAttributes(ctx context.Context) error { 20 + if len(locks) == 0 { 21 + return nil 22 + } 23 + 24 + if err := locks.LoadOwner(ctx); err != nil { 25 + return fmt.Errorf("load owner: %w", err) 26 + } 27 + 28 + return nil 29 + } 30 + 31 + // LoadOwner loads the owner of the locks 32 + func (locks LFSLockList) LoadOwner(ctx context.Context) error { 33 + if len(locks) == 0 { 34 + return nil 35 + } 36 + 37 + usersIDs := container.FilterSlice(locks, func(lock *LFSLock) (int64, bool) { 38 + return lock.OwnerID, true 39 + }) 40 + users := make(map[int64]*user_model.User, len(usersIDs)) 41 + if err := db.GetEngine(ctx). 42 + In("id", usersIDs). 43 + Find(&users); err != nil { 44 + return fmt.Errorf("find users: %w", err) 45 + } 46 + for _, v := range locks { 47 + v.Owner = users[v.OwnerID] 48 + if v.Owner == nil { // not exist 49 + v.Owner = user_model.NewGhostUser() 50 + } 51 + } 52 + 53 + return nil 54 + }
+4
release-notes/4998.md
··· 1 + fix: [commit](https://codeberg.org/forgejo/forgejo/commit/7f1db1df3ee8d620f997b8e70a40c2f48ae96c0f) Show lock owner instead of repo owner on LFS setting page. 2 + feat: [commit](https://codeberg.org/forgejo/forgejo/commit/ebfdc659d814561f8783094e2eb26738a5500e55) Render plain text file if the LFS object doesn't exist. 3 + fix: [commit](https://codeberg.org/forgejo/forgejo/commit/9e066c3cad7bb1b30e2def34bd0608aac825cf58) Fix panic of ssh public key page after deletion of auth source. 4 + fix: [commit](https://codeberg.org/forgejo/forgejo/commit/a8e25e907c66140961f28ba92403176c816dfb60) Add missing repository type filter parameters to pager.
+15
routers/web/explore/repo.go
··· 144 144 pager.AddParam(ctx, "topic", "TopicOnly") 145 145 pager.AddParam(ctx, "language", "Language") 146 146 pager.AddParamString(relevantReposOnlyParam, fmt.Sprint(opts.OnlyShowRelevant)) 147 + if archived.Has() { 148 + pager.AddParamString("archived", fmt.Sprint(archived.Value())) 149 + } 150 + if fork.Has() { 151 + pager.AddParamString("fork", fmt.Sprint(fork.Value())) 152 + } 153 + if mirror.Has() { 154 + pager.AddParamString("mirror", fmt.Sprint(mirror.Value())) 155 + } 156 + if template.Has() { 157 + pager.AddParamString("template", fmt.Sprint(template.Value())) 158 + } 159 + if private.Has() { 160 + pager.AddParamString("private", fmt.Sprint(private.Value())) 161 + } 147 162 ctx.Data["Page"] = pager 148 163 149 164 ctx.HTML(http.StatusOK, opts.TplName)
+17 -1
routers/web/org/home.go
··· 4 4 package org 5 5 6 6 import ( 7 + "fmt" 7 8 "net/http" 8 9 "path" 9 10 "strings" ··· 154 155 155 156 pager := context.NewPagination(int(count), setting.UI.User.RepoPagingNum, page, 5) 156 157 pager.SetDefaultParams(ctx) 157 - pager.AddParam(ctx, "language", "Language") 158 + pager.AddParamString("language", language) 159 + if archived.Has() { 160 + pager.AddParamString("archived", fmt.Sprint(archived.Value())) 161 + } 162 + if fork.Has() { 163 + pager.AddParamString("fork", fmt.Sprint(fork.Value())) 164 + } 165 + if mirror.Has() { 166 + pager.AddParamString("mirror", fmt.Sprint(mirror.Value())) 167 + } 168 + if template.Has() { 169 + pager.AddParamString("template", fmt.Sprint(template.Value())) 170 + } 171 + if private.Has() { 172 + pager.AddParamString("private", fmt.Sprint(private.Value())) 173 + } 158 174 ctx.Data["Page"] = pager 159 175 160 176 ctx.Data["ShowMemberAndTeamTab"] = ctx.Org.IsMember || len(members) > 0
+9 -6
routers/web/repo/pull.go
··· 1524 1524 // instead of 500. 1525 1525 1526 1526 if err := pull_service.NewPullRequest(ctx, repo, pullIssue, labelIDs, attachments, pullRequest, assigneeIDs); err != nil { 1527 - if errors.Is(err, user_model.ErrBlockedByUser) { 1527 + switch { 1528 + case errors.Is(err, user_model.ErrBlockedByUser): 1528 1529 ctx.JSONError(ctx.Tr("repo.pulls.blocked_by_user")) 1529 - return 1530 - } else if repo_model.IsErrUserDoesNotHaveAccessToRepo(err) { 1530 + case repo_model.IsErrUserDoesNotHaveAccessToRepo(err): 1531 1531 ctx.Error(http.StatusBadRequest, "UserDoesNotHaveAccessToRepo", err.Error()) 1532 - return 1533 - } else if git.IsErrPushRejected(err) { 1532 + case git.IsErrPushRejected(err): 1534 1533 pushrejErr := err.(*git.ErrPushRejected) 1535 1534 message := pushrejErr.Message 1536 1535 if len(message) == 0 { ··· 1547 1546 return 1548 1547 } 1549 1548 ctx.JSONError(flashError) 1550 - return 1549 + default: 1550 + // It's an unexpected error. 1551 + // If it happens, we should add another case to handle it. 1552 + log.Error("Unexpected error of NewPullRequest: %T %s", err, err) 1553 + ctx.ServerError("CompareAndPullRequest", err) 1551 1554 } 1552 1555 ctx.ServerError("NewPullRequest", err) 1553 1556 return
+5
routers/web/repo/setting/lfs.go
··· 95 95 ctx.ServerError("LFSLocks", err) 96 96 return 97 97 } 98 + if err := lfsLocks.LoadAttributes(ctx); err != nil { 99 + ctx.ServerError("LFSLocks", err) 100 + return 101 + } 102 + 98 103 ctx.Data["LFSLocks"] = lfsLocks 99 104 100 105 if len(lfsLocks) == 0 {
+2 -4
routers/web/repo/view.go
··· 240 240 } 241 241 242 242 meta, err := git_model.GetLFSMetaObjectByOid(ctx, repoID, pointer.Oid) 243 - if err != nil && err != git_model.ErrLFSObjectNotExist { // fallback to plain file 243 + if err != nil { // fallback to plain file 244 + log.Warn("Unable to access LFS pointer %s in repo %d: %v", pointer.Oid, repoID, err) 244 245 return buf, dataRc, &fileInfo{isTextFile, false, blob.Size(), nil, st}, nil 245 246 } 246 247 247 248 dataRc.Close() 248 - if err != nil { 249 - return nil, nil, nil, err 250 - } 251 249 252 250 dataRc, err = lfs.ReadMetaObject(pointer) 253 251 if err != nil {
+15
routers/web/user/notification.go
··· 446 446 // redirect to last page if request page is more than total pages 447 447 pager := context.NewPagination(total, setting.UI.User.RepoPagingNum, page, 5) 448 448 pager.SetDefaultParams(ctx) 449 + if archived.Has() { 450 + pager.AddParamString("archived", fmt.Sprint(archived.Value())) 451 + } 452 + if fork.Has() { 453 + pager.AddParamString("fork", fmt.Sprint(fork.Value())) 454 + } 455 + if mirror.Has() { 456 + pager.AddParamString("mirror", fmt.Sprint(mirror.Value())) 457 + } 458 + if template.Has() { 459 + pager.AddParamString("template", fmt.Sprint(template.Value())) 460 + } 461 + if private.Has() { 462 + pager.AddParamString("private", fmt.Sprint(private.Value())) 463 + } 449 464 ctx.Data["Page"] = pager 450 465 451 466 ctx.Data["Status"] = 2
+15
routers/web/user/profile.go
··· 335 335 if tab == "activity" { 336 336 pager.AddParam(ctx, "date", "Date") 337 337 } 338 + if archived.Has() { 339 + pager.AddParamString("archived", fmt.Sprint(archived.Value())) 340 + } 341 + if fork.Has() { 342 + pager.AddParamString("fork", fmt.Sprint(fork.Value())) 343 + } 344 + if mirror.Has() { 345 + pager.AddParamString("mirror", fmt.Sprint(mirror.Value())) 346 + } 347 + if template.Has() { 348 + pager.AddParamString("template", fmt.Sprint(template.Value())) 349 + } 350 + if private.Has() { 351 + pager.AddParamString("private", fmt.Sprint(private.Value())) 352 + } 338 353 ctx.Data["Page"] = pager 339 354 } 340 355
+3 -3
templates/repo/settings/lfs_locks.tmpl
··· 30 30 {{end}} 31 31 </td> 32 32 <td> 33 - <a href="{{$.Owner.HomeLink}}"> 34 - {{ctx.AvatarUtils.Avatar $.Owner}} 35 - {{$.Owner.DisplayName}} 33 + <a href="{{$lock.Owner.HomeLink}}"> 34 + {{ctx.AvatarUtils.Avatar $lock.Owner}} 35 + {{$lock.Owner.DisplayName}} 36 36 </a> 37 37 </td> 38 38 <td>{{TimeSince .Created ctx.Locale}}</td>
+2
tests/gitea-repositories-meta/user2/lfs.git/objects/30/77e1c4c8964613df72c37d14275c1eda5228a9
··· 1 + xK��OR0�0`p�� �t 2 + ��s��MQH��)I-��I+VH�LK3rS��S�,ݒԊ.-���t"U&e��23�,1'�8���A�
tests/gitea-repositories-meta/user2/lfs.git/objects/6b/bc79965141058b0026f2064dfb6d2eae3c4540

This is a binary file and will not be displayed.

tests/gitea-repositories-meta/user2/lfs.git/objects/b0/89e97ee59224e8c5676673c096ee4b6a8b9342

This is a binary file and will not be displayed.

tests/gitea-repositories-meta/user2/lfs.git/objects/e9/c32647bab825977942598c0efa415de300304b

This is a binary file and will not be displayed.

+1 -1
tests/gitea-repositories-meta/user2/lfs.git/refs/heads/master
··· 1 - 73cf03db6ece34e12bf91e8853dc58f678f2f82d 1 + e9c32647bab825977942598c0efa415de300304b
+75
tests/integration/lfs_view_test.go
··· 4 4 package integration 5 5 6 6 import ( 7 + "context" 8 + "fmt" 7 9 "net/http" 10 + "strings" 8 11 "testing" 9 12 13 + repo_model "code.gitea.io/gitea/models/repo" 14 + "code.gitea.io/gitea/models/unittest" 15 + user_model "code.gitea.io/gitea/models/user" 16 + "code.gitea.io/gitea/modules/lfs" 17 + api "code.gitea.io/gitea/modules/structs" 10 18 "code.gitea.io/gitea/tests" 11 19 12 20 "github.com/stretchr/testify/assert" 21 + "github.com/stretchr/testify/require" 13 22 ) 14 23 15 24 // check that files stored in LFS render properly in the web UI ··· 101 110 resp = session.MakeRequest(t, req, http.StatusOK) 102 111 doc := NewHTMLParser(t, resp.Body).doc 103 112 assert.Equal(t, 1, doc.Find(`.sha.label[href="/user2/lfs/commit/73cf03db6ece34e12bf91e8853dc58f678f2f82d"]`).Length(), "could not find link to commit") 113 + }) 114 + 115 + // check that an invalid lfs entry defaults to plaintext 116 + t.Run("Invalid", func(t *testing.T) { 117 + defer tests.PrintCurrentTest(t)() 118 + 119 + req := NewRequest(t, "GET", "/user2/lfs/src/branch/master/invalid") 120 + resp := session.MakeRequest(t, req, http.StatusOK) 121 + 122 + doc := NewHTMLParser(t, resp.Body).doc 123 + 124 + content := doc.Find("div.file-view").Text() 125 + assert.Contains(t, content, "oid sha256:9d178b5f15046343fd32f451df93acc2bdd9e6373be478b968e4cad6b6647351") 126 + }) 127 + } 128 + 129 + // TestLFSLockView tests the LFS lock view on settings page of repositories 130 + func TestLFSLockView(t *testing.T) { 131 + defer tests.PrepareTestEnv(t)() 132 + 133 + user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) // in org 3 134 + repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3}) // own by org 3 135 + session := loginUser(t, user2.Name) 136 + 137 + // create a lock 138 + lockPath := "test_lfs_lock_view.zip" 139 + lockID := "" 140 + { 141 + req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/%s.git/info/lfs/locks", repo3.FullName()), map[string]string{"path": lockPath}) 142 + req.Header.Set("Accept", lfs.AcceptHeader) 143 + req.Header.Set("Content-Type", lfs.MediaType) 144 + resp := session.MakeRequest(t, req, http.StatusCreated) 145 + lockResp := &api.LFSLockResponse{} 146 + DecodeJSON(t, resp, lockResp) 147 + lockID = lockResp.Lock.ID 148 + } 149 + defer func() { 150 + // release the lock 151 + req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/%s.git/info/lfs/locks/%s/unlock", repo3.FullName(), lockID), map[string]string{}) 152 + req.Header.Set("Accept", lfs.AcceptHeader) 153 + req.Header.Set("Content-Type", lfs.MediaType) 154 + session.MakeRequest(t, req, http.StatusOK) 155 + }() 156 + 157 + t.Run("owner name", func(t *testing.T) { 158 + defer tests.PrintCurrentTest(t)() 159 + 160 + // make sure the display names are different, or the test is meaningless 161 + require.NoError(t, repo3.LoadOwner(context.Background())) 162 + require.NotEqual(t, user2.DisplayName(), repo3.Owner.DisplayName()) 163 + 164 + req := NewRequest(t, "GET", fmt.Sprintf("/%s/settings/lfs/locks", repo3.FullName())) 165 + resp := session.MakeRequest(t, req, http.StatusOK) 166 + 167 + doc := NewHTMLParser(t, resp.Body).doc 168 + 169 + tr := doc.Find("table#lfs-files-locks-table tbody tr") 170 + require.Equal(t, 1, tr.Length()) 171 + 172 + td := tr.First().Find("td") 173 + require.Equal(t, 4, td.Length()) 174 + 175 + // path 176 + assert.Equal(t, lockPath, strings.TrimSpace(td.Eq(0).Text())) 177 + // owner name 178 + assert.Equal(t, user2.DisplayName(), strings.TrimSpace(td.Eq(1).Text())) 104 179 }) 105 180 }
+1
web_src/js/components/RepoActionView.vue
··· 878 878 word-break: break-all; 879 879 white-space: break-spaces; 880 880 margin-left: 10px; 881 + overflow-wrap: anywhere; 881 882 } 882 883 883 884 /* selectors here are intentionally exact to only match fullscreen */