forked from gitea/gitea
Add doctor dbconsistency fix to delete repos with no owner (#27290)
to address #27273 replace #24873
This commit is contained in:
parent
3dc0c962bf
commit
e83f2cbbac
|
@ -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, 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.
|
||||
// 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, repoID int64) error {
|
||||
func DeleteRepositoryDirectly(ctx context.Context, doer *user_model.User, repoID int64, ignoreOrgTeams ...bool) error {
|
||||
ctx, committer, err := db.TxContext(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -65,10 +65,13 @@ func DeleteRepositoryDirectly(ctx context.Context, doer *user_model.User, repoID
|
|||
return fmt.Errorf("list actions artifacts of repo %v: %w", repoID, err)
|
||||
}
|
||||
|
||||
// In case is a organization.
|
||||
org, err := user_model.GetUserByID(ctx, repo.OwnerID)
|
||||
if err != nil {
|
||||
return err
|
||||
// In case owner is a organization, we have to change repo specific teams
|
||||
// if ignoreOrgTeams is not true
|
||||
var org *user_model.User
|
||||
if len(ignoreOrgTeams) == 0 || !ignoreOrgTeams[0] {
|
||||
if org, err = user_model.GetUserByID(ctx, repo.OwnerID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Delete Deploy Keys
|
||||
|
@ -93,7 +96,7 @@ func DeleteRepositoryDirectly(ctx context.Context, doer *user_model.User, repoID
|
|||
}
|
||||
}
|
||||
|
||||
if org.IsOrganization() {
|
||||
if org != nil && org.IsOrganization() {
|
||||
teams, err := organization.FindOrgTeams(ctx, org.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
Loading…
Reference in New Issue