mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-11-09 11:21:19 +01:00
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
2 changed files with 79 additions and 6 deletions
70
modules/doctor/repository.go
Normal file
70
modules/doctor/repository.go
Normal file
|
@ -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.
|
// 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, repoID int64) error {
|
func DeleteRepositoryDirectly(ctx context.Context, doer *user_model.User, 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
|
||||||
|
@ -65,11 +65,14 @@ func DeleteRepositoryDirectly(ctx context.Context, doer *user_model.User, repoID
|
||||||
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, repo.OwnerID)
|
// 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, repo.OwnerID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Delete Deploy Keys
|
// Delete Deploy Keys
|
||||||
deployKeys, err := asymkey_model.ListDeployKeys(ctx, &asymkey_model.ListDeployKeysOptions{RepoID: repoID})
|
deployKeys, err := asymkey_model.ListDeployKeys(ctx, &asymkey_model.ListDeployKeysOptions{RepoID: repoID})
|
||||||
|
@ -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)
|
teams, err := organization.FindOrgTeams(ctx, org.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
Loading…
Reference in a new issue