Add doctor dbconsistency fix to delete repos with no owner (#27290) (#27693)

Backport #27290
pull/27739/head
6543 1 year ago committed by GitHub
parent 6919a02ab7
commit aabcf2d7ad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 70
      modules/doctor/repository.go
  2. 13
      services/repository/delete.go

@ -0,0 +1,70 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package doctor
import (
"context"
"code.gitea.io/gitea/models/db"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/log"
repo_service "code.gitea.io/gitea/services/repository"
"xorm.io/builder"
)
func handleDeleteOrphanedRepos(ctx context.Context, logger log.Logger, autofix bool) error {
test := &consistencyCheck{
Name: "Repos with no existing owner",
Counter: countOrphanedRepos,
Fixer: deleteOrphanedRepos,
FixedMessage: "Deleted all content related to orphaned repos",
}
return test.Run(ctx, logger, autofix)
}
// countOrphanedRepos count repository where user of owner_id do not exist
func countOrphanedRepos(ctx context.Context) (int64, error) {
return db.CountOrphanedObjects(ctx, "repository", "user", "repository.owner_id=user.id")
}
// deleteOrphanedRepos delete repository where user of owner_id do not exist
func deleteOrphanedRepos(ctx context.Context) (int64, error) {
batchSize := db.MaxBatchInsertSize("repository")
e := db.GetEngine(ctx)
var deleted int64
adminUser := &user_model.User{IsAdmin: true}
for {
var ids []int64
if err := e.Table("`repository`").
Join("LEFT", "`user`", "repository.owner_id=user.id").
Where(builder.IsNull{"`user`.id"}).
Select("`repository`.id").Limit(batchSize).Find(&ids); err != nil {
return deleted, err
}
// if we don't get ids we have deleted them all
if len(ids) == 0 {
return deleted, nil
}
for _, id := range ids {
if err := repo_service.DeleteRepositoryDirectly(ctx, adminUser, 0, id, true); err != nil {
return deleted, err
}
deleted++
}
}
}
func init() {
Register(&Check{
Title: "Deleted all content related to orphaned repos",
Name: "delete-orphaned-repos",
IsDefault: false,
Run: handleDeleteOrphanedRepos,
Priority: 4,
})
}

@ -33,7 +33,7 @@ import (
// DeleteRepository deletes a repository for a user or organization. // DeleteRepository deletes a repository for a user or organization.
// make sure if you call this func to close open sessions (sqlite will otherwise get a deadlock) // make sure if you call this func to close open sessions (sqlite will otherwise get a deadlock)
func DeleteRepositoryDirectly(ctx context.Context, doer *user_model.User, uid, repoID int64) error { func DeleteRepositoryDirectly(ctx context.Context, doer *user_model.User, uid, repoID int64, ignoreOrgTeams ...bool) error {
ctx, committer, err := db.TxContext(ctx) ctx, committer, err := db.TxContext(ctx)
if err != nil { if err != nil {
return err return err
@ -53,11 +53,14 @@ func DeleteRepositoryDirectly(ctx context.Context, doer *user_model.User, uid, r
return fmt.Errorf("list actions artifacts of repo %v: %w", repoID, err) return fmt.Errorf("list actions artifacts of repo %v: %w", repoID, err)
} }
// In case is a organization. // In case owner is a organization, we have to change repo specific teams
org, err := user_model.GetUserByID(ctx, uid) // if ignoreOrgTeams is not true
if err != nil { var org *user_model.User
if len(ignoreOrgTeams) == 0 || !ignoreOrgTeams[0] {
if org, err = user_model.GetUserByID(ctx, uid); err != nil {
return err return err
} }
}
repo := &repo_model.Repository{OwnerID: uid} repo := &repo_model.Repository{OwnerID: uid}
has, err := sess.ID(repoID).Get(repo) has, err := sess.ID(repoID).Get(repo)
@ -95,7 +98,7 @@ func DeleteRepositoryDirectly(ctx context.Context, doer *user_model.User, uid, r
} }
} }
if org.IsOrganization() { if org != nil && org.IsOrganization() {
teams, err := organization.FindOrgTeams(ctx, org.ID) teams, err := organization.FindOrgTeams(ctx, org.ID)
if err != nil { if err != nil {
return err return err

Loading…
Cancel
Save