mirror of
https://github.com/go-gitea/gitea.git
synced 2026-08-03 03:19:39 +00:00
refactor: retry file remove/rename when a file is busy and clean up os detection (#38588)
Rename functions "util.Remove" (remove.go) to "util.RemoveWithRetry" (file_retry.go) and add comments to clarify their behaviors, also add tests. Refactor callers: when no concurrent access (cmd cli, migration, app init, test), use "os.Xxx" directly. More details are in `modules/util/file_retry.go` By the way, clean up OS (windows) detection, make FileURLToPath test always run
This commit is contained in:
@@ -50,8 +50,8 @@ func rewriteAllPublicKeys(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
t.Close()
|
||||
if err := util.Remove(tmpPath); err != nil {
|
||||
_ = t.Close()
|
||||
if err := util.RemoveWithRetry(tmpPath); err != nil {
|
||||
log.Warn("Unable to remove temporary authorized keys file: %s: Error: %v", tmpPath, err)
|
||||
}
|
||||
}()
|
||||
@@ -74,6 +74,6 @@ func rewriteAllPublicKeys(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
t.Close()
|
||||
return util.Rename(tmpPath, fPath)
|
||||
_ = t.Close()
|
||||
return util.RenameWithRetry(tmpPath, fPath)
|
||||
}
|
||||
|
||||
@@ -61,8 +61,8 @@ func rewriteAllPrincipalKeys(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
t.Close()
|
||||
os.Remove(tmpPath)
|
||||
_ = t.Close()
|
||||
_ = util.RemoveWithRetry(tmpPath)
|
||||
}()
|
||||
|
||||
if setting.SSH.AuthorizedPrincipalsBackup {
|
||||
@@ -83,8 +83,8 @@ func rewriteAllPrincipalKeys(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
t.Close()
|
||||
return util.Rename(tmpPath, fPath)
|
||||
_ = t.Close()
|
||||
return util.RenameWithRetry(tmpPath, fPath)
|
||||
}
|
||||
|
||||
func regeneratePrincipalKeys(ctx context.Context, t io.Writer) error {
|
||||
|
||||
+1
-1
@@ -89,7 +89,7 @@ func DeleteOrganization(ctx context.Context, org *org_model.Organization, purge
|
||||
// so just keep error logs of those operations.
|
||||
path := user_model.UserPath(org.Name)
|
||||
|
||||
if err := util.RemoveAll(path); err != nil {
|
||||
if err := util.RemoveAllWithRetry(path); err != nil {
|
||||
return fmt.Errorf("failed to RemoveAll %s: %w", path, err)
|
||||
}
|
||||
|
||||
|
||||
@@ -188,7 +188,7 @@ func attemptMerge(ctx context.Context, file *unmergedFile, tmpBasePath string, t
|
||||
}
|
||||
root = strings.TrimSpace(root)
|
||||
defer func() {
|
||||
_ = util.Remove(filepath.Join(tmpBasePath, root))
|
||||
_ = util.RemoveWithRetry(filepath.Join(tmpBasePath, root))
|
||||
}()
|
||||
|
||||
base, _, err := gitcmd.NewCommand("unpack-file").AddDynamicArguments(file.stage2.sha).WithRepo(tmpGitRepo).RunStdString(ctx)
|
||||
@@ -197,7 +197,7 @@ func attemptMerge(ctx context.Context, file *unmergedFile, tmpBasePath string, t
|
||||
}
|
||||
base = strings.TrimSpace(filepath.Join(tmpBasePath, base))
|
||||
defer func() {
|
||||
_ = util.Remove(base)
|
||||
_ = util.RemoveWithRetry(base)
|
||||
}()
|
||||
head, _, err := gitcmd.NewCommand("unpack-file").AddDynamicArguments(file.stage3.sha).WithRepo(tmpGitRepo).RunStdString(ctx)
|
||||
if err != nil {
|
||||
@@ -205,7 +205,7 @@ func attemptMerge(ctx context.Context, file *unmergedFile, tmpBasePath string, t
|
||||
}
|
||||
head = strings.TrimSpace(head)
|
||||
defer func() {
|
||||
_ = util.Remove(filepath.Join(tmpBasePath, head))
|
||||
_ = util.RemoveWithRetry(filepath.Join(tmpBasePath, head))
|
||||
}()
|
||||
|
||||
// now git merge-file annoyingly takes a different order to the merge-tree ...
|
||||
|
||||
@@ -232,7 +232,7 @@ func processGiteaTemplateFile(ctx context.Context, tmpDir string, templateRepo,
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = util.RemoveAll(util.FilePathJoinAbs(tmpDir, ".git")); err != nil {
|
||||
if err = util.RemoveAllWithRetry(util.FilePathJoinAbs(tmpDir, ".git")); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return skippedFiles, nil
|
||||
@@ -256,7 +256,7 @@ func generateRepoCommit(ctx context.Context, repo, templateRepo, generateRepo *r
|
||||
return fmt.Errorf("GetTemplateSubmoduleCommits: %w", err)
|
||||
}
|
||||
|
||||
if err = util.RemoveAll(filepath.Join(tmpDir, ".git")); err != nil {
|
||||
if err = util.RemoveAllWithRetry(filepath.Join(tmpDir, ".git")); err != nil {
|
||||
return fmt.Errorf("remove git dir: %w", err)
|
||||
}
|
||||
|
||||
|
||||
@@ -11,14 +11,14 @@ import (
|
||||
|
||||
activities_model "gitea.dev/models/activities"
|
||||
"gitea.dev/models/db"
|
||||
"gitea.dev/models/git"
|
||||
git_model "gitea.dev/models/git"
|
||||
issues_model "gitea.dev/models/issues"
|
||||
"gitea.dev/models/organization"
|
||||
access_model "gitea.dev/models/perm/access"
|
||||
repo_model "gitea.dev/models/repo"
|
||||
"gitea.dev/models/unit"
|
||||
user_model "gitea.dev/models/user"
|
||||
git2 "gitea.dev/modules/git"
|
||||
"gitea.dev/modules/git"
|
||||
"gitea.dev/modules/git/gitrepo"
|
||||
"gitea.dev/modules/graceful"
|
||||
issue_indexer "gitea.dev/modules/indexer/issues"
|
||||
@@ -33,9 +33,9 @@ import (
|
||||
|
||||
// WebSearchRepository represents a repository returned by web search
|
||||
type WebSearchRepository struct {
|
||||
Repository *structs.Repository `json:"repository"`
|
||||
LatestCommitStatus *git.CommitStatus `json:"latest_commit_status"`
|
||||
LocaleLatestCommitStatus string `json:"locale_latest_commit_status"`
|
||||
Repository *structs.Repository `json:"repository"`
|
||||
LatestCommitStatus *git_model.CommitStatus `json:"latest_commit_status"`
|
||||
LocaleLatestCommitStatus string `json:"locale_latest_commit_status"`
|
||||
}
|
||||
|
||||
// WebSearchResults results of a successful web search
|
||||
@@ -218,7 +218,7 @@ func CheckDaemonExportOK(ctx context.Context, repo *repo_model.Repository) error
|
||||
|
||||
// Create/Remove git-daemon-export-ok for git-daemon...
|
||||
daemonExportFile := `git-daemon-export-ok`
|
||||
isExist, err := git2.IsRepoFileExist(ctx, repo, daemonExportFile)
|
||||
isExist, err := git.IsRepoFileExist(ctx, repo, daemonExportFile)
|
||||
if err != nil {
|
||||
log.Error("Unable to check if %s exists. Error: %v", daemonExportFile, err)
|
||||
return err
|
||||
@@ -226,11 +226,11 @@ func CheckDaemonExportOK(ctx context.Context, repo *repo_model.Repository) error
|
||||
|
||||
isPublic := !repo.IsPrivate && repo.Owner.Visibility == structs.VisibleTypePublic
|
||||
if !isPublic && isExist {
|
||||
if err = git2.RemoveRepoFileOrDir(ctx, repo, daemonExportFile); err != nil {
|
||||
if err = git.RemoveRepoFileOrDir(ctx, repo, daemonExportFile); err != nil {
|
||||
log.Error("Failed to remove %s: %v", daemonExportFile, err)
|
||||
}
|
||||
} else if isPublic && !isExist {
|
||||
if f, err := git2.CreateRepoFile(ctx, repo, daemonExportFile); err != nil {
|
||||
if f, err := git.CreateRepoFile(ctx, repo, daemonExportFile); err != nil {
|
||||
log.Error("Failed to create %s: %v", daemonExportFile, err)
|
||||
} else {
|
||||
f.Close()
|
||||
@@ -309,7 +309,7 @@ func updateRepository(ctx context.Context, repo *repo_model.Repository, visibili
|
||||
}
|
||||
|
||||
func HasWiki(ctx context.Context, repo *repo_model.Repository) bool {
|
||||
hasWiki, err := git2.IsRepositoryExist(ctx, repo.WikiStorageRepo())
|
||||
hasWiki, err := git.IsRepositoryExist(ctx, repo.WikiStorageRepo())
|
||||
if err != nil {
|
||||
log.Error("gitrepo.IsRepositoryExist: %v", err)
|
||||
}
|
||||
@@ -333,7 +333,7 @@ func CheckCreateRepository(ctx context.Context, doer, owner *user_model.User, na
|
||||
return repo_model.ErrRepoAlreadyExist{Uname: owner.Name, Name: name}
|
||||
}
|
||||
repo := gitrepo.CodeRepoByName(owner.Name, name)
|
||||
isExist, err := git2.IsRepositoryExist(ctx, repo)
|
||||
isExist, err := git.IsRepositoryExist(ctx, repo)
|
||||
if err != nil {
|
||||
log.Error("Unable to check if repo %s/%s exists, error: %v", owner.Name, name, err)
|
||||
return err
|
||||
|
||||
@@ -98,7 +98,7 @@ func RenameUser(ctx context.Context, u *user_model.User, newUserName string, doe
|
||||
}
|
||||
|
||||
// Do not fail if directory does not exist
|
||||
if err = util.Rename(user_model.UserPath(oldUserName), user_model.UserPath(newUserName)); err != nil && !os.IsNotExist(err) {
|
||||
if err = util.RenameWithRetry(user_model.UserPath(oldUserName), user_model.UserPath(newUserName)); err != nil && !os.IsNotExist(err) {
|
||||
u.Name = oldUserName
|
||||
u.LowerName = strings.ToLower(oldUserName)
|
||||
return fmt.Errorf("rename user directory: %w", err)
|
||||
@@ -107,7 +107,7 @@ func RenameUser(ctx context.Context, u *user_model.User, newUserName string, doe
|
||||
if err = committer.Commit(); err != nil {
|
||||
u.Name = oldUserName
|
||||
u.LowerName = strings.ToLower(oldUserName)
|
||||
if err2 := util.Rename(user_model.UserPath(newUserName), user_model.UserPath(oldUserName)); err2 != nil && !os.IsNotExist(err2) {
|
||||
if err2 := util.RenameWithRetry(user_model.UserPath(newUserName), user_model.UserPath(oldUserName)); err2 != nil && !os.IsNotExist(err2) {
|
||||
log.Error("Unable to rollback directory change during failed username change from: %s to: %s. DB Error: %v. Filesystem Error: %v", oldUserName, newUserName, err, err2)
|
||||
return fmt.Errorf("failed to rollback directory change during failed username change from: %s to: %s. DB Error: %w. Filesystem Error: %v", oldUserName, newUserName, err, err2)
|
||||
}
|
||||
@@ -258,7 +258,7 @@ func DeleteUser(ctx context.Context, u *user_model.User, purge bool) error {
|
||||
|
||||
// Note: There are something just cannot be roll back, so just keep error logs of those operations.
|
||||
path := user_model.UserPath(u.Name)
|
||||
if err := util.RemoveAll(path); err != nil {
|
||||
if err := util.RemoveAllWithRetry(path); err != nil {
|
||||
err = fmt.Errorf("failed to RemoveAll %s: %w", path, err)
|
||||
_ = system_model.CreateNotice(ctx, system_model.NoticeTask, fmt.Sprintf("delete user '%s': %v", u.Name, err))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user