1
1
mirror of https://github.com/go-gitea/gitea synced 2025-07-22 18:28:37 +00:00

Rework create/fork/adopt/generate repository to make sure resources will be cleanup once failed (#31035)

Fix #28144 

To make the resources will be cleanup once failed. All repository
operations now follow a consistent pattern:

- 1. Create a database record for the repository with the status
being_migrated.
- 2. Register a deferred cleanup function to delete the repository and
its related data if the operation fails.
- 3.	Perform the actual Git and database operations step by step.
- 4. Upon successful completion, update the repository’s status to
ready.

The adopt operation is a special case — if it fails, the repository on
disk should not be deleted.
This commit is contained in:
Lunny Xiao
2025-04-07 22:12:54 -07:00
committed by GitHub
parent 90b509aafb
commit a100ac3306
14 changed files with 562 additions and 390 deletions

View File

@@ -6,15 +6,23 @@ package integration
import (
"fmt"
"net/http"
"os"
"path"
"strconv"
"strings"
"testing"
"time"
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/modules/util"
repo_service "code.gitea.io/gitea/services/repository"
"code.gitea.io/gitea/tests"
"github.com/PuerkitoBio/goquery"
@@ -493,3 +501,46 @@ func testViewCommit(t *testing.T) {
resp := MakeRequest(t, req, http.StatusNotFound)
assert.True(t, test.IsNormalPageCompleted(resp.Body.String()), "non-existing commit should render 404 page")
}
// TestGenerateRepository the test cannot succeed when moved as a unit test
func TestGenerateRepository(t *testing.T) {
defer tests.PrepareTestEnv(t)()
// a successful generate from template
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
repo44 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 44})
generatedRepo, err := repo_service.GenerateRepository(git.DefaultContext, user2, user2, repo44, repo_service.GenerateRepoOptions{
Name: "generated-from-template-44",
GitContent: true,
})
assert.NoError(t, err)
assert.NotNil(t, generatedRepo)
exist, err := util.IsExist(repo_model.RepoPath(user2.Name, generatedRepo.Name))
assert.NoError(t, err)
assert.True(t, exist)
unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerName: user2.Name, Name: generatedRepo.Name})
err = repo_service.DeleteRepositoryDirectly(db.DefaultContext, user2, generatedRepo.ID)
assert.NoError(t, err)
// a failed creating because some mock data
// create the repository directory so that the creation will fail after database record created.
assert.NoError(t, os.MkdirAll(repo_model.RepoPath(user2.Name, "generated-from-template-44"), os.ModePerm))
generatedRepo2, err := repo_service.GenerateRepository(db.DefaultContext, user2, user2, repo44, repo_service.GenerateRepoOptions{
Name: "generated-from-template-44",
GitContent: true,
})
assert.Nil(t, generatedRepo2)
assert.Error(t, err)
// assert the cleanup is successful
unittest.AssertNotExistsBean(t, &repo_model.Repository{OwnerName: user2.Name, Name: generatedRepo.Name})
exist, err = util.IsExist(repo_model.RepoPath(user2.Name, generatedRepo.Name))
assert.NoError(t, err)
assert.False(t, exist)
}