From 80476238ab75f2b6125506715ff0fe233e330956 Mon Sep 17 00:00:00 2001 From: forgejo-backport-action Date: Wed, 6 May 2026 15:31:51 +0200 Subject: [PATCH] [v15.0/forgejo] fix: cleanup data before migration retry (#12422) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/12370 In the case you hit some API error (Github ratelimit was often a problem) or the instance restarted in the middle of your migration, you would be left with data on the disk and/or database. Upon retrying the migration the migration code would (rightfully) fail because it's trying to migrate stuff that already exists. This was hit so often on Codeberg it was better to force people to delete and start whole migration process again: https://codeberg.org/Codeberg-Infrastructure/forgejo/commit/28ee60c91f237268aa526086d0cce7a8f5380a9b Delete the repository data before retrying to solve this. Co-authored-by: Gusted Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/12422 --- models/repo.go | 23 ++------ routers/api/v1/repo/key.go | 2 +- routers/api/v1/repo/migrate.go | 2 +- routers/web/repo/migrate.go | 2 - routers/web/repo/setting/deploy_key.go | 2 +- services/asymkey/deploy_key.go | 16 ++---- services/cron/tasks_extended.go | 4 +- services/doctor/repository.go | 4 +- services/repository/check.go | 5 +- services/repository/create.go | 2 +- services/repository/create_test.go | 4 +- services/repository/delete.go | 43 ++++++++++----- services/repository/repository.go | 2 +- .../fixtures/TestRetryMigrateTask/issue.yml | 16 ++++++ .../TestRetryMigrateTask/repo_unit.yml | 34 ++++++++++++ .../fixtures/TestRetryMigrateTask/task.yml | 22 ++++++++ services/task/task.go | 18 ++++--- services/task/task_test.go | 52 +++++++++++++++++++ tests/integration/api_repo_test.go | 2 +- .../ephemeral_actions_runner_deletion_test.go | 3 +- tests/integration/git_push_test.go | 4 +- 21 files changed, 191 insertions(+), 71 deletions(-) create mode 100644 services/task/fixtures/TestRetryMigrateTask/issue.yml create mode 100644 services/task/fixtures/TestRetryMigrateTask/repo_unit.yml create mode 100644 services/task/fixtures/TestRetryMigrateTask/task.yml diff --git a/models/repo.go b/models/repo.go index 1b9cc8fa60..6a4da96b95 100644 --- a/models/repo.go +++ b/models/repo.go @@ -14,10 +14,8 @@ import ( asymkey_model "forgejo.org/models/asymkey" "forgejo.org/models/db" issues_model "forgejo.org/models/issues" - access_model "forgejo.org/models/perm/access" repo_model "forgejo.org/models/repo" "forgejo.org/models/unit" - user_model "forgejo.org/models/user" "forgejo.org/modules/log" "forgejo.org/services/stats" @@ -277,7 +275,7 @@ func DoctorUserStarNum(ctx context.Context) (err error) { } // DeleteDeployKey delete deploy keys -func DeleteDeployKey(ctx context.Context, doer *user_model.User, id int64) error { +func DeleteDeployKey(ctx context.Context, id, repoID int64) error { key, err := asymkey_model.GetDeployKeyByID(ctx, id) if err != nil { if asymkey_model.IsErrDeployKeyNotExist(err) { @@ -286,21 +284,10 @@ func DeleteDeployKey(ctx context.Context, doer *user_model.User, id int64) error return fmt.Errorf("GetDeployKeyByID: %w", err) } - // Check if user has access to delete this key. - if !doer.IsAdmin { - repo, err := repo_model.GetRepositoryByID(ctx, key.RepoID) - if err != nil { - return fmt.Errorf("GetRepositoryByID: %w", err) - } - has, err := access_model.IsUserRepoAdmin(ctx, repo, doer) - if err != nil { - return fmt.Errorf("GetUserRepoPermission: %w", err) - } else if !has { - return asymkey_model.ErrKeyAccessDenied{ - UserID: doer.ID, - KeyID: key.ID, - Note: "deploy", - } + if key.RepoID != repoID { + return asymkey_model.ErrKeyAccessDenied{ + KeyID: key.ID, + Note: "deploy", } } diff --git a/routers/api/v1/repo/key.go b/routers/api/v1/repo/key.go index 36fe2080d4..caa482cf9b 100644 --- a/routers/api/v1/repo/key.go +++ b/routers/api/v1/repo/key.go @@ -279,7 +279,7 @@ func DeleteDeploykey(ctx *context.APIContext) { // "404": // "$ref": "#/responses/notFound" - if err := asymkey_service.DeleteDeployKey(ctx, ctx.Doer, ctx.ParamsInt64(":id")); err != nil { + if err := asymkey_service.DeleteDeployKey(ctx, ctx.ParamsInt64(":id"), ctx.Repo.Repository.ID); err != nil { if asymkey_model.IsErrKeyAccessDenied(err) { ctx.Error(http.StatusForbidden, "", "You do not have access to this key") } else { diff --git a/routers/api/v1/repo/migrate.go b/routers/api/v1/repo/migrate.go index 272806bfb0..7ec4f901a3 100644 --- a/routers/api/v1/repo/migrate.go +++ b/routers/api/v1/repo/migrate.go @@ -205,7 +205,7 @@ func Migrate(ctx *context.APIContext) { } if repo != nil { - if errDelete := repo_service.DeleteRepositoryDirectly(ctx, ctx.Doer, repo.ID); errDelete != nil { + if errDelete := repo_service.DeleteRepositoryDirectly(ctx, repo.ID, repo_service.DeleteRepositoryOpts{}); errDelete != nil { log.Error("DeleteRepository: %v", errDelete) } } diff --git a/routers/web/repo/migrate.go b/routers/web/repo/migrate.go index cbccb10b06..4397c49e9e 100644 --- a/routers/web/repo/migrate.go +++ b/routers/web/repo/migrate.go @@ -269,7 +269,6 @@ func setMigrationContextData(ctx *context.Context, serviceType structs.GitServic func MigrateRetryPost(ctx *context.Context) { ok, err := quota_model.EvaluateForUser(ctx, ctx.Repo.Repository.OwnerID, quota_model.LimitSubjectSizeReposAll) if err != nil { - log.Error("quota_model.EvaluateForUser: %v", err) ctx.ServerError("quota_model.EvaluateForUser", err) return } @@ -287,7 +286,6 @@ func MigrateRetryPost(ctx *context.Context) { } if err := task.RetryMigrateTask(ctx, ctx.Repo.Repository.ID); err != nil { - log.Error("Retry task failed: %v", err) ctx.ServerError("task.RetryMigrateTask", err) return } diff --git a/routers/web/repo/setting/deploy_key.go b/routers/web/repo/setting/deploy_key.go index c59f0e90c2..879a21aaa5 100644 --- a/routers/web/repo/setting/deploy_key.go +++ b/routers/web/repo/setting/deploy_key.go @@ -99,7 +99,7 @@ func DeployKeysPost(ctx *context.Context) { // DeleteDeployKey response for deleting a deploy key func DeleteDeployKey(ctx *context.Context) { - if err := asymkey_service.DeleteDeployKey(ctx, ctx.Doer, ctx.FormInt64("id")); err != nil { + if err := asymkey_service.DeleteDeployKey(ctx, ctx.FormInt64("id"), ctx.Repo.Repository.ID); err != nil { ctx.Flash.Error("DeleteDeployKey: " + err.Error()) } else { ctx.Flash.Success(ctx.Tr("repo.settings.deploy_key_deletion_success")) diff --git a/services/asymkey/deploy_key.go b/services/asymkey/deploy_key.go index 4a2cb53eec..518a2a54ec 100644 --- a/services/asymkey/deploy_key.go +++ b/services/asymkey/deploy_key.go @@ -9,21 +9,13 @@ import ( "forgejo.org/models" asymkey_model "forgejo.org/models/asymkey" "forgejo.org/models/db" - user_model "forgejo.org/models/user" ) // DeleteDeployKey deletes deploy key from its repository authorized_keys file if needed. -func DeleteDeployKey(ctx context.Context, doer *user_model.User, id int64) error { - dbCtx, committer, err := db.TxContext(ctx) - if err != nil { - return err - } - defer committer.Close() - - if err := models.DeleteDeployKey(dbCtx, doer, id); err != nil { - return err - } - if err := committer.Commit(); err != nil { +func DeleteDeployKey(ctx context.Context, id, repoID int64) error { + if err := db.WithTx(ctx, func(ctx context.Context) error { + return models.DeleteDeployKey(ctx, id, repoID) + }); err != nil { return err } diff --git a/services/cron/tasks_extended.go b/services/cron/tasks_extended.go index 5cc5d4eb14..b7db283f07 100644 --- a/services/cron/tasks_extended.go +++ b/services/cron/tasks_extended.go @@ -111,8 +111,8 @@ func registerDeleteMissingRepositories() { Enabled: false, RunAtStart: false, Schedule: "@every 72h", - }, func(ctx context.Context, user *user_model.User, _ Config) error { - return repo_service.DeleteMissingRepositories(ctx, user) + }, func(ctx context.Context, _ *user_model.User, _ Config) error { + return repo_service.DeleteMissingRepositories(ctx) }) } diff --git a/services/doctor/repository.go b/services/doctor/repository.go index cd51483d88..a3d845f980 100644 --- a/services/doctor/repository.go +++ b/services/doctor/repository.go @@ -7,7 +7,6 @@ import ( "context" "forgejo.org/models/db" - user_model "forgejo.org/models/user" "forgejo.org/modules/log" "forgejo.org/modules/storage" repo_service "forgejo.org/services/repository" @@ -39,7 +38,6 @@ 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 { select { @@ -60,7 +58,7 @@ func deleteOrphanedRepos(ctx context.Context) (int64, error) { } for _, id := range ids { - if err := repo_service.DeleteRepositoryDirectly(ctx, adminUser, id, true); err != nil { + if err := repo_service.DeleteRepositoryDirectly(ctx, id, repo_service.DeleteRepositoryOpts{IgnoreOrgTeams: true}); err != nil { return deleted, err } deleted++ diff --git a/services/repository/check.go b/services/repository/check.go index 7e680f3c58..1c9e16e02c 100644 --- a/services/repository/check.go +++ b/services/repository/check.go @@ -12,7 +12,6 @@ import ( "forgejo.org/models/db" repo_model "forgejo.org/models/repo" system_model "forgejo.org/models/system" - user_model "forgejo.org/models/user" "forgejo.org/modules/git" "forgejo.org/modules/log" repo_module "forgejo.org/modules/repository" @@ -146,7 +145,7 @@ func gatherMissingRepoRecords(ctx context.Context) (repo_model.RepositoryList, e } // DeleteMissingRepositories deletes all repository records that lost Git files. -func DeleteMissingRepositories(ctx context.Context, doer *user_model.User) error { +func DeleteMissingRepositories(ctx context.Context) error { repos, err := gatherMissingRepoRecords(ctx) if err != nil { return err @@ -163,7 +162,7 @@ func DeleteMissingRepositories(ctx context.Context, doer *user_model.User) error default: } log.Trace("Deleting %d/%d...", repo.OwnerID, repo.ID) - if err := DeleteRepositoryDirectly(ctx, doer, repo.ID); err != nil { + if err := DeleteRepositoryDirectly(ctx, repo.ID, DeleteRepositoryOpts{}); err != nil { log.Error("Failed to DeleteRepository %-v: Error: %v", repo, err) if err2 := system_model.CreateRepositoryNotice("Failed to DeleteRepository %s [%d]: Error: %v", repo.FullName(), repo.ID, err); err2 != nil { log.Error("CreateRepositoryNotice: %v", err) diff --git a/services/repository/create.go b/services/repository/create.go index b7232b27cc..43458bed5d 100644 --- a/services/repository/create.go +++ b/services/repository/create.go @@ -308,7 +308,7 @@ func CreateRepositoryDirectly(ctx context.Context, doer, u *user_model.User, opt return nil }); err != nil { if rollbackRepo != nil { - if errDelete := DeleteRepositoryDirectly(ctx, doer, rollbackRepo.ID); errDelete != nil { + if errDelete := DeleteRepositoryDirectly(ctx, rollbackRepo.ID, DeleteRepositoryOpts{}); errDelete != nil { log.Error("Rollback deleteRepository: %v", errDelete) } } diff --git a/services/repository/create_test.go b/services/repository/create_test.go index bd14a5e520..4669199151 100644 --- a/services/repository/create_test.go +++ b/services/repository/create_test.go @@ -130,7 +130,7 @@ func TestIncludesAllRepositoriesTeams(t *testing.T) { } // Remove repo and check teams repositories. - require.NoError(t, DeleteRepositoryDirectly(db.DefaultContext, user, repoIDs[0]), "DeleteRepository") + require.NoError(t, DeleteRepositoryDirectly(db.DefaultContext, repoIDs[0], DeleteRepositoryOpts{}), "DeleteRepository") teamRepos[0] = repoIDs[1:] teamRepos[1] = repoIDs[1:] teamRepos[3] = repoIDs[1:3] @@ -142,7 +142,7 @@ func TestIncludesAllRepositoriesTeams(t *testing.T) { // Wipe created items. for i, rid := range repoIDs { if i > 0 { // first repo already deleted. - require.NoError(t, DeleteRepositoryDirectly(db.DefaultContext, user, rid), "DeleteRepository %d", i) + require.NoError(t, DeleteRepositoryDirectly(db.DefaultContext, rid, DeleteRepositoryOpts{}), "DeleteRepository %d", i) } } require.NoError(t, organization.DeleteOrganization(db.DefaultContext, org), "DeleteOrganization") diff --git a/services/repository/delete.go b/services/repository/delete.go index 8766204f34..8544a86604 100644 --- a/services/repository/delete.go +++ b/services/repository/delete.go @@ -38,9 +38,17 @@ import ( "xorm.io/builder" ) +type DeleteRepositoryOpts struct { + // Don't modify teams if they are attached to this repository. + IgnoreOrgTeams bool + // Keep migration-related beans. Should only be used to cleanup data to + // start another migration. + KeepMigrationBeans bool +} + // 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, ignoreOrgTeams ...bool) error { +func DeleteRepositoryDirectly(ctx context.Context, repoID int64, opts DeleteRepositoryOpts) error { ctx, committer, err := db.TxContext(ctx) if err != nil { return err @@ -75,7 +83,7 @@ func DeleteRepositoryDirectly(ctx context.Context, doer *user_model.User, repoID // 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 !opts.IgnoreOrgTeams { if org, err = user_model.GetUserByID(ctx, repo.OwnerID); err != nil { return err } @@ -88,7 +96,7 @@ func DeleteRepositoryDirectly(ctx context.Context, doer *user_model.User, repoID } needRewriteKeysFile := len(deployKeys) > 0 for _, dKey := range deployKeys { - if err := models.DeleteDeployKey(ctx, doer, dKey.ID); err != nil { + if err := models.DeleteDeployKey(ctx, dKey.ID, repoID); err != nil { return fmt.Errorf("deleteDeployKeys: %w", err) } } @@ -173,9 +181,7 @@ func DeleteRepositoryDirectly(ctx context.Context, doer *user_model.User, repoID &repo_model.Release{RepoID: repoID}, &repo_model.RepoIndexerStatus{RepoID: repoID}, &repo_model.Redirect{RedirectRepoID: repoID}, - &repo_model.RepoUnit{RepoID: repoID}, &repo_model.Star{RepoID: repoID}, - &admin_model.Task{RepoID: repoID}, &repo_model.Watch{RepoID: repoID}, &webhook.Webhook{RepoID: repoID}, &secret_model.Secret{RepoID: repoID}, @@ -195,18 +201,29 @@ func DeleteRepositoryDirectly(ctx context.Context, doer *user_model.User, repoID return fmt.Errorf("deleteBeans: %w", err) } + if !opts.KeepMigrationBeans { + if err := db.DeleteBeans(ctx, + &admin_model.Task{RepoID: repoID}, + &repo_model.RepoUnit{RepoID: repoID}, + ); err != nil { + return fmt.Errorf("deleteBeans: %w", err) + } + } + // Delete Pulls and related objects if err := issues_model.DeletePullsByBaseRepoID(ctx, repoID); err != nil { return err } - if cnt, err := sess.ID(repoID).Delete(&repo_model.Repository{}); err != nil { - return err - } else if cnt != 1 { - return repo_model.ErrRepoNotExist{ - ID: repoID, - OwnerName: "", - Name: "", + if !opts.KeepMigrationBeans { + if cnt, err := sess.ID(repoID).Delete(&repo_model.Repository{}); err != nil { + return err + } else if cnt != 1 { + return repo_model.ErrRepoNotExist{ + ID: repoID, + OwnerName: "", + Name: "", + } } } @@ -491,7 +508,7 @@ func DeleteOwnerRepositoriesDirectly(ctx context.Context, owner *user_model.User break } for _, repo := range repos { - if err := DeleteRepositoryDirectly(ctx, owner, repo.ID); err != nil { + if err := DeleteRepositoryDirectly(ctx, repo.ID, DeleteRepositoryOpts{}); err != nil { return fmt.Errorf("unable to delete repository %s for %s[%d]. Error: %w", repo.Name, owner.Name, owner.ID, err) } } diff --git a/services/repository/repository.go b/services/repository/repository.go index 0d5ce647e0..8b206f79d3 100644 --- a/services/repository/repository.go +++ b/services/repository/repository.go @@ -63,7 +63,7 @@ func DeleteRepository(ctx context.Context, doer *user_model.User, repo *repo_mod notify_service.DeleteRepository(ctx, doer, repo) } - return DeleteRepositoryDirectly(ctx, doer, repo.ID) + return DeleteRepositoryDirectly(ctx, repo.ID, DeleteRepositoryOpts{}) } // PushCreateRepo creates a repository when a new repository is pushed to an appropriate namespace diff --git a/services/task/fixtures/TestRetryMigrateTask/issue.yml b/services/task/fixtures/TestRetryMigrateTask/issue.yml new file mode 100644 index 0000000000..3fa46ff7d1 --- /dev/null +++ b/services/task/fixtures/TestRetryMigrateTask/issue.yml @@ -0,0 +1,16 @@ +- + id: 1001 + repo_id: 20 + index: 1 + poster_id: 1 + original_author_id: 0 + name: eepy + content: I am part of a migration. + milestone_id: 0 + priority: 0 + is_closed: false + is_pull: false + num_comments: 0 + created_unix: 1777647620 + updated_unix: 1777647620 + is_locked: false diff --git a/services/task/fixtures/TestRetryMigrateTask/repo_unit.yml b/services/task/fixtures/TestRetryMigrateTask/repo_unit.yml new file mode 100644 index 0000000000..f8e2ae5c84 --- /dev/null +++ b/services/task/fixtures/TestRetryMigrateTask/repo_unit.yml @@ -0,0 +1,34 @@ +- + id: 1001 + repo_id: 20 + type: 1 + config: "{}" + created_unix: 1777646333 + +- + id: 1002 + repo_id: 20 + type: 2 + config: "{\"EnableTimetracker\":false,\"AllowOnlyContributorsToTrackTime\":false}" + created_unix: 1777646333 + +- + id: 1003 + repo_id: 20 + type: 3 + config: "{\"IgnoreWhitespaceConflicts\":true,\"AllowMerge\":true,\"AllowRebase\":false,\"AllowRebaseMerge\":true,\"AllowSquash\":false}" + created_unix: 1777646333 + +- + id: 1004 + repo_id: 20 + type: 4 + config: "{}" + created_unix: 1777646333 + +- + id: 1005 + repo_id: 20 + type: 5 + config: "{}" + created_unix: 1777646333 diff --git a/services/task/fixtures/TestRetryMigrateTask/task.yml b/services/task/fixtures/TestRetryMigrateTask/task.yml new file mode 100644 index 0000000000..92bb0283b4 --- /dev/null +++ b/services/task/fixtures/TestRetryMigrateTask/task.yml @@ -0,0 +1,22 @@ +- + id: 1001 + doer_id: 2 + owner_id: 2 + repo_id: 1 + type: 0 + status: 4 + start_time: 1777645339 + end_time: 1777645339 + created: 1777645339 + +- + id: 1002 + doer_id: 2 + owner_id: 2 + repo_id: 20 + type: 0 + status: 3 + message: 'canceled' + start_time: 1777645339 + end_time: 1777645339 + created: 1777645339 diff --git a/services/task/task.go b/services/task/task.go index 0012b12cb0..695b321228 100644 --- a/services/task/task.go +++ b/services/task/task.go @@ -133,25 +133,31 @@ func CreateMigrateTask(ctx context.Context, doer, u *user_model.User, opts base. return task, nil } -// RetryMigrateTask retry a migrate task +// RetryMigrateTask will retry the migration. +// All data, from a previous migration, is deleted before it's retried. func RetryMigrateTask(ctx context.Context, repoID int64) error { migratingTask, err := admin_model.GetMigratingTask(ctx, repoID) if err != nil { - log.Error("GetMigratingTask: %v", err) - return err + return fmt.Errorf("GetMigratingTask: %w", err) } if migratingTask.Status == structs.TaskStatusQueued || migratingTask.Status == structs.TaskStatusRunning { return nil } - // TODO Need to removing the storage/database garbage brought by the failed task + // The migration is being retried, it could've failed for a variety of cases. + // In most cases however, some data already got uploaded to the disk or + // database. The migration code makes the assumption this is not the case and + // if we do not clean it up, the retry attempt will fail with absolute + // certainty. + if err := repo_service.DeleteRepositoryDirectly(ctx, repoID, repo_service.DeleteRepositoryOpts{IgnoreOrgTeams: true, KeepMigrationBeans: true}); err != nil { + return fmt.Errorf("DeleteRepositoryDirectly: %v", err) + } // Reset task status and messages migratingTask.Status = structs.TaskStatusQueued migratingTask.Message = "" if err = migratingTask.UpdateCols(ctx, "status", "message"); err != nil { - log.Error("task.UpdateCols failed: %v", err) - return err + return fmt.Errorf("task.UpdateCols: %w", err) } return taskQueue.Push(migratingTask) diff --git a/services/task/task_test.go b/services/task/task_test.go index 50ab1394a4..7444f6df5d 100644 --- a/services/task/task_test.go +++ b/services/task/task_test.go @@ -1,12 +1,21 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + package task import ( "testing" admin_model "forgejo.org/models/admin" + issues_model "forgejo.org/models/issues" + repo_model "forgejo.org/models/repo" "forgejo.org/models/unittest" user_model "forgejo.org/models/user" "forgejo.org/modules/migration" + "forgejo.org/modules/queue" + "forgejo.org/modules/setting" + "forgejo.org/modules/structs" + "forgejo.org/modules/test" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -50,3 +59,46 @@ func TestCreateMigrateTask(t *testing.T) { assert.Equal(t, "https://admin:password@example.com", config.CloneAddr) }) } + +func TestRetryMigrateTask(t *testing.T) { + defer unittest.OverrideFixtures("services/task/fixtures/TestRetryMigrateTask/")() + require.NoError(t, unittest.PrepareTestDatabase()) + + t.Run("Migrate task does not exist", func(t *testing.T) { + err := RetryMigrateTask(t.Context(), 100) + require.ErrorIs(t, err, admin_model.ErrTaskDoesNotExist{RepoID: 100}) + }) + + t.Run("Normal", func(t *testing.T) { + // Override the task queue temporarily. + called := false + testQueue, err := queue.NewWorkerPoolQueueWithContext(t.Context(), "task", setting.QueueSettings{Type: "immediate"}, func(items ...*admin_model.Task) []*admin_model.Task { + if assert.Len(t, items, 1) { + assert.Empty(t, items[0].Message) + assert.Equal(t, structs.TaskStatusQueued, items[0].Status) + assert.EqualValues(t, 1002, items[0].ID) + } + called = true + return nil + }, true) + require.NoError(t, err) + defer test.MockVariableValue(&taskQueue, testQueue)() + + // Preconditions. + unittest.AssertExistsIf(t, true, &repo_model.Repository{ID: 20}) + unittest.AssertExistsIf(t, true, &admin_model.Task{RepoID: 20, Status: structs.TaskStatusFailed}) + unittest.AssertExistsIf(t, true, &issues_model.Issue{RepoID: 20}) + unittest.AssertCount(t, &repo_model.RepoUnit{RepoID: 20}, 5) + + require.NoError(t, RetryMigrateTask(t.Context(), 20)) + + // Verify queue was called. + assert.True(t, called) + // Verify some beans were NOT deleted. + unittest.AssertExistsIf(t, true, &repo_model.Repository{ID: 20}) + unittest.AssertExistsIf(t, true, &admin_model.Task{RepoID: 20, Status: structs.TaskStatusQueued}) + unittest.AssertCount(t, &repo_model.RepoUnit{RepoID: 20}, 5) + // Verify some beans were deleted. + unittest.AssertExistsIf(t, false, &issues_model.Issue{RepoID: 20}) + }) +} diff --git a/tests/integration/api_repo_test.go b/tests/integration/api_repo_test.go index 29edff7ac0..afa99cc17f 100644 --- a/tests/integration/api_repo_test.go +++ b/tests/integration/api_repo_test.go @@ -957,7 +957,7 @@ func TestAPIRepoTransfer(t *testing.T) { // cleanup repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: apiRepo.ID}) - _ = repo_service.DeleteRepositoryDirectly(db.DefaultContext, user, repo.ID) + require.NoError(t, repo_service.DeleteRepositoryDirectly(db.DefaultContext, repo.ID, repo_service.DeleteRepositoryOpts{})) } // This test verifies that a repo-specific access token with `write:repository` scope is not a sufficient to transfer a diff --git a/tests/integration/ephemeral_actions_runner_deletion_test.go b/tests/integration/ephemeral_actions_runner_deletion_test.go index 9e995c4864..54448bd47a 100644 --- a/tests/integration/ephemeral_actions_runner_deletion_test.go +++ b/tests/integration/ephemeral_actions_runner_deletion_test.go @@ -116,8 +116,7 @@ func TestEphemeralRunnerDeletionOnRepositoryDeletion(t *testing.T) { task := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionTask{ID: 10054}) assert.Equal(t, actions_model.StatusRunning, task.Status) - user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5}) - err = repo_service.DeleteRepositoryDirectly(t.Context(), user, task.RepoID, true) + err = repo_service.DeleteRepositoryDirectly(t.Context(), task.RepoID, repo_service.DeleteRepositoryOpts{IgnoreOrgTeams: true}) require.NoError(t, err) _, err = actions_model.GetRunnerByID(t.Context(), 10000008) diff --git a/tests/integration/git_push_test.go b/tests/integration/git_push_test.go index c7c4f6750e..3a8c653efb 100644 --- a/tests/integration/git_push_test.go +++ b/tests/integration/git_push_test.go @@ -201,7 +201,7 @@ func runTestGitPush(t *testing.T, u *url.URL, objectFormat git.ObjectFormat, git assert.Equal(t, commitID, branch.CommitID) } - require.NoError(t, repo_service.DeleteRepositoryDirectly(db.DefaultContext, user, repo.ID)) + require.NoError(t, repo_service.DeleteRepositoryDirectly(db.DefaultContext, repo.ID, repo_service.DeleteRepositoryOpts{})) } func TestOptionsGitPush(t *testing.T) { @@ -310,6 +310,6 @@ func testOptionsGitPush(t *testing.T, u *url.URL) { assert.True(t, logFiltered[0]) }) - require.NoError(t, repo_service.DeleteRepositoryDirectly(db.DefaultContext, user, repo.ID)) + require.NoError(t, repo_service.DeleteRepositoryDirectly(db.DefaultContext, repo.ID, repo_service.DeleteRepositoryOpts{})) }) }