mirror of
https://github.com/go-gitea/gitea.git
synced 2026-05-12 13:22:55 +02:00
reverted back to old logic
This commit is contained in:
parent
008415e752
commit
63f9dbf2a6
@ -522,26 +522,24 @@ func DeleteOrgRepos(ctx *context.APIContext) {
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
org := ctx.Org.Organization
|
||||
orgID := org.ID
|
||||
doer := ctx.Doer
|
||||
|
||||
// Check if org has any repos
|
||||
count, err := db.GetEngine(ctx).Where("owner_id = ?", orgID).Count(new(repo_model.Repository))
|
||||
repos, err := repo_model.GetOrgRepositories(ctx, org.ID)
|
||||
if err != nil {
|
||||
ctx.APIErrorInternal(err)
|
||||
return
|
||||
}
|
||||
|
||||
if count == 0 {
|
||||
if len(repos) == 0 {
|
||||
ctx.Status(http.StatusNoContent)
|
||||
return
|
||||
}
|
||||
|
||||
doer := ctx.Doer
|
||||
|
||||
// Start deletion in background with detached context
|
||||
go func() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
desc := fmt.Sprintf("Panic during org repo deletion for org ID %d: %v", orgID, r)
|
||||
desc := fmt.Sprintf("Panic during org repo deletion for org ID %d: %v", org.ID, r)
|
||||
if noticeErr := system_model.CreateNotice(graceful.GetManager().HammerContext(), system_model.NoticeRepository, desc); noticeErr != nil {
|
||||
log.Error("Failed to create notice for panic: %v", noticeErr)
|
||||
}
|
||||
@ -551,59 +549,17 @@ func DeleteOrgRepos(ctx *context.APIContext) {
|
||||
// Use HammerContext so deletion continues even if client disconnects
|
||||
bgCtx := graceful.GetManager().HammerContext()
|
||||
|
||||
const batchSize = 50
|
||||
const maxRetries = 3
|
||||
// Track failed deletions with retry limit to prevent infinite loops when repos cannot be deleted.
|
||||
// If a repo fails 3 times, skip it; if all remaining repos have hit max retries, exit the loop.
|
||||
failedRepos := make(map[int64]int) // repo ID -> retry count
|
||||
|
||||
for {
|
||||
repos := make([]*repo_model.Repository, 0, batchSize)
|
||||
// Always fetch from offset 0 since we're deleting as we go
|
||||
err := db.GetEngine(bgCtx).Where("owner_id = ?", orgID).
|
||||
Limit(batchSize, 0).
|
||||
Find(&repos)
|
||||
if err != nil {
|
||||
desc := fmt.Sprintf("Failed to fetch repositories for org ID %d: %v", orgID, err)
|
||||
for _, repo := range repos {
|
||||
if err := repo_service.DeleteRepository(bgCtx, doer, repo, true); err != nil {
|
||||
desc := fmt.Sprintf("Failed to delete repository %s (ID: %d) in org %s: %v", repo.Name, repo.ID, org.Name, err)
|
||||
if noticeErr := system_model.CreateNotice(bgCtx, system_model.NoticeRepository, desc); noticeErr != nil {
|
||||
log.Error("Failed to create notice for repo fetch failure: %v", noticeErr)
|
||||
log.Error("Failed to create notice for repo deletion failure: %v", noticeErr)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
// exit the loop when there are no more repos to delete
|
||||
if len(repos) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
allFailed := true
|
||||
for _, repo := range repos {
|
||||
// Skip repos that have failed too many times
|
||||
if failedRepos[repo.ID] >= maxRetries {
|
||||
continue
|
||||
}
|
||||
|
||||
if err := repo_service.DeleteRepository(bgCtx, doer, repo, true); err != nil {
|
||||
failedRepos[repo.ID]++
|
||||
desc := fmt.Sprintf("Failed to delete repository %s (ID: %d) in org ID %d (attempt %d/%d): %v",
|
||||
repo.Name, repo.ID, orgID, failedRepos[repo.ID], maxRetries, err)
|
||||
if noticeErr := system_model.CreateNotice(bgCtx, system_model.NoticeRepository, desc); noticeErr != nil {
|
||||
log.Error("Failed to create notice for repo deletion failure: %v", noticeErr)
|
||||
}
|
||||
} else {
|
||||
allFailed = false
|
||||
delete(failedRepos, repo.ID) // Remove from failed map if it succeeds
|
||||
log.Info("Successfully deleted repository %s (ID: %d) in org ID %d", repo.Name, repo.ID, orgID)
|
||||
}
|
||||
}
|
||||
|
||||
// If all repos in this batch have failed max retries, break to avoid infinite loop
|
||||
if allFailed && len(failedRepos) > 0 {
|
||||
log.Error("All remaining repositories failed to delete after %d retries for org ID %d", maxRetries, orgID)
|
||||
break
|
||||
} else {
|
||||
log.Info("Successfully deleted repository %s (ID: %d) in org %s", repo.Name, repo.ID, org.Name)
|
||||
}
|
||||
}
|
||||
log.Info("Completed deletion of repositories in org ID %d", orgID)
|
||||
log.Info("Completed deletion of repositories in org %s", org.Name)
|
||||
}()
|
||||
|
||||
ctx.Status(http.StatusAccepted)
|
||||
|
||||
@ -427,53 +427,4 @@ func TestAPIDeleteOrgRepos(t *testing.T) {
|
||||
req = NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/orgs/%s/repos", orgName)).AddTokenAuth(token)
|
||||
MakeRequest(t, req, http.StatusNoContent)
|
||||
})
|
||||
|
||||
t.Run("Pagination works for large org", func(t *testing.T) {
|
||||
defer tests.PrintCurrentTest(t)()
|
||||
|
||||
session := loginUser(t, "user1")
|
||||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteOrganization, auth_model.AccessTokenScopeWriteRepository)
|
||||
|
||||
orgName := "test_pagination_org"
|
||||
req := NewRequestWithJSON(t, "POST", "/api/v1/orgs", &api.CreateOrgOption{
|
||||
UserName: orgName,
|
||||
}).AddTokenAuth(token)
|
||||
MakeRequest(t, req, http.StatusCreated)
|
||||
|
||||
// Create 75 repos to test pagination (batch size is 50)
|
||||
for i := range 75 {
|
||||
repoName := fmt.Sprintf("test_repo_%d", i)
|
||||
req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/org/%s/repos", orgName), &api.CreateRepoOption{
|
||||
Name: repoName,
|
||||
}).AddTokenAuth(token)
|
||||
MakeRequest(t, req, http.StatusCreated)
|
||||
}
|
||||
|
||||
// Delete all repos - should return 202 Accepted
|
||||
req = NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/orgs/%s/repos", orgName)).AddTokenAuth(token)
|
||||
MakeRequest(t, req, http.StatusAccepted)
|
||||
|
||||
// Wait for background deletion to complete (poll until done)
|
||||
org := unittest.AssertExistsAndLoadBean(t, &org_model.Organization{Name: orgName})
|
||||
maxWait := 30 * time.Second
|
||||
checkInterval := 500 * time.Millisecond
|
||||
elapsed := time.Duration(0)
|
||||
|
||||
for elapsed < maxWait {
|
||||
time.Sleep(checkInterval)
|
||||
elapsed += checkInterval
|
||||
|
||||
remainingRepos, err := repo_model.GetOrgRepositories(t.Context(), org.ID)
|
||||
assert.NoError(t, err)
|
||||
|
||||
if len(remainingRepos) == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Verify all repos were deleted
|
||||
remainingRepos, err := repo_model.GetOrgRepositories(t.Context(), org.ID)
|
||||
assert.NoError(t, err)
|
||||
assert.Empty(t, remainingRepos, "All repositories should be deleted")
|
||||
})
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user