aboutsummaryrefslogtreecommitdiffstats
path: root/services/repository/create.go
diff options
context:
space:
mode:
Diffstat (limited to 'services/repository/create.go')
-rw-r--r--services/repository/create.go230
1 files changed, 119 insertions, 111 deletions
diff --git a/services/repository/create.go b/services/repository/create.go
index 1a6a68b35a..bed02e5d7e 100644
--- a/services/repository/create.go
+++ b/services/repository/create.go
@@ -17,6 +17,7 @@ import (
"code.gitea.io/gitea/models/perm"
access_model "code.gitea.io/gitea/models/perm/access"
repo_model "code.gitea.io/gitea/models/repo"
+ system_model "code.gitea.io/gitea/models/system"
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/models/webhook"
@@ -28,7 +29,6 @@ import (
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/templates/vars"
- "code.gitea.io/gitea/modules/util"
)
// CreateRepoOptions contains the create repository options
@@ -100,8 +100,8 @@ func prepareRepoCommit(ctx context.Context, repo *repo_model.Repository, tmpDir
// .gitignore
if len(opts.Gitignores) > 0 {
var buf bytes.Buffer
- names := strings.Split(opts.Gitignores, ",")
- for _, name := range names {
+ names := strings.SplitSeq(opts.Gitignores, ",")
+ for name := range names {
data, err = options.Gitignore(name)
if err != nil {
return fmt.Errorf("GetRepoInitFile[%s]: %w", name, err)
@@ -140,21 +140,20 @@ func prepareRepoCommit(ctx context.Context, repo *repo_model.Repository, tmpDir
// InitRepository initializes README and .gitignore if needed.
func initRepository(ctx context.Context, u *user_model.User, repo *repo_model.Repository, opts CreateRepoOptions) (err error) {
- if err = repo_module.CheckInitRepository(ctx, repo); err != nil {
- return err
+ // Init git bare new repository.
+ if err = git.InitRepository(ctx, repo.RepoPath(), true, repo.ObjectFormatName); err != nil {
+ return fmt.Errorf("git.InitRepository: %w", err)
+ } else if err = gitrepo.CreateDelegateHooks(ctx, repo); err != nil {
+ return fmt.Errorf("createDelegateHooks: %w", err)
}
// Initialize repository according to user's choice.
if opts.AutoInit {
- tmpDir, err := os.MkdirTemp(os.TempDir(), "gitea-"+repo.Name)
+ tmpDir, cleanup, err := setting.AppDataTempDir("git-repo-content").MkdirTempRandom("repos-" + repo.Name)
if err != nil {
- return fmt.Errorf("Failed to create temp dir for repository %s: %w", repo.FullName(), err)
+ return fmt.Errorf("failed to create temp dir for repository %s: %w", repo.FullName(), err)
}
- defer func() {
- if err := util.RemoveAll(tmpDir); err != nil {
- log.Warn("Unable to remove temporary directory: %s: Error: %v", tmpDir, err)
- }
- }()
+ defer cleanup()
if err = prepareRepoCommit(ctx, repo, tmpDir, opts); err != nil {
return fmt.Errorf("prepareRepoCommit: %w", err)
@@ -192,18 +191,25 @@ func initRepository(ctx context.Context, u *user_model.User, repo *repo_model.Re
}
}
- if err = UpdateRepository(ctx, repo, false); err != nil {
+ if err = repo_model.UpdateRepositoryColsNoAutoTime(ctx, repo, "is_empty", "default_branch", "default_wiki_branch"); err != nil {
return fmt.Errorf("updateRepository: %w", err)
}
+ if err = repo_module.UpdateRepoSize(ctx, repo); err != nil {
+ log.Error("Failed to update size for repository: %v", err)
+ }
+
return nil
}
// CreateRepositoryDirectly creates a repository for the user/organization.
-func CreateRepositoryDirectly(ctx context.Context, doer, u *user_model.User, opts CreateRepoOptions) (*repo_model.Repository, error) {
- if !doer.IsAdmin && !u.CanCreateRepo() {
+// if needsUpdateToReady is true, it will update the repository status to ready when success
+func CreateRepositoryDirectly(ctx context.Context, doer, owner *user_model.User,
+ opts CreateRepoOptions, needsUpdateToReady bool,
+) (*repo_model.Repository, error) {
+ if !doer.CanCreateRepoIn(owner) {
return nil, repo_model.ErrReachLimitOfRepo{
- Limit: u.MaxRepoCreation,
+ Limit: owner.MaxRepoCreation,
}
}
@@ -223,9 +229,9 @@ func CreateRepositoryDirectly(ctx context.Context, doer, u *user_model.User, opt
}
repo := &repo_model.Repository{
- OwnerID: u.ID,
- Owner: u,
- OwnerName: u.Name,
+ OwnerID: owner.ID,
+ Owner: owner,
+ OwnerName: owner.Name,
Name: opts.Name,
LowerName: strings.ToLower(opts.Name),
Description: opts.Description,
@@ -244,100 +250,91 @@ func CreateRepositoryDirectly(ctx context.Context, doer, u *user_model.User, opt
ObjectFormatName: opts.ObjectFormatName,
}
- var rollbackRepo *repo_model.Repository
-
- if err := db.WithTx(ctx, func(ctx context.Context) error {
- if err := CreateRepositoryByExample(ctx, doer, u, repo, false, false); err != nil {
- return err
- }
-
- // No need for init mirror.
- if opts.IsMirror {
- return nil
- }
+ // 1 - create the repository database operations first
+ err := db.WithTx(ctx, func(ctx context.Context) error {
+ return createRepositoryInDB(ctx, doer, owner, repo, false)
+ })
+ if err != nil {
+ return nil, err
+ }
- isExist, err := gitrepo.IsRepositoryExist(ctx, repo)
+ // last - clean up if something goes wrong
+ // WARNING: Don't override all later err with local variables
+ defer func() {
if err != nil {
- log.Error("Unable to check if %s exists. Error: %v", repo.FullName(), err)
- return err
- }
- if isExist {
- // repo already exists - We have two or three options.
- // 1. We fail stating that the directory exists
- // 2. We create the db repository to go with this data and adopt the git repo
- // 3. We delete it and start afresh
- //
- // Previously Gitea would just delete and start afresh - this was naughty.
- // So we will now fail and delegate to other functionality to adopt or delete
- log.Error("Files already exist in %s and we are not going to adopt or delete.", repo.FullName())
- return repo_model.ErrRepoFilesAlreadyExist{
- Uname: u.Name,
- Name: repo.Name,
- }
+ // we can not use the ctx because it maybe canceled or timeout
+ cleanupRepository(repo.ID)
}
+ }()
- if err = initRepository(ctx, doer, repo, opts); err != nil {
- if err2 := gitrepo.DeleteRepository(ctx, repo); err2 != nil {
- log.Error("initRepository: %v", err)
- return fmt.Errorf(
- "delete repo directory %s/%s failed(2): %v", u.Name, repo.Name, err2)
- }
- return fmt.Errorf("initRepository: %w", err)
- }
+ // No need for init mirror.
+ if opts.IsMirror {
+ return repo, nil
+ }
- // Initialize Issue Labels if selected
- if len(opts.IssueLabels) > 0 {
- if err = repo_module.InitializeLabels(ctx, repo.ID, opts.IssueLabels, false); err != nil {
- rollbackRepo = repo
- rollbackRepo.OwnerID = u.ID
- return fmt.Errorf("InitializeLabels: %w", err)
- }
+ // 2 - check whether the repository with the same storage exists
+ var isExist bool
+ isExist, err = gitrepo.IsRepositoryExist(ctx, repo)
+ if err != nil {
+ log.Error("Unable to check if %s exists. Error: %v", repo.FullName(), err)
+ return nil, err
+ }
+ if isExist {
+ log.Error("Files already exist in %s and we are not going to adopt or delete.", repo.FullName())
+ // Don't return directly, we need err in defer to cleanupRepository
+ err = repo_model.ErrRepoFilesAlreadyExist{
+ Uname: repo.OwnerName,
+ Name: repo.Name,
}
+ return nil, err
+ }
- if err := repo_module.CheckDaemonExportOK(ctx, repo); err != nil {
- return fmt.Errorf("checkDaemonExportOK: %w", err)
- }
+ // 3 - init git repository in storage
+ if err = initRepository(ctx, doer, repo, opts); err != nil {
+ return nil, fmt.Errorf("initRepository: %w", err)
+ }
- if stdout, _, err := git.NewCommand("update-server-info").
- RunStdString(ctx, &git.RunOpts{Dir: repo.RepoPath()}); err != nil {
- log.Error("CreateRepository(git update-server-info) in %v: Stdout: %s\nError: %v", repo, stdout, err)
- rollbackRepo = repo
- rollbackRepo.OwnerID = u.ID
- return fmt.Errorf("CreateRepository(git update-server-info): %w", err)
+ // 4 - Initialize Issue Labels if selected
+ if len(opts.IssueLabels) > 0 {
+ if err = repo_module.InitializeLabels(ctx, repo.ID, opts.IssueLabels, false); err != nil {
+ return nil, fmt.Errorf("InitializeLabels: %w", err)
}
+ }
- // update licenses
- var licenses []string
- if len(opts.License) > 0 {
- licenses = append(licenses, opts.License)
+ // 5 - Update the git repository
+ if err = updateGitRepoAfterCreate(ctx, repo); err != nil {
+ return nil, fmt.Errorf("updateGitRepoAfterCreate: %w", err)
+ }
- stdout, _, err := git.NewCommand("rev-parse", "HEAD").RunStdString(ctx, &git.RunOpts{Dir: repo.RepoPath()})
- if err != nil {
- log.Error("CreateRepository(git rev-parse HEAD) in %v: Stdout: %s\nError: %v", repo, stdout, err)
- rollbackRepo = repo
- rollbackRepo.OwnerID = u.ID
- return fmt.Errorf("CreateRepository(git rev-parse HEAD): %w", err)
- }
- if err := repo_model.UpdateRepoLicenses(ctx, repo, stdout, licenses); err != nil {
- return err
- }
+ // 6 - update licenses
+ var licenses []string
+ if len(opts.License) > 0 {
+ licenses = append(licenses, opts.License)
+
+ var stdout string
+ stdout, _, err = git.NewCommand("rev-parse", "HEAD").RunStdString(ctx, &git.RunOpts{Dir: repo.RepoPath()})
+ if err != nil {
+ log.Error("CreateRepository(git rev-parse HEAD) in %v: Stdout: %s\nError: %v", repo, stdout, err)
+ return nil, fmt.Errorf("CreateRepository(git rev-parse HEAD): %w", err)
}
- return nil
- }); err != nil {
- if rollbackRepo != nil {
- if errDelete := DeleteRepositoryDirectly(ctx, doer, rollbackRepo.ID); errDelete != nil {
- log.Error("Rollback deleteRepository: %v", errDelete)
- }
+ if err = repo_model.UpdateRepoLicenses(ctx, repo, stdout, licenses); err != nil {
+ return nil, err
}
+ }
- return nil, err
+ // 7 - update repository status to be ready
+ if needsUpdateToReady {
+ repo.Status = repo_model.RepositoryReady
+ if err = repo_model.UpdateRepositoryColsWithAutoTime(ctx, repo, "status"); err != nil {
+ return nil, fmt.Errorf("UpdateRepositoryCols: %w", err)
+ }
}
return repo, nil
}
-// CreateRepositoryByExample creates a repository for the user/organization.
-func CreateRepositoryByExample(ctx context.Context, doer, u *user_model.User, repo *repo_model.Repository, overwriteOrAdopt, isFork bool) (err error) {
+// createRepositoryInDB creates a repository for the user/organization.
+func createRepositoryInDB(ctx context.Context, doer, u *user_model.User, repo *repo_model.Repository, isFork bool) (err error) {
if err = repo_model.IsUsableRepoName(repo.Name); err != nil {
return err
}
@@ -352,19 +349,6 @@ func CreateRepositoryByExample(ctx context.Context, doer, u *user_model.User, re
}
}
- isExist, err := gitrepo.IsRepositoryExist(ctx, repo)
- if err != nil {
- log.Error("Unable to check if %s exists. Error: %v", repo.FullName(), err)
- return err
- }
- if !overwriteOrAdopt && isExist {
- log.Error("Files already exist in %s and we are not going to adopt or delete.", repo.FullName())
- return repo_model.ErrRepoFilesAlreadyExist{
- Uname: u.Name,
- Name: repo.Name,
- }
- }
-
if err = db.Insert(ctx, repo); err != nil {
return err
}
@@ -384,7 +368,8 @@ func CreateRepositoryByExample(ctx context.Context, doer, u *user_model.User, re
}
units := make([]repo_model.RepoUnit, 0, len(defaultUnits))
for _, tp := range defaultUnits {
- if tp == unit.TypeIssues {
+ switch tp {
+ case unit.TypeIssues:
units = append(units, repo_model.RepoUnit{
RepoID: repo.ID,
Type: tp,
@@ -394,7 +379,7 @@ func CreateRepositoryByExample(ctx context.Context, doer, u *user_model.User, re
EnableDependencies: setting.Service.DefaultEnableDependencies,
},
})
- } else if tp == unit.TypePullRequests {
+ case unit.TypePullRequests:
units = append(units, repo_model.RepoUnit{
RepoID: repo.ID,
Type: tp,
@@ -404,13 +389,13 @@ func CreateRepositoryByExample(ctx context.Context, doer, u *user_model.User, re
AllowRebaseUpdate: true,
},
})
- } else if tp == unit.TypeProjects {
+ case unit.TypeProjects:
units = append(units, repo_model.RepoUnit{
RepoID: repo.ID,
Type: tp,
Config: &repo_model.ProjectsConfig{ProjectsMode: repo_model.ProjectsModeAll},
})
- } else {
+ default:
units = append(units, repo_model.RepoUnit{
RepoID: repo.ID,
Type: tp,
@@ -472,3 +457,26 @@ func CreateRepositoryByExample(ctx context.Context, doer, u *user_model.User, re
return nil
}
+
+func cleanupRepository(repoID int64) {
+ if errDelete := DeleteRepositoryDirectly(db.DefaultContext, repoID); errDelete != nil {
+ log.Error("cleanupRepository failed: %v", errDelete)
+ // add system notice
+ if err := system_model.CreateRepositoryNotice("DeleteRepositoryDirectly failed when cleanup repository: %v", errDelete); err != nil {
+ log.Error("CreateRepositoryNotice: %v", err)
+ }
+ }
+}
+
+func updateGitRepoAfterCreate(ctx context.Context, repo *repo_model.Repository) error {
+ if err := checkDaemonExportOK(ctx, repo); err != nil {
+ return fmt.Errorf("checkDaemonExportOK: %w", err)
+ }
+
+ if stdout, _, err := git.NewCommand("update-server-info").
+ RunStdString(ctx, &git.RunOpts{Dir: repo.RepoPath()}); err != nil {
+ log.Error("CreateRepository(git update-server-info) in %v: Stdout: %s\nError: %v", repo, stdout, err)
+ return fmt.Errorf("CreateRepository(git update-server-info): %w", err)
+ }
+ return nil
+}