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.

move repository deletion to service layer (#26948)

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>

authored by

Lunny Xiao
wxiaoguang
and committed by
GitHub
4f32abaf 3c0c2796

+567 -476
+1
models/activities/main_test.go
··· 10 10 "code.gitea.io/gitea/models/unittest" 11 11 12 12 _ "code.gitea.io/gitea/models" 13 + _ "code.gitea.io/gitea/models/actions" 13 14 ) 14 15 15 16 func TestMain(m *testing.M) {
+1
models/auth/main_test.go
··· 10 10 "code.gitea.io/gitea/models/unittest" 11 11 12 12 _ "code.gitea.io/gitea/models" 13 + _ "code.gitea.io/gitea/models/actions" 13 14 _ "code.gitea.io/gitea/models/activities" 14 15 _ "code.gitea.io/gitea/models/auth" 15 16 _ "code.gitea.io/gitea/models/perm/access"
+2
models/git/main_test.go
··· 10 10 "code.gitea.io/gitea/models/unittest" 11 11 12 12 _ "code.gitea.io/gitea/models" 13 + _ "code.gitea.io/gitea/models/actions" 14 + _ "code.gitea.io/gitea/models/activities" 13 15 ) 14 16 15 17 func TestMain(m *testing.M) {
+2
models/issues/main_test.go
··· 11 11 "code.gitea.io/gitea/models/unittest" 12 12 13 13 _ "code.gitea.io/gitea/models" 14 + _ "code.gitea.io/gitea/models/actions" 15 + _ "code.gitea.io/gitea/models/activities" 14 16 _ "code.gitea.io/gitea/models/repo" 15 17 _ "code.gitea.io/gitea/models/user" 16 18
+1
models/main_test.go
··· 12 12 "code.gitea.io/gitea/models/unittest" 13 13 user_model "code.gitea.io/gitea/models/user" 14 14 15 + _ "code.gitea.io/gitea/models/actions" 15 16 _ "code.gitea.io/gitea/models/system" 16 17 17 18 "github.com/stretchr/testify/assert"
-79
models/org_team.go
··· 151 151 return nil 152 152 } 153 153 154 - // HasRepository returns true if given repository belong to team. 155 - func HasRepository(t *organization.Team, repoID int64) bool { 156 - return organization.HasTeamRepo(db.DefaultContext, t.OrgID, t.ID, repoID) 157 - } 158 - 159 - // removeRepository removes a repository from a team and recalculates access 160 - // Note: Repository shall not be removed from team if it includes all repositories (unless the repository is deleted) 161 - func removeRepository(ctx context.Context, t *organization.Team, repo *repo_model.Repository, recalculate bool) (err error) { 162 - e := db.GetEngine(ctx) 163 - if err = organization.RemoveTeamRepo(ctx, t.ID, repo.ID); err != nil { 164 - return err 165 - } 166 - 167 - t.NumRepos-- 168 - if _, err = e.ID(t.ID).Cols("num_repos").Update(t); err != nil { 169 - return err 170 - } 171 - 172 - // Don't need to recalculate when delete a repository from organization. 173 - if recalculate { 174 - if err = access_model.RecalculateTeamAccesses(ctx, repo, t.ID); err != nil { 175 - return err 176 - } 177 - } 178 - 179 - teamUsers, err := organization.GetTeamUsersByTeamID(ctx, t.ID) 180 - if err != nil { 181 - return fmt.Errorf("getTeamUsersByTeamID: %w", err) 182 - } 183 - for _, teamUser := range teamUsers { 184 - has, err := access_model.HasAccess(ctx, teamUser.UID, repo) 185 - if err != nil { 186 - return err 187 - } else if has { 188 - continue 189 - } 190 - 191 - if err = repo_model.WatchRepo(ctx, teamUser.UID, repo.ID, false); err != nil { 192 - return err 193 - } 194 - 195 - // Remove all IssueWatches a user has subscribed to in the repositories 196 - if err := issues_model.RemoveIssueWatchersByRepoID(ctx, teamUser.UID, repo.ID); err != nil { 197 - return err 198 - } 199 - } 200 - 201 - return nil 202 - } 203 - 204 - // RemoveRepository removes repository from team of organization. 205 - // If the team shall include all repositories the request is ignored. 206 - func RemoveRepository(t *organization.Team, repoID int64) error { 207 - if !HasRepository(t, repoID) { 208 - return nil 209 - } 210 - 211 - if t.IncludesAllRepositories { 212 - return nil 213 - } 214 - 215 - repo, err := repo_model.GetRepositoryByID(db.DefaultContext, repoID) 216 - if err != nil { 217 - return err 218 - } 219 - 220 - ctx, committer, err := db.TxContext(db.DefaultContext) 221 - if err != nil { 222 - return err 223 - } 224 - defer committer.Close() 225 - 226 - if err = removeRepository(ctx, t, repo, true); err != nil { 227 - return err 228 - } 229 - 230 - return committer.Commit() 231 - } 232 - 233 154 // NewTeam creates a record of new team. 234 155 // It's caller's responsibility to assign organization ID. 235 156 func NewTeam(t *organization.Team) (err error) {
-30
models/org_team_test.go
··· 51 51 assert.True(t, organization.IsErrLastOrgOwner(err)) 52 52 } 53 53 54 - func TestTeam_HasRepository(t *testing.T) { 55 - assert.NoError(t, unittest.PrepareTestDatabase()) 56 - 57 - test := func(teamID, repoID int64, expected bool) { 58 - team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamID}) 59 - assert.Equal(t, expected, HasRepository(team, repoID)) 60 - } 61 - test(1, 1, false) 62 - test(1, 3, true) 63 - test(1, 5, true) 64 - test(1, unittest.NonexistentID, false) 65 - 66 - test(2, 3, true) 67 - test(2, 5, false) 68 - } 69 - 70 - func TestTeam_RemoveRepository(t *testing.T) { 71 - assert.NoError(t, unittest.PrepareTestDatabase()) 72 - 73 - testSuccess := func(teamID, repoID int64) { 74 - team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamID}) 75 - assert.NoError(t, RemoveRepository(team, repoID)) 76 - unittest.AssertNotExistsBean(t, &organization.TeamRepo{TeamID: teamID, RepoID: repoID}) 77 - unittest.CheckConsistencyFor(t, &organization.Team{ID: teamID}, &repo_model.Repository{ID: repoID}) 78 - } 79 - testSuccess(2, 3) 80 - testSuccess(2, 5) 81 - testSuccess(1, unittest.NonexistentID) 82 - } 83 - 84 54 func TestIsUsableTeamName(t *testing.T) { 85 55 assert.NoError(t, organization.IsUsableTeamName("usable")) 86 56 assert.True(t, db.IsErrNameReserved(organization.IsUsableTeamName("new")))
+2
models/organization/main_test.go
··· 10 10 "code.gitea.io/gitea/models/unittest" 11 11 12 12 _ "code.gitea.io/gitea/models" 13 + _ "code.gitea.io/gitea/models/actions" 14 + _ "code.gitea.io/gitea/models/activities" 13 15 _ "code.gitea.io/gitea/models/organization" 14 16 _ "code.gitea.io/gitea/models/repo" 15 17 _ "code.gitea.io/gitea/models/user"
+2
models/packages/package_test.go
··· 13 13 user_model "code.gitea.io/gitea/models/user" 14 14 15 15 _ "code.gitea.io/gitea/models" 16 + _ "code.gitea.io/gitea/models/actions" 17 + _ "code.gitea.io/gitea/models/activities" 16 18 17 19 "github.com/stretchr/testify/assert" 18 20 )
+2
models/perm/access/main_test.go
··· 10 10 "code.gitea.io/gitea/models/unittest" 11 11 12 12 _ "code.gitea.io/gitea/models" 13 + _ "code.gitea.io/gitea/models/actions" 14 + _ "code.gitea.io/gitea/models/activities" 13 15 _ "code.gitea.io/gitea/models/repo" 14 16 _ "code.gitea.io/gitea/models/user" 15 17 )
-326
models/repo.go
··· 11 11 12 12 _ "image/jpeg" // Needed for jpeg support 13 13 14 - actions_model "code.gitea.io/gitea/models/actions" 15 - activities_model "code.gitea.io/gitea/models/activities" 16 - admin_model "code.gitea.io/gitea/models/admin" 17 14 asymkey_model "code.gitea.io/gitea/models/asymkey" 18 15 "code.gitea.io/gitea/models/db" 19 - git_model "code.gitea.io/gitea/models/git" 20 16 issues_model "code.gitea.io/gitea/models/issues" 21 - "code.gitea.io/gitea/models/organization" 22 17 access_model "code.gitea.io/gitea/models/perm/access" 23 - project_model "code.gitea.io/gitea/models/project" 24 18 repo_model "code.gitea.io/gitea/models/repo" 25 - secret_model "code.gitea.io/gitea/models/secret" 26 19 system_model "code.gitea.io/gitea/models/system" 27 20 "code.gitea.io/gitea/models/unit" 28 21 user_model "code.gitea.io/gitea/models/user" 29 - "code.gitea.io/gitea/models/webhook" 30 - actions_module "code.gitea.io/gitea/modules/actions" 31 - "code.gitea.io/gitea/modules/lfs" 32 22 "code.gitea.io/gitea/modules/log" 33 - "code.gitea.io/gitea/modules/storage" 34 - 35 - "xorm.io/builder" 36 23 ) 37 24 38 25 // Init initialize model ··· 41 28 return err 42 29 } 43 30 return system_model.Init(ctx) 44 - } 45 - 46 - // DeleteRepository deletes a repository for a user or organization. 47 - // make sure if you call this func to close open sessions (sqlite will otherwise get a deadlock) 48 - func DeleteRepository(doer *user_model.User, uid, repoID int64) error { 49 - ctx, committer, err := db.TxContext(db.DefaultContext) 50 - if err != nil { 51 - return err 52 - } 53 - defer committer.Close() 54 - sess := db.GetEngine(ctx) 55 - 56 - // Query the action tasks of this repo, they will be needed after they have been deleted to remove the logs 57 - tasks, err := actions_model.FindTasks(ctx, actions_model.FindTaskOptions{RepoID: repoID}) 58 - if err != nil { 59 - return fmt.Errorf("find actions tasks of repo %v: %w", repoID, err) 60 - } 61 - 62 - // Query the artifacts of this repo, they will be needed after they have been deleted to remove artifacts files in ObjectStorage 63 - artifacts, err := actions_model.ListArtifactsByRepoID(ctx, repoID) 64 - if err != nil { 65 - return fmt.Errorf("list actions artifacts of repo %v: %w", repoID, err) 66 - } 67 - 68 - // In case is a organization. 69 - org, err := user_model.GetUserByID(ctx, uid) 70 - if err != nil { 71 - return err 72 - } 73 - 74 - repo := &repo_model.Repository{OwnerID: uid} 75 - has, err := sess.ID(repoID).Get(repo) 76 - if err != nil { 77 - return err 78 - } else if !has { 79 - return repo_model.ErrRepoNotExist{ 80 - ID: repoID, 81 - UID: uid, 82 - OwnerName: "", 83 - Name: "", 84 - } 85 - } 86 - 87 - // Delete Deploy Keys 88 - deployKeys, err := asymkey_model.ListDeployKeys(ctx, &asymkey_model.ListDeployKeysOptions{RepoID: repoID}) 89 - if err != nil { 90 - return fmt.Errorf("listDeployKeys: %w", err) 91 - } 92 - needRewriteKeysFile := len(deployKeys) > 0 93 - for _, dKey := range deployKeys { 94 - if err := DeleteDeployKey(ctx, doer, dKey.ID); err != nil { 95 - return fmt.Errorf("deleteDeployKeys: %w", err) 96 - } 97 - } 98 - 99 - if cnt, err := sess.ID(repoID).Delete(&repo_model.Repository{}); err != nil { 100 - return err 101 - } else if cnt != 1 { 102 - return repo_model.ErrRepoNotExist{ 103 - ID: repoID, 104 - UID: uid, 105 - OwnerName: "", 106 - Name: "", 107 - } 108 - } 109 - 110 - if org.IsOrganization() { 111 - teams, err := organization.FindOrgTeams(ctx, org.ID) 112 - if err != nil { 113 - return err 114 - } 115 - for _, t := range teams { 116 - if !organization.HasTeamRepo(ctx, t.OrgID, t.ID, repoID) { 117 - continue 118 - } else if err = removeRepository(ctx, t, repo, false); err != nil { 119 - return err 120 - } 121 - } 122 - } 123 - 124 - attachments := make([]*repo_model.Attachment, 0, 20) 125 - if err = sess.Join("INNER", "`release`", "`release`.id = `attachment`.release_id"). 126 - Where("`release`.repo_id = ?", repoID). 127 - Find(&attachments); err != nil { 128 - return err 129 - } 130 - releaseAttachments := make([]string, 0, len(attachments)) 131 - for i := 0; i < len(attachments); i++ { 132 - releaseAttachments = append(releaseAttachments, attachments[i].RelativePath()) 133 - } 134 - 135 - if _, err := db.Exec(ctx, "UPDATE `user` SET num_stars=num_stars-1 WHERE id IN (SELECT `uid` FROM `star` WHERE repo_id = ?)", repo.ID); err != nil { 136 - return err 137 - } 138 - 139 - if _, err := db.GetEngine(ctx).In("hook_id", builder.Select("id").From("webhook").Where(builder.Eq{"webhook.repo_id": repo.ID})). 140 - Delete(&webhook.HookTask{}); err != nil { 141 - return err 142 - } 143 - 144 - if err := db.DeleteBeans(ctx, 145 - &access_model.Access{RepoID: repo.ID}, 146 - &activities_model.Action{RepoID: repo.ID}, 147 - &repo_model.Collaboration{RepoID: repoID}, 148 - &issues_model.Comment{RefRepoID: repoID}, 149 - &git_model.CommitStatus{RepoID: repoID}, 150 - &git_model.Branch{RepoID: repoID}, 151 - &git_model.LFSLock{RepoID: repoID}, 152 - &repo_model.LanguageStat{RepoID: repoID}, 153 - &issues_model.Milestone{RepoID: repoID}, 154 - &repo_model.Mirror{RepoID: repoID}, 155 - &activities_model.Notification{RepoID: repoID}, 156 - &git_model.ProtectedBranch{RepoID: repoID}, 157 - &git_model.ProtectedTag{RepoID: repoID}, 158 - &repo_model.PushMirror{RepoID: repoID}, 159 - &repo_model.Release{RepoID: repoID}, 160 - &repo_model.RepoIndexerStatus{RepoID: repoID}, 161 - &repo_model.Redirect{RedirectRepoID: repoID}, 162 - &repo_model.RepoUnit{RepoID: repoID}, 163 - &repo_model.Star{RepoID: repoID}, 164 - &admin_model.Task{RepoID: repoID}, 165 - &repo_model.Watch{RepoID: repoID}, 166 - &webhook.Webhook{RepoID: repoID}, 167 - &secret_model.Secret{RepoID: repoID}, 168 - &actions_model.ActionTaskStep{RepoID: repoID}, 169 - &actions_model.ActionTask{RepoID: repoID}, 170 - &actions_model.ActionRunJob{RepoID: repoID}, 171 - &actions_model.ActionRun{RepoID: repoID}, 172 - &actions_model.ActionRunner{RepoID: repoID}, 173 - &actions_model.ActionScheduleSpec{RepoID: repoID}, 174 - &actions_model.ActionSchedule{RepoID: repoID}, 175 - &actions_model.ActionArtifact{RepoID: repoID}, 176 - ); err != nil { 177 - return fmt.Errorf("deleteBeans: %w", err) 178 - } 179 - 180 - // Delete Labels and related objects 181 - if err := issues_model.DeleteLabelsByRepoID(ctx, repoID); err != nil { 182 - return err 183 - } 184 - 185 - // Delete Pulls and related objects 186 - if err := issues_model.DeletePullsByBaseRepoID(ctx, repoID); err != nil { 187 - return err 188 - } 189 - 190 - // Delete Issues and related objects 191 - var attachmentPaths []string 192 - if attachmentPaths, err = issues_model.DeleteIssuesByRepoID(ctx, repoID); err != nil { 193 - return err 194 - } 195 - 196 - // Delete issue index 197 - if err := db.DeleteResourceIndex(ctx, "issue_index", repoID); err != nil { 198 - return err 199 - } 200 - 201 - if repo.IsFork { 202 - if _, err := db.Exec(ctx, "UPDATE `repository` SET num_forks=num_forks-1 WHERE id=?", repo.ForkID); err != nil { 203 - return fmt.Errorf("decrease fork count: %w", err) 204 - } 205 - } 206 - 207 - if _, err := db.Exec(ctx, "UPDATE `user` SET num_repos=num_repos-1 WHERE id=?", uid); err != nil { 208 - return err 209 - } 210 - 211 - if len(repo.Topics) > 0 { 212 - if err := repo_model.RemoveTopicsFromRepo(ctx, repo.ID); err != nil { 213 - return err 214 - } 215 - } 216 - 217 - if err := project_model.DeleteProjectByRepoID(ctx, repoID); err != nil { 218 - return fmt.Errorf("unable to delete projects for repo[%d]: %w", repoID, err) 219 - } 220 - 221 - // Remove LFS objects 222 - var lfsObjects []*git_model.LFSMetaObject 223 - if err = sess.Where("repository_id=?", repoID).Find(&lfsObjects); err != nil { 224 - return err 225 - } 226 - 227 - lfsPaths := make([]string, 0, len(lfsObjects)) 228 - for _, v := range lfsObjects { 229 - count, err := db.CountByBean(ctx, &git_model.LFSMetaObject{Pointer: lfs.Pointer{Oid: v.Oid}}) 230 - if err != nil { 231 - return err 232 - } 233 - if count > 1 { 234 - continue 235 - } 236 - 237 - lfsPaths = append(lfsPaths, v.RelativePath()) 238 - } 239 - 240 - if _, err := db.DeleteByBean(ctx, &git_model.LFSMetaObject{RepositoryID: repoID}); err != nil { 241 - return err 242 - } 243 - 244 - // Remove archives 245 - var archives []*repo_model.RepoArchiver 246 - if err = sess.Where("repo_id=?", repoID).Find(&archives); err != nil { 247 - return err 248 - } 249 - 250 - archivePaths := make([]string, 0, len(archives)) 251 - for _, v := range archives { 252 - archivePaths = append(archivePaths, v.RelativePath()) 253 - } 254 - 255 - if _, err := db.DeleteByBean(ctx, &repo_model.RepoArchiver{RepoID: repoID}); err != nil { 256 - return err 257 - } 258 - 259 - if repo.NumForks > 0 { 260 - if _, err = sess.Exec("UPDATE `repository` SET fork_id=0,is_fork=? WHERE fork_id=?", false, repo.ID); err != nil { 261 - log.Error("reset 'fork_id' and 'is_fork': %v", err) 262 - } 263 - } 264 - 265 - // Get all attachments with both issue_id and release_id are zero 266 - var newAttachments []*repo_model.Attachment 267 - if err := sess.Where(builder.Eq{ 268 - "repo_id": repo.ID, 269 - "issue_id": 0, 270 - "release_id": 0, 271 - }).Find(&newAttachments); err != nil { 272 - return err 273 - } 274 - 275 - newAttachmentPaths := make([]string, 0, len(newAttachments)) 276 - for _, attach := range newAttachments { 277 - newAttachmentPaths = append(newAttachmentPaths, attach.RelativePath()) 278 - } 279 - 280 - if _, err := sess.Where("repo_id=?", repo.ID).Delete(new(repo_model.Attachment)); err != nil { 281 - return err 282 - } 283 - 284 - if err = committer.Commit(); err != nil { 285 - return err 286 - } 287 - 288 - committer.Close() 289 - 290 - if needRewriteKeysFile { 291 - if err := asymkey_model.RewriteAllPublicKeys(); err != nil { 292 - log.Error("RewriteAllPublicKeys failed: %v", err) 293 - } 294 - } 295 - 296 - // We should always delete the files after the database transaction succeed. If 297 - // we delete the file but the database rollback, the repository will be broken. 298 - 299 - // Remove repository files. 300 - repoPath := repo.RepoPath() 301 - system_model.RemoveAllWithNotice(db.DefaultContext, "Delete repository files", repoPath) 302 - 303 - // Remove wiki files 304 - if repo.HasWiki() { 305 - system_model.RemoveAllWithNotice(db.DefaultContext, "Delete repository wiki", repo.WikiPath()) 306 - } 307 - 308 - // Remove archives 309 - for _, archive := range archivePaths { 310 - system_model.RemoveStorageWithNotice(db.DefaultContext, storage.RepoArchives, "Delete repo archive file", archive) 311 - } 312 - 313 - // Remove lfs objects 314 - for _, lfsObj := range lfsPaths { 315 - system_model.RemoveStorageWithNotice(db.DefaultContext, storage.LFS, "Delete orphaned LFS file", lfsObj) 316 - } 317 - 318 - // Remove issue attachment files. 319 - for _, attachment := range attachmentPaths { 320 - system_model.RemoveStorageWithNotice(db.DefaultContext, storage.Attachments, "Delete issue attachment", attachment) 321 - } 322 - 323 - // Remove release attachment files. 324 - for _, releaseAttachment := range releaseAttachments { 325 - system_model.RemoveStorageWithNotice(db.DefaultContext, storage.Attachments, "Delete release attachment", releaseAttachment) 326 - } 327 - 328 - // Remove attachment with no issue_id and release_id. 329 - for _, newAttachment := range newAttachmentPaths { 330 - system_model.RemoveStorageWithNotice(db.DefaultContext, storage.Attachments, "Delete issue attachment", newAttachment) 331 - } 332 - 333 - if len(repo.Avatar) > 0 { 334 - if err := storage.RepoAvatars.Delete(repo.CustomAvatarRelativePath()); err != nil { 335 - return fmt.Errorf("Failed to remove %s: %w", repo.Avatar, err) 336 - } 337 - } 338 - 339 - // Finally, delete action logs after the actions have already been deleted to avoid new log files 340 - for _, task := range tasks { 341 - err := actions_module.RemoveLogs(ctx, task.LogInStorage, task.LogFilename) 342 - if err != nil { 343 - log.Error("remove log file %q: %v", task.LogFilename, err) 344 - // go on 345 - } 346 - } 347 - 348 - // delete actions artifacts in ObjectStorage after the repo have already been deleted 349 - for _, art := range artifacts { 350 - if err := storage.ActionsArtifacts.Delete(art.StoragePath); err != nil { 351 - log.Error("remove artifact file %q: %v", art.StoragePath, err) 352 - // go on 353 - } 354 - } 355 - 356 - return nil 357 31 } 358 32 359 33 type repoChecker struct {
+3 -1
models/repo/main_test.go
··· 9 9 10 10 "code.gitea.io/gitea/models/unittest" 11 11 12 - _ "code.gitea.io/gitea/models" // register table model 12 + _ "code.gitea.io/gitea/models" // register table model 13 + _ "code.gitea.io/gitea/models/actions" 14 + _ "code.gitea.io/gitea/models/activities" 13 15 _ "code.gitea.io/gitea/models/perm/access" // register table model 14 16 _ "code.gitea.io/gitea/models/repo" // register table model 15 17 _ "code.gitea.io/gitea/models/user" // register table model
+3 -1
models/system/main_test.go
··· 9 9 10 10 "code.gitea.io/gitea/models/unittest" 11 11 12 - _ "code.gitea.io/gitea/models" // register models 12 + _ "code.gitea.io/gitea/models" // register models 13 + _ "code.gitea.io/gitea/models/actions" 14 + _ "code.gitea.io/gitea/models/activities" 13 15 _ "code.gitea.io/gitea/models/system" // register models of system 14 16 ) 15 17
+2
models/user/main_test.go
··· 10 10 "code.gitea.io/gitea/models/unittest" 11 11 12 12 _ "code.gitea.io/gitea/models" 13 + _ "code.gitea.io/gitea/models/actions" 14 + _ "code.gitea.io/gitea/models/activities" 13 15 _ "code.gitea.io/gitea/models/user" 14 16 ) 15 17
-2
modules/activitypub/client_test.go
··· 15 15 user_model "code.gitea.io/gitea/models/user" 16 16 "code.gitea.io/gitea/modules/setting" 17 17 18 - _ "code.gitea.io/gitea/models" // https://discourse.gitea.io/t/testfixtures-could-not-clean-table-access-no-such-table-access/4137/4 19 - 20 18 "github.com/stretchr/testify/assert" 21 19 ) 22 20
+4
modules/activitypub/main_test.go
··· 8 8 "testing" 9 9 10 10 "code.gitea.io/gitea/models/unittest" 11 + 12 + _ "code.gitea.io/gitea/models" 13 + _ "code.gitea.io/gitea/models/actions" 14 + _ "code.gitea.io/gitea/models/activities" 11 15 ) 12 16 13 17 func TestMain(m *testing.M) {
+2
modules/indexer/code/indexer_test.go
··· 16 16 "code.gitea.io/gitea/modules/indexer/code/internal" 17 17 18 18 _ "code.gitea.io/gitea/models" 19 + _ "code.gitea.io/gitea/models/actions" 20 + _ "code.gitea.io/gitea/models/activities" 19 21 20 22 "github.com/stretchr/testify/assert" 21 23 )
+2
modules/indexer/issues/indexer_test.go
··· 15 15 "code.gitea.io/gitea/modules/setting" 16 16 17 17 _ "code.gitea.io/gitea/models" 18 + _ "code.gitea.io/gitea/models/actions" 19 + _ "code.gitea.io/gitea/models/activities" 18 20 19 21 "github.com/stretchr/testify/assert" 20 22 )
+2
modules/indexer/stats/indexer_test.go
··· 16 16 "code.gitea.io/gitea/modules/setting" 17 17 18 18 _ "code.gitea.io/gitea/models" 19 + _ "code.gitea.io/gitea/models/actions" 20 + _ "code.gitea.io/gitea/models/activities" 19 21 20 22 "github.com/stretchr/testify/assert" 21 23 )
+2
modules/repository/main_test.go
··· 8 8 "testing" 9 9 10 10 "code.gitea.io/gitea/models/unittest" 11 + 12 + _ "code.gitea.io/gitea/models/actions" 11 13 ) 12 14 13 15 func TestMain(m *testing.M) {
+2 -1
routers/api/v1/org/team.go
··· 23 23 "code.gitea.io/gitea/routers/api/v1/utils" 24 24 "code.gitea.io/gitea/services/convert" 25 25 org_service "code.gitea.io/gitea/services/org" 26 + repo_service "code.gitea.io/gitea/services/repository" 26 27 ) 27 28 28 29 // ListTeams list all the teams of an organization ··· 726 727 ctx.Error(http.StatusForbidden, "", "Must have admin-level access to the repository") 727 728 return 728 729 } 729 - if err := models.RemoveRepository(ctx.Org.Team, repo.ID); err != nil { 730 + if err := repo_service.RemoveRepositoryFromTeam(ctx, ctx.Org.Team, repo.ID); err != nil { 730 731 ctx.Error(http.StatusInternalServerError, "RemoveRepository", err) 731 732 return 732 733 }
+2 -2
routers/api/v1/repo/migrate.go
··· 170 170 opts.Releases = false 171 171 } 172 172 173 - repo, err := repo_service.CreateRepositoryDirectly(ctx.Doer, repoOwner, repo_service.CreateRepoOptions{ 173 + repo, err := repo_service.CreateRepositoryDirectly(ctx, ctx.Doer, repoOwner, repo_service.CreateRepoOptions{ 174 174 Name: opts.RepoName, 175 175 Description: opts.Description, 176 176 OriginalURL: form.CloneAddr, ··· 200 200 } 201 201 202 202 if repo != nil { 203 - if errDelete := models.DeleteRepository(ctx.Doer, repoOwner.ID, repo.ID); errDelete != nil { 203 + if errDelete := repo_service.DeleteRepositoryDirectly(ctx, ctx.Doer, repoOwner.ID, repo.ID); errDelete != nil { 204 204 log.Error("DeleteRepository: %v", errDelete) 205 205 } 206 206 }
+4 -4
routers/api/v1/repo/teams.go
··· 7 7 "fmt" 8 8 "net/http" 9 9 10 - "code.gitea.io/gitea/models" 11 10 "code.gitea.io/gitea/models/organization" 12 11 "code.gitea.io/gitea/modules/context" 13 12 "code.gitea.io/gitea/services/convert" 14 13 org_service "code.gitea.io/gitea/services/org" 14 + repo_service "code.gitea.io/gitea/services/repository" 15 15 ) 16 16 17 17 // ListTeams list a repository's teams ··· 97 97 return 98 98 } 99 99 100 - if models.HasRepository(team, ctx.Repo.Repository.ID) { 100 + if repo_service.HasRepository(team, ctx.Repo.Repository.ID) { 101 101 apiTeam, err := convert.ToTeam(ctx, team) 102 102 if err != nil { 103 103 ctx.InternalServerError(err) ··· 192 192 return 193 193 } 194 194 195 - repoHasTeam := models.HasRepository(team, ctx.Repo.Repository.ID) 195 + repoHasTeam := repo_service.HasRepository(team, ctx.Repo.Repository.ID) 196 196 var err error 197 197 if add { 198 198 if repoHasTeam { ··· 205 205 ctx.Error(http.StatusUnprocessableEntity, "notAdded", fmt.Errorf("team '%s' was not added to repo", team.Name)) 206 206 return 207 207 } 208 - err = models.RemoveRepository(team, ctx.Repo.Repository.ID) 208 + err = repo_service.RemoveRepositoryFromTeam(ctx, team, ctx.Repo.Repository.ID) 209 209 } 210 210 if err != nil { 211 211 ctx.InternalServerError(err)
+2 -1
routers/web/org/teams.go
··· 29 29 "code.gitea.io/gitea/services/convert" 30 30 "code.gitea.io/gitea/services/forms" 31 31 org_service "code.gitea.io/gitea/services/org" 32 + repo_service "code.gitea.io/gitea/services/repository" 32 33 ) 33 34 34 35 const ( ··· 248 249 } 249 250 err = org_service.TeamAddRepository(ctx.Org.Team, repo) 250 251 case "remove": 251 - err = models.RemoveRepository(ctx.Org.Team, ctx.FormInt64("repoid")) 252 + err = repo_service.RemoveRepositoryFromTeam(ctx, ctx.Org.Team, ctx.FormInt64("repoid")) 252 253 case "addall": 253 254 err = models.AddAllRepositories(ctx.Org.Team) 254 255 case "removeall":
+2 -1
routers/web/repo/setting/collaboration.go
··· 21 21 "code.gitea.io/gitea/routers/utils" 22 22 "code.gitea.io/gitea/services/mailer" 23 23 org_service "code.gitea.io/gitea/services/org" 24 + repo_service "code.gitea.io/gitea/services/repository" 24 25 ) 25 26 26 27 // Collaboration render a repository's collaboration page ··· 196 197 return 197 198 } 198 199 199 - if err = models.RemoveRepository(team, ctx.Repo.Repository.ID); err != nil { 200 + if err = repo_service.RemoveRepositoryFromTeam(ctx, team, ctx.Repo.Repository.ID); err != nil { 200 201 ctx.ServerError("team.RemoveRepositorys", err) 201 202 return 202 203 }
+5 -5
routers/web/repo/setting/settings_test.go
··· 7 7 "net/http" 8 8 "testing" 9 9 10 - "code.gitea.io/gitea/models" 11 10 asymkey_model "code.gitea.io/gitea/models/asymkey" 12 11 "code.gitea.io/gitea/models/organization" 13 12 "code.gitea.io/gitea/models/perm" ··· 19 18 "code.gitea.io/gitea/modules/setting" 20 19 "code.gitea.io/gitea/modules/web" 21 20 "code.gitea.io/gitea/services/forms" 21 + repo_service "code.gitea.io/gitea/services/repository" 22 22 23 23 "github.com/stretchr/testify/assert" 24 24 ) ··· 248 248 249 249 AddTeamPost(ctx) 250 250 251 - assert.True(t, models.HasRepository(team, re.ID)) 251 + assert.True(t, repo_service.HasRepository(team, re.ID)) 252 252 assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status()) 253 253 assert.Empty(t, ctx.Flash.ErrorMsg) 254 254 } ··· 288 288 289 289 AddTeamPost(ctx) 290 290 291 - assert.False(t, models.HasRepository(team, re.ID)) 291 + assert.False(t, repo_service.HasRepository(team, re.ID)) 292 292 assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status()) 293 293 assert.NotEmpty(t, ctx.Flash.ErrorMsg) 294 294 } ··· 329 329 AddTeamPost(ctx) 330 330 331 331 AddTeamPost(ctx) 332 - assert.True(t, models.HasRepository(team, re.ID)) 332 + assert.True(t, repo_service.HasRepository(team, re.ID)) 333 333 assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status()) 334 334 assert.NotEmpty(t, ctx.Flash.ErrorMsg) 335 335 } ··· 402 402 403 403 DeleteTeam(ctx) 404 404 405 - assert.False(t, models.HasRepository(team, re.ID)) 405 + assert.False(t, repo_service.HasRepository(team, re.ID)) 406 406 }
+3
services/asymkey/main_test.go
··· 8 8 "testing" 9 9 10 10 "code.gitea.io/gitea/models/unittest" 11 + 12 + _ "code.gitea.io/gitea/models/actions" 13 + _ "code.gitea.io/gitea/models/activities" 11 14 ) 12 15 13 16 func TestMain(m *testing.M) {
+2
services/attachment/attachment_test.go
··· 13 13 "code.gitea.io/gitea/models/unittest" 14 14 user_model "code.gitea.io/gitea/models/user" 15 15 16 + _ "code.gitea.io/gitea/models/actions" 17 + 16 18 "github.com/stretchr/testify/assert" 17 19 ) 18 20
+2
services/convert/main_test.go
··· 8 8 "testing" 9 9 10 10 "code.gitea.io/gitea/models/unittest" 11 + 12 + _ "code.gitea.io/gitea/models/actions" 11 13 ) 12 14 13 15 func TestMain(m *testing.M) {
+2
services/feed/action_test.go
··· 14 14 "code.gitea.io/gitea/models/unittest" 15 15 user_model "code.gitea.io/gitea/models/user" 16 16 17 + _ "code.gitea.io/gitea/models/actions" 18 + 17 19 "github.com/stretchr/testify/assert" 18 20 ) 19 21
+2
services/gitdiff/main_test.go
··· 10 10 "code.gitea.io/gitea/models/unittest" 11 11 12 12 _ "code.gitea.io/gitea/models" 13 + _ "code.gitea.io/gitea/models/actions" 14 + _ "code.gitea.io/gitea/models/activities" 13 15 ) 14 16 15 17 func TestMain(m *testing.M) {
+2
services/issue/main_test.go
··· 8 8 "testing" 9 9 10 10 "code.gitea.io/gitea/models/unittest" 11 + 12 + _ "code.gitea.io/gitea/models/actions" 11 13 ) 12 14 13 15 func TestMain(m *testing.M) {
+2
services/mailer/main_test.go
··· 8 8 "testing" 9 9 10 10 "code.gitea.io/gitea/models/unittest" 11 + 12 + _ "code.gitea.io/gitea/models/actions" 11 13 ) 12 14 13 15 func TestMain(m *testing.M) {
+1 -1
services/migrations/gitea_uploader.go
··· 100 100 101 101 var r *repo_model.Repository 102 102 if opts.MigrateToRepoID <= 0 { 103 - r, err = repo_service.CreateRepositoryDirectly(g.doer, owner, repo_service.CreateRepoOptions{ 103 + r, err = repo_service.CreateRepositoryDirectly(g.ctx, g.doer, owner, repo_service.CreateRepoOptions{ 104 104 Name: g.repoName, 105 105 Description: repo.Description, 106 106 OriginalURL: repo.OriginalURL,
+1 -1
services/org/repo.go
··· 17 17 func TeamAddRepository(t *organization.Team, repo *repo_model.Repository) (err error) { 18 18 if repo.OwnerID != t.OrgID { 19 19 return errors.New("repository does not belong to organization") 20 - } else if models.HasRepository(t, repo.ID) { 20 + } else if organization.HasTeamRepo(db.DefaultContext, t.OrgID, t.ID, repo.ID) { 21 21 return nil 22 22 } 23 23
+1 -1
services/packages/cargo/index.go
··· 206 206 repo, err := repo_model.GetRepositoryByOwnerAndName(ctx, owner.Name, IndexRepositoryName) 207 207 if err != nil { 208 208 if errors.Is(err, util.ErrNotExist) { 209 - repo, err = repo_service.CreateRepositoryDirectly(doer, owner, repo_service.CreateRepoOptions{ 209 + repo, err = repo_service.CreateRepositoryDirectly(ctx, doer, owner, repo_service.CreateRepoOptions{ 210 210 Name: IndexRepositoryName, 211 211 }) 212 212 if err != nil {
+2
services/pull/main_test.go
··· 9 9 "testing" 10 10 11 11 "code.gitea.io/gitea/models/unittest" 12 + 13 + _ "code.gitea.io/gitea/models/actions" 12 14 ) 13 15 14 16 func TestMain(m *testing.M) {
+2
services/release/release_test.go
··· 16 16 "code.gitea.io/gitea/modules/git" 17 17 "code.gitea.io/gitea/services/attachment" 18 18 19 + _ "code.gitea.io/gitea/models/actions" 20 + 19 21 "github.com/stretchr/testify/assert" 20 22 ) 21 23
+2
services/repository/archiver/archiver_test.go
··· 12 12 "code.gitea.io/gitea/models/unittest" 13 13 "code.gitea.io/gitea/modules/contexttest" 14 14 15 + _ "code.gitea.io/gitea/models/actions" 16 + 15 17 "github.com/stretchr/testify/assert" 16 18 ) 17 19
+1 -2
services/repository/check.go
··· 9 9 "strings" 10 10 "time" 11 11 12 - "code.gitea.io/gitea/models" 13 12 "code.gitea.io/gitea/models/db" 14 13 repo_model "code.gitea.io/gitea/models/repo" 15 14 system_model "code.gitea.io/gitea/models/system" ··· 165 164 default: 166 165 } 167 166 log.Trace("Deleting %d/%d...", repo.OwnerID, repo.ID) 168 - if err := models.DeleteRepository(doer, repo.OwnerID, repo.ID); err != nil { 167 + if err := DeleteRepositoryDirectly(ctx, doer, repo.OwnerID, repo.ID); err != nil { 169 168 log.Error("Failed to DeleteRepository %-v: Error: %v", repo, err) 170 169 if err2 := system_model.CreateRepositoryNotice("Failed to DeleteRepository %s [%d]: Error: %v", repo.FullName(), repo.ID, err); err2 != nil { 171 170 log.Error("CreateRepositoryNotice: %v", err)
+3 -4
services/repository/create.go
··· 12 12 "strings" 13 13 "time" 14 14 15 - "code.gitea.io/gitea/models" 16 15 "code.gitea.io/gitea/models/db" 17 16 repo_model "code.gitea.io/gitea/models/repo" 18 17 user_model "code.gitea.io/gitea/models/user" ··· 199 198 } 200 199 201 200 // CreateRepositoryDirectly creates a repository for the user/organization. 202 - func CreateRepositoryDirectly(doer, u *user_model.User, opts CreateRepoOptions) (*repo_model.Repository, error) { 201 + func CreateRepositoryDirectly(ctx context.Context, doer, u *user_model.User, opts CreateRepoOptions) (*repo_model.Repository, error) { 203 202 if !doer.IsAdmin && !u.CanCreateRepo() { 204 203 return nil, repo_model.ErrReachLimitOfRepo{ 205 204 Limit: u.MaxRepoCreation, ··· 239 238 240 239 var rollbackRepo *repo_model.Repository 241 240 242 - if err := db.WithTx(db.DefaultContext, func(ctx context.Context) error { 241 + if err := db.WithTx(ctx, func(ctx context.Context) error { 243 242 if err := repo_module.CreateRepositoryByExample(ctx, doer, u, repo, false, false); err != nil { 244 243 return err 245 244 } ··· 303 302 return nil 304 303 }); err != nil { 305 304 if rollbackRepo != nil { 306 - if errDelete := models.DeleteRepository(doer, rollbackRepo.OwnerID, rollbackRepo.ID); errDelete != nil { 305 + if errDelete := DeleteRepositoryDirectly(ctx, doer, rollbackRepo.OwnerID, rollbackRepo.ID); errDelete != nil { 307 306 log.Error("Rollback deleteRepository: %v", errDelete) 308 307 } 309 308 }
+5 -5
services/repository/create_test.go
··· 28 28 assert.Len(t, team.Repos, len(repoIds), "%s: repo count", team.Name) 29 29 for i, rid := range repoIds { 30 30 if rid > 0 { 31 - assert.True(t, models.HasRepository(team, rid), "%s: HasRepository(%d) %d", rid, i) 31 + assert.True(t, HasRepository(team, rid), "%s: HasRepository(%d) %d", rid, i) 32 32 } 33 33 } 34 34 } ··· 54 54 // Create repos. 55 55 repoIds := make([]int64, 0) 56 56 for i := 0; i < 3; i++ { 57 - r, err := CreateRepositoryDirectly(user, org.AsUser(), CreateRepoOptions{Name: fmt.Sprintf("repo-%d", i)}) 57 + r, err := CreateRepositoryDirectly(db.DefaultContext, user, org.AsUser(), CreateRepoOptions{Name: fmt.Sprintf("repo-%d", i)}) 58 58 assert.NoError(t, err, "CreateRepository %d", i) 59 59 if r != nil { 60 60 repoIds = append(repoIds, r.ID) ··· 116 116 } 117 117 118 118 // Create repo and check teams repositories. 119 - r, err := CreateRepositoryDirectly(user, org.AsUser(), CreateRepoOptions{Name: "repo-last"}) 119 + r, err := CreateRepositoryDirectly(db.DefaultContext, user, org.AsUser(), CreateRepoOptions{Name: "repo-last"}) 120 120 assert.NoError(t, err, "CreateRepository last") 121 121 if r != nil { 122 122 repoIds = append(repoIds, r.ID) ··· 129 129 } 130 130 131 131 // Remove repo and check teams repositories. 132 - assert.NoError(t, models.DeleteRepository(user, org.ID, repoIds[0]), "DeleteRepository") 132 + assert.NoError(t, DeleteRepositoryDirectly(db.DefaultContext, user, org.ID, repoIds[0]), "DeleteRepository") 133 133 teamRepos[0] = repoIds[1:] 134 134 teamRepos[1] = repoIds[1:] 135 135 teamRepos[3] = repoIds[1:3] ··· 141 141 // Wipe created items. 142 142 for i, rid := range repoIds { 143 143 if i > 0 { // first repo already deleted. 144 - assert.NoError(t, models.DeleteRepository(user, org.ID, rid), "DeleteRepository %d", i) 144 + assert.NoError(t, DeleteRepositoryDirectly(db.DefaultContext, user, org.ID, rid), "DeleteRepository %d", i) 145 145 } 146 146 } 147 147 assert.NoError(t, organization.DeleteOrganization(db.DefaultContext, org), "DeleteOrganization")
+424
services/repository/delete.go
··· 1 + // Copyright 2023 The Gitea Authors. All rights reserved. 2 + // SPDX-License-Identifier: MIT 3 + 4 + package repository 5 + 6 + import ( 7 + "context" 8 + "fmt" 9 + 10 + "code.gitea.io/gitea/models" 11 + actions_model "code.gitea.io/gitea/models/actions" 12 + activities_model "code.gitea.io/gitea/models/activities" 13 + admin_model "code.gitea.io/gitea/models/admin" 14 + asymkey_model "code.gitea.io/gitea/models/asymkey" 15 + "code.gitea.io/gitea/models/db" 16 + git_model "code.gitea.io/gitea/models/git" 17 + issues_model "code.gitea.io/gitea/models/issues" 18 + "code.gitea.io/gitea/models/organization" 19 + access_model "code.gitea.io/gitea/models/perm/access" 20 + project_model "code.gitea.io/gitea/models/project" 21 + repo_model "code.gitea.io/gitea/models/repo" 22 + secret_model "code.gitea.io/gitea/models/secret" 23 + system_model "code.gitea.io/gitea/models/system" 24 + user_model "code.gitea.io/gitea/models/user" 25 + "code.gitea.io/gitea/models/webhook" 26 + actions_module "code.gitea.io/gitea/modules/actions" 27 + "code.gitea.io/gitea/modules/lfs" 28 + "code.gitea.io/gitea/modules/log" 29 + "code.gitea.io/gitea/modules/storage" 30 + 31 + "xorm.io/builder" 32 + ) 33 + 34 + // DeleteRepository deletes a repository for a user or organization. 35 + // make sure if you call this func to close open sessions (sqlite will otherwise get a deadlock) 36 + func DeleteRepositoryDirectly(ctx context.Context, doer *user_model.User, uid, repoID int64) error { 37 + ctx, committer, err := db.TxContext(ctx) 38 + if err != nil { 39 + return err 40 + } 41 + defer committer.Close() 42 + sess := db.GetEngine(ctx) 43 + 44 + // Query the action tasks of this repo, they will be needed after they have been deleted to remove the logs 45 + tasks, err := actions_model.FindTasks(ctx, actions_model.FindTaskOptions{RepoID: repoID}) 46 + if err != nil { 47 + return fmt.Errorf("find actions tasks of repo %v: %w", repoID, err) 48 + } 49 + 50 + // Query the artifacts of this repo, they will be needed after they have been deleted to remove artifacts files in ObjectStorage 51 + artifacts, err := actions_model.ListArtifactsByRepoID(ctx, repoID) 52 + if err != nil { 53 + return fmt.Errorf("list actions artifacts of repo %v: %w", repoID, err) 54 + } 55 + 56 + // In case is a organization. 57 + org, err := user_model.GetUserByID(ctx, uid) 58 + if err != nil { 59 + return err 60 + } 61 + 62 + repo := &repo_model.Repository{OwnerID: uid} 63 + has, err := sess.ID(repoID).Get(repo) 64 + if err != nil { 65 + return err 66 + } else if !has { 67 + return repo_model.ErrRepoNotExist{ 68 + ID: repoID, 69 + UID: uid, 70 + OwnerName: "", 71 + Name: "", 72 + } 73 + } 74 + 75 + // Delete Deploy Keys 76 + deployKeys, err := asymkey_model.ListDeployKeys(ctx, &asymkey_model.ListDeployKeysOptions{RepoID: repoID}) 77 + if err != nil { 78 + return fmt.Errorf("listDeployKeys: %w", err) 79 + } 80 + needRewriteKeysFile := len(deployKeys) > 0 81 + for _, dKey := range deployKeys { 82 + if err := models.DeleteDeployKey(ctx, doer, dKey.ID); err != nil { 83 + return fmt.Errorf("deleteDeployKeys: %w", err) 84 + } 85 + } 86 + 87 + if cnt, err := sess.ID(repoID).Delete(&repo_model.Repository{}); err != nil { 88 + return err 89 + } else if cnt != 1 { 90 + return repo_model.ErrRepoNotExist{ 91 + ID: repoID, 92 + UID: uid, 93 + OwnerName: "", 94 + Name: "", 95 + } 96 + } 97 + 98 + if org.IsOrganization() { 99 + teams, err := organization.FindOrgTeams(ctx, org.ID) 100 + if err != nil { 101 + return err 102 + } 103 + for _, t := range teams { 104 + if !organization.HasTeamRepo(ctx, t.OrgID, t.ID, repoID) { 105 + continue 106 + } else if err = removeRepositoryFromTeam(ctx, t, repo, false); err != nil { 107 + return err 108 + } 109 + } 110 + } 111 + 112 + attachments := make([]*repo_model.Attachment, 0, 20) 113 + if err = sess.Join("INNER", "`release`", "`release`.id = `attachment`.release_id"). 114 + Where("`release`.repo_id = ?", repoID). 115 + Find(&attachments); err != nil { 116 + return err 117 + } 118 + releaseAttachments := make([]string, 0, len(attachments)) 119 + for i := 0; i < len(attachments); i++ { 120 + releaseAttachments = append(releaseAttachments, attachments[i].RelativePath()) 121 + } 122 + 123 + if _, err := db.Exec(ctx, "UPDATE `user` SET num_stars=num_stars-1 WHERE id IN (SELECT `uid` FROM `star` WHERE repo_id = ?)", repo.ID); err != nil { 124 + return err 125 + } 126 + 127 + if _, err := db.GetEngine(ctx).In("hook_id", builder.Select("id").From("webhook").Where(builder.Eq{"webhook.repo_id": repo.ID})). 128 + Delete(&webhook.HookTask{}); err != nil { 129 + return err 130 + } 131 + 132 + if err := db.DeleteBeans(ctx, 133 + &access_model.Access{RepoID: repo.ID}, 134 + &activities_model.Action{RepoID: repo.ID}, 135 + &repo_model.Collaboration{RepoID: repoID}, 136 + &issues_model.Comment{RefRepoID: repoID}, 137 + &git_model.CommitStatus{RepoID: repoID}, 138 + &git_model.Branch{RepoID: repoID}, 139 + &git_model.LFSLock{RepoID: repoID}, 140 + &repo_model.LanguageStat{RepoID: repoID}, 141 + &issues_model.Milestone{RepoID: repoID}, 142 + &repo_model.Mirror{RepoID: repoID}, 143 + &activities_model.Notification{RepoID: repoID}, 144 + &git_model.ProtectedBranch{RepoID: repoID}, 145 + &git_model.ProtectedTag{RepoID: repoID}, 146 + &repo_model.PushMirror{RepoID: repoID}, 147 + &repo_model.Release{RepoID: repoID}, 148 + &repo_model.RepoIndexerStatus{RepoID: repoID}, 149 + &repo_model.Redirect{RedirectRepoID: repoID}, 150 + &repo_model.RepoUnit{RepoID: repoID}, 151 + &repo_model.Star{RepoID: repoID}, 152 + &admin_model.Task{RepoID: repoID}, 153 + &repo_model.Watch{RepoID: repoID}, 154 + &webhook.Webhook{RepoID: repoID}, 155 + &secret_model.Secret{RepoID: repoID}, 156 + &actions_model.ActionTaskStep{RepoID: repoID}, 157 + &actions_model.ActionTask{RepoID: repoID}, 158 + &actions_model.ActionRunJob{RepoID: repoID}, 159 + &actions_model.ActionRun{RepoID: repoID}, 160 + &actions_model.ActionRunner{RepoID: repoID}, 161 + &actions_model.ActionScheduleSpec{RepoID: repoID}, 162 + &actions_model.ActionSchedule{RepoID: repoID}, 163 + &actions_model.ActionArtifact{RepoID: repoID}, 164 + ); err != nil { 165 + return fmt.Errorf("deleteBeans: %w", err) 166 + } 167 + 168 + // Delete Labels and related objects 169 + if err := issues_model.DeleteLabelsByRepoID(ctx, repoID); err != nil { 170 + return err 171 + } 172 + 173 + // Delete Pulls and related objects 174 + if err := issues_model.DeletePullsByBaseRepoID(ctx, repoID); err != nil { 175 + return err 176 + } 177 + 178 + // Delete Issues and related objects 179 + var attachmentPaths []string 180 + if attachmentPaths, err = issues_model.DeleteIssuesByRepoID(ctx, repoID); err != nil { 181 + return err 182 + } 183 + 184 + // Delete issue index 185 + if err := db.DeleteResourceIndex(ctx, "issue_index", repoID); err != nil { 186 + return err 187 + } 188 + 189 + if repo.IsFork { 190 + if _, err := db.Exec(ctx, "UPDATE `repository` SET num_forks=num_forks-1 WHERE id=?", repo.ForkID); err != nil { 191 + return fmt.Errorf("decrease fork count: %w", err) 192 + } 193 + } 194 + 195 + if _, err := db.Exec(ctx, "UPDATE `user` SET num_repos=num_repos-1 WHERE id=?", uid); err != nil { 196 + return err 197 + } 198 + 199 + if len(repo.Topics) > 0 { 200 + if err := repo_model.RemoveTopicsFromRepo(ctx, repo.ID); err != nil { 201 + return err 202 + } 203 + } 204 + 205 + if err := project_model.DeleteProjectByRepoID(ctx, repoID); err != nil { 206 + return fmt.Errorf("unable to delete projects for repo[%d]: %w", repoID, err) 207 + } 208 + 209 + // Remove LFS objects 210 + var lfsObjects []*git_model.LFSMetaObject 211 + if err = sess.Where("repository_id=?", repoID).Find(&lfsObjects); err != nil { 212 + return err 213 + } 214 + 215 + lfsPaths := make([]string, 0, len(lfsObjects)) 216 + for _, v := range lfsObjects { 217 + count, err := db.CountByBean(ctx, &git_model.LFSMetaObject{Pointer: lfs.Pointer{Oid: v.Oid}}) 218 + if err != nil { 219 + return err 220 + } 221 + if count > 1 { 222 + continue 223 + } 224 + 225 + lfsPaths = append(lfsPaths, v.RelativePath()) 226 + } 227 + 228 + if _, err := db.DeleteByBean(ctx, &git_model.LFSMetaObject{RepositoryID: repoID}); err != nil { 229 + return err 230 + } 231 + 232 + // Remove archives 233 + var archives []*repo_model.RepoArchiver 234 + if err = sess.Where("repo_id=?", repoID).Find(&archives); err != nil { 235 + return err 236 + } 237 + 238 + archivePaths := make([]string, 0, len(archives)) 239 + for _, v := range archives { 240 + archivePaths = append(archivePaths, v.RelativePath()) 241 + } 242 + 243 + if _, err := db.DeleteByBean(ctx, &repo_model.RepoArchiver{RepoID: repoID}); err != nil { 244 + return err 245 + } 246 + 247 + if repo.NumForks > 0 { 248 + if _, err = sess.Exec("UPDATE `repository` SET fork_id=0,is_fork=? WHERE fork_id=?", false, repo.ID); err != nil { 249 + log.Error("reset 'fork_id' and 'is_fork': %v", err) 250 + } 251 + } 252 + 253 + // Get all attachments with both issue_id and release_id are zero 254 + var newAttachments []*repo_model.Attachment 255 + if err := sess.Where(builder.Eq{ 256 + "repo_id": repo.ID, 257 + "issue_id": 0, 258 + "release_id": 0, 259 + }).Find(&newAttachments); err != nil { 260 + return err 261 + } 262 + 263 + newAttachmentPaths := make([]string, 0, len(newAttachments)) 264 + for _, attach := range newAttachments { 265 + newAttachmentPaths = append(newAttachmentPaths, attach.RelativePath()) 266 + } 267 + 268 + if _, err := sess.Where("repo_id=?", repo.ID).Delete(new(repo_model.Attachment)); err != nil { 269 + return err 270 + } 271 + 272 + if err = committer.Commit(); err != nil { 273 + return err 274 + } 275 + 276 + committer.Close() 277 + 278 + if needRewriteKeysFile { 279 + if err := asymkey_model.RewriteAllPublicKeys(); err != nil { 280 + log.Error("RewriteAllPublicKeys failed: %v", err) 281 + } 282 + } 283 + 284 + // We should always delete the files after the database transaction succeed. If 285 + // we delete the file but the database rollback, the repository will be broken. 286 + 287 + // Remove repository files. 288 + repoPath := repo.RepoPath() 289 + system_model.RemoveAllWithNotice(db.DefaultContext, "Delete repository files", repoPath) 290 + 291 + // Remove wiki files 292 + if repo.HasWiki() { 293 + system_model.RemoveAllWithNotice(db.DefaultContext, "Delete repository wiki", repo.WikiPath()) 294 + } 295 + 296 + // Remove archives 297 + for _, archive := range archivePaths { 298 + system_model.RemoveStorageWithNotice(db.DefaultContext, storage.RepoArchives, "Delete repo archive file", archive) 299 + } 300 + 301 + // Remove lfs objects 302 + for _, lfsObj := range lfsPaths { 303 + system_model.RemoveStorageWithNotice(db.DefaultContext, storage.LFS, "Delete orphaned LFS file", lfsObj) 304 + } 305 + 306 + // Remove issue attachment files. 307 + for _, attachment := range attachmentPaths { 308 + system_model.RemoveStorageWithNotice(db.DefaultContext, storage.Attachments, "Delete issue attachment", attachment) 309 + } 310 + 311 + // Remove release attachment files. 312 + for _, releaseAttachment := range releaseAttachments { 313 + system_model.RemoveStorageWithNotice(db.DefaultContext, storage.Attachments, "Delete release attachment", releaseAttachment) 314 + } 315 + 316 + // Remove attachment with no issue_id and release_id. 317 + for _, newAttachment := range newAttachmentPaths { 318 + system_model.RemoveStorageWithNotice(db.DefaultContext, storage.Attachments, "Delete issue attachment", newAttachment) 319 + } 320 + 321 + if len(repo.Avatar) > 0 { 322 + if err := storage.RepoAvatars.Delete(repo.CustomAvatarRelativePath()); err != nil { 323 + return fmt.Errorf("Failed to remove %s: %w", repo.Avatar, err) 324 + } 325 + } 326 + 327 + // Finally, delete action logs after the actions have already been deleted to avoid new log files 328 + for _, task := range tasks { 329 + err := actions_module.RemoveLogs(ctx, task.LogInStorage, task.LogFilename) 330 + if err != nil { 331 + log.Error("remove log file %q: %v", task.LogFilename, err) 332 + // go on 333 + } 334 + } 335 + 336 + // delete actions artifacts in ObjectStorage after the repo have already been deleted 337 + for _, art := range artifacts { 338 + if err := storage.ActionsArtifacts.Delete(art.StoragePath); err != nil { 339 + log.Error("remove artifact file %q: %v", art.StoragePath, err) 340 + // go on 341 + } 342 + } 343 + 344 + return nil 345 + } 346 + 347 + // removeRepositoryFromTeam removes a repository from a team and recalculates access 348 + // Note: Repository shall not be removed from team if it includes all repositories (unless the repository is deleted) 349 + func removeRepositoryFromTeam(ctx context.Context, t *organization.Team, repo *repo_model.Repository, recalculate bool) (err error) { 350 + e := db.GetEngine(ctx) 351 + if err = organization.RemoveTeamRepo(ctx, t.ID, repo.ID); err != nil { 352 + return err 353 + } 354 + 355 + t.NumRepos-- 356 + if _, err = e.ID(t.ID).Cols("num_repos").Update(t); err != nil { 357 + return err 358 + } 359 + 360 + // Don't need to recalculate when delete a repository from organization. 361 + if recalculate { 362 + if err = access_model.RecalculateTeamAccesses(ctx, repo, t.ID); err != nil { 363 + return err 364 + } 365 + } 366 + 367 + teamUsers, err := organization.GetTeamUsersByTeamID(ctx, t.ID) 368 + if err != nil { 369 + return fmt.Errorf("getTeamUsersByTeamID: %w", err) 370 + } 371 + for _, teamUser := range teamUsers { 372 + has, err := access_model.HasAccess(ctx, teamUser.UID, repo) 373 + if err != nil { 374 + return err 375 + } else if has { 376 + continue 377 + } 378 + 379 + if err = repo_model.WatchRepo(ctx, teamUser.UID, repo.ID, false); err != nil { 380 + return err 381 + } 382 + 383 + // Remove all IssueWatches a user has subscribed to in the repositories 384 + if err := issues_model.RemoveIssueWatchersByRepoID(ctx, teamUser.UID, repo.ID); err != nil { 385 + return err 386 + } 387 + } 388 + 389 + return nil 390 + } 391 + 392 + // HasRepository returns true if given repository belong to team. 393 + func HasRepository(t *organization.Team, repoID int64) bool { 394 + return organization.HasTeamRepo(db.DefaultContext, t.OrgID, t.ID, repoID) 395 + } 396 + 397 + // RemoveRepositoryFromTeam removes repository from team of organization. 398 + // If the team shall include all repositories the request is ignored. 399 + func RemoveRepositoryFromTeam(ctx context.Context, t *organization.Team, repoID int64) error { 400 + if !HasRepository(t, repoID) { 401 + return nil 402 + } 403 + 404 + if t.IncludesAllRepositories { 405 + return nil 406 + } 407 + 408 + repo, err := repo_model.GetRepositoryByID(ctx, repoID) 409 + if err != nil { 410 + return err 411 + } 412 + 413 + ctx, committer, err := db.TxContext(ctx) 414 + if err != nil { 415 + return err 416 + } 417 + defer committer.Close() 418 + 419 + if err = removeRepositoryFromTeam(ctx, t, repo, true); err != nil { 420 + return err 421 + } 422 + 423 + return committer.Commit() 424 + }
+45
services/repository/delete_test.go
··· 1 + // Copyright 2017 The Gitea Authors. All rights reserved. 2 + // SPDX-License-Identifier: MIT 3 + 4 + package repository 5 + 6 + import ( 7 + "testing" 8 + 9 + "code.gitea.io/gitea/models/db" 10 + "code.gitea.io/gitea/models/organization" 11 + repo_model "code.gitea.io/gitea/models/repo" 12 + "code.gitea.io/gitea/models/unittest" 13 + 14 + "github.com/stretchr/testify/assert" 15 + ) 16 + 17 + func TestTeam_HasRepository(t *testing.T) { 18 + assert.NoError(t, unittest.PrepareTestDatabase()) 19 + 20 + test := func(teamID, repoID int64, expected bool) { 21 + team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamID}) 22 + assert.Equal(t, expected, HasRepository(team, repoID)) 23 + } 24 + test(1, 1, false) 25 + test(1, 3, true) 26 + test(1, 5, true) 27 + test(1, unittest.NonexistentID, false) 28 + 29 + test(2, 3, true) 30 + test(2, 5, false) 31 + } 32 + 33 + func TestTeam_RemoveRepository(t *testing.T) { 34 + assert.NoError(t, unittest.PrepareTestDatabase()) 35 + 36 + testSuccess := func(teamID, repoID int64) { 37 + team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamID}) 38 + assert.NoError(t, RemoveRepositoryFromTeam(db.DefaultContext, team, repoID)) 39 + unittest.AssertNotExistsBean(t, &organization.TeamRepo{TeamID: teamID, RepoID: repoID}) 40 + unittest.CheckConsistencyFor(t, &organization.Team{ID: teamID}, &repo_model.Repository{ID: repoID}) 41 + } 42 + testSuccess(2, 3) 43 + testSuccess(2, 5) 44 + testSuccess(1, unittest.NonexistentID) 45 + }
+2
services/repository/files/content_test.go
··· 13 13 "code.gitea.io/gitea/modules/git" 14 14 api "code.gitea.io/gitea/modules/structs" 15 15 16 + _ "code.gitea.io/gitea/models/actions" 17 + 16 18 "github.com/stretchr/testify/assert" 17 19 ) 18 20
+2 -3
services/repository/repository.go
··· 7 7 "context" 8 8 "fmt" 9 9 10 - "code.gitea.io/gitea/models" 11 10 "code.gitea.io/gitea/models/db" 12 11 "code.gitea.io/gitea/models/git" 13 12 issues_model "code.gitea.io/gitea/models/issues" ··· 41 40 42 41 // CreateRepository creates a repository for the user/organization. 43 42 func CreateRepository(ctx context.Context, doer, owner *user_model.User, opts CreateRepoOptions) (*repo_model.Repository, error) { 44 - repo, err := CreateRepositoryDirectly(doer, owner, opts) 43 + repo, err := CreateRepositoryDirectly(ctx, doer, owner, opts) 45 44 if err != nil { 46 45 // No need to rollback here we should do this in CreateRepository... 47 46 return nil, err ··· 63 62 notify_service.DeleteRepository(ctx, doer, repo) 64 63 } 65 64 66 - if err := models.DeleteRepository(doer, repo.OwnerID, repo.ID); err != nil { 65 + if err := DeleteRepositoryDirectly(ctx, doer, repo.OwnerID, repo.ID); err != nil { 67 66 return err 68 67 } 69 68
+2 -1
services/task/task.go
··· 7 7 "fmt" 8 8 9 9 admin_model "code.gitea.io/gitea/models/admin" 10 + "code.gitea.io/gitea/models/db" 10 11 repo_model "code.gitea.io/gitea/models/repo" 11 12 user_model "code.gitea.io/gitea/models/user" 12 13 "code.gitea.io/gitea/modules/graceful" ··· 100 101 return nil, err 101 102 } 102 103 103 - repo, err := repo_service.CreateRepositoryDirectly(doer, u, repo_service.CreateRepoOptions{ 104 + repo, err := repo_service.CreateRepositoryDirectly(db.DefaultContext, doer, u, repo_service.CreateRepoOptions{ 104 105 Name: opts.RepoName, 105 106 Description: opts.Description, 106 107 OriginalURL: opts.OriginalURL,
+2 -1
services/user/user.go
··· 26 26 "code.gitea.io/gitea/services/agit" 27 27 "code.gitea.io/gitea/services/packages" 28 28 container_service "code.gitea.io/gitea/services/packages/container" 29 + repo_service "code.gitea.io/gitea/services/repository" 29 30 ) 30 31 31 32 // RenameUser renames a user ··· 174 175 break 175 176 } 176 177 for _, repo := range repos { 177 - if err := models.DeleteRepository(u, u.ID, repo.ID); err != nil { 178 + if err := repo_service.DeleteRepositoryDirectly(ctx, u, u.ID, repo.ID); err != nil { 178 179 return fmt.Errorf("unable to delete repository %s for %s[%d]. Error: %w", repo.Name, u.Name, u.ID, err) 179 180 } 180 181 }
+1
services/webhook/main_test.go
··· 12 12 "code.gitea.io/gitea/modules/setting" 13 13 14 14 _ "code.gitea.io/gitea/models" 15 + _ "code.gitea.io/gitea/models/actions" 15 16 ) 16 17 17 18 func TestMain(m *testing.M) {
+2
services/wiki/wiki_test.go
··· 14 14 user_model "code.gitea.io/gitea/models/user" 15 15 "code.gitea.io/gitea/modules/git" 16 16 17 + _ "code.gitea.io/gitea/models/actions" 18 + 17 19 "github.com/stretchr/testify/assert" 18 20 ) 19 21
+2 -2
tests/integration/api_repo_test.go
··· 9 9 "net/url" 10 10 "testing" 11 11 12 - "code.gitea.io/gitea/models" 13 12 auth_model "code.gitea.io/gitea/models/auth" 14 13 "code.gitea.io/gitea/models/db" 15 14 access_model "code.gitea.io/gitea/models/perm/access" ··· 18 17 user_model "code.gitea.io/gitea/models/user" 19 18 "code.gitea.io/gitea/modules/setting" 20 19 api "code.gitea.io/gitea/modules/structs" 20 + repo_service "code.gitea.io/gitea/services/repository" 21 21 "code.gitea.io/gitea/tests" 22 22 23 23 "github.com/stretchr/testify/assert" ··· 541 541 542 542 // cleanup 543 543 repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: apiRepo.ID}) 544 - _ = models.DeleteRepository(user, repo.OwnerID, repo.ID) 544 + _ = repo_service.DeleteRepositoryDirectly(db.DefaultContext, user, repo.OwnerID, repo.ID) 545 545 } 546 546 547 547 func transfer(t *testing.T) *repo_model.Repository {
+1 -1
tests/integration/mirror_pull_test.go
··· 39 39 Releases: false, 40 40 } 41 41 42 - mirrorRepo, err := repo_service.CreateRepositoryDirectly(user, user, repo_service.CreateRepoOptions{ 42 + mirrorRepo, err := repo_service.CreateRepositoryDirectly(db.DefaultContext, user, user, repo_service.CreateRepoOptions{ 43 43 Name: opts.RepoName, 44 44 Description: opts.Description, 45 45 IsPrivate: opts.Private,
+1 -1
tests/integration/mirror_push_test.go
··· 39 39 user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) 40 40 srcRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) 41 41 42 - mirrorRepo, err := repo_service.CreateRepositoryDirectly(user, user, repo_service.CreateRepoOptions{ 42 + mirrorRepo, err := repo_service.CreateRepositoryDirectly(db.DefaultContext, user, user, repo_service.CreateRepoOptions{ 43 43 Name: "test-push-mirror", 44 44 }) 45 45 assert.NoError(t, err)