diff --git a/modules/gitrepo/cleanup.go b/modules/gitrepo/cleanup.go new file mode 100644 index 0000000000000..90fd61091e528 --- /dev/null +++ b/modules/gitrepo/cleanup.go @@ -0,0 +1,42 @@ +// Copyright 2025 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package gitrepo + +import ( + "context" + "os" + "path/filepath" + "time" +) + +var lockFiles = []string{ + "config.lock", + "objects/info/commit-graphs/commit-graph-chain.lock", +} + +// CleanupRepo cleans up the repository by removing unnecessary lock files. +func CleanupRepo(ctx context.Context, repo Repository) error { + return CleanFixedFileLocks(ctx, repo, time.Now().Add(-24*time.Hour)) +} + +// CleanFixedFileLocks removes lock files that haven't been modified since the last update. +func CleanFixedFileLocks(ctx context.Context, repo Repository, lastUpdated time.Time) error { + for _, lockFile := range lockFiles { + p := filepath.Join(repoPath(repo), lockFile) + fInfo, err := os.Stat(p) + if err != nil { + if os.IsNotExist(err) { + continue + } + return err + } + + if fInfo.ModTime().Before(lastUpdated) { + if err := os.Remove(p); err != nil { + return err + } + } + } + return nil +} diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index a974b17225674..912c116842206 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -3080,6 +3080,7 @@ dashboard.sync_branch.started = Branches Sync started dashboard.sync_tag.started = Tags Sync started dashboard.rebuild_issue_indexer = Rebuild issue indexer dashboard.sync_repo_licenses = Sync repo licenses +dashboard.cleanup_repo_lock_files = Clean up repository lock files users.user_manage_panel = User Account Management users.new_account = Create User Account diff --git a/services/cron/tasks_extended.go b/services/cron/tasks_extended.go index 37471119842ec..a2cdc1b4c62ee 100644 --- a/services/cron/tasks_extended.go +++ b/services/cron/tasks_extended.go @@ -224,6 +224,16 @@ func registerRebuildIssueIndexer() { }) } +func registerCleanupRepoLockFiles() { + RegisterTaskFatal("cleanup_repo_lock_files", &BaseConfig{ + Enabled: false, + RunAtStart: false, + Schedule: "@every 24h", + }, func(ctx context.Context, _ *user_model.User, config Config) error { + return repo_service.CleanupRepo(ctx) + }) +} + func initExtendedTasks() { registerDeleteInactiveUsers() registerDeleteRepositoryArchives() @@ -239,4 +249,5 @@ func initExtendedTasks() { registerDeleteOldSystemNotices() registerGCLFS() registerRebuildIssueIndexer() + registerCleanupRepoLockFiles() } diff --git a/services/repository/cleanup.go b/services/repository/cleanup.go new file mode 100644 index 0000000000000..46af56052f553 --- /dev/null +++ b/services/repository/cleanup.go @@ -0,0 +1,38 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package repository + +import ( + "context" + + "code.gitea.io/gitea/models/db" + repo_model "code.gitea.io/gitea/models/repo" + "code.gitea.io/gitea/modules/gitrepo" + "code.gitea.io/gitea/modules/log" + + "xorm.io/builder" +) + +func CleanupRepo(ctx context.Context) error { + log.Trace("Doing: CleanupRepo") + + if err := db.Iterate( + ctx, + builder.Eq{"is_empty": false}, + func(ctx context.Context, repo *repo_model.Repository) error { + select { + case <-ctx.Done(): + return db.ErrCancelledf("before cleanup repo lock files for %s", repo.FullName()) + default: + } + return gitrepo.CleanupRepo(ctx, repo) + }, + ); err != nil { + log.Trace("Error: CleanupRepo: %v", err) + return err + } + + log.Trace("Finished: CleanupRepo") + return nil +} diff --git a/tests/integration/api_admin_test.go b/tests/integration/api_admin_test.go index d28a103e596f8..1cceacefbad98 100644 --- a/tests/integration/api_admin_test.go +++ b/tests/integration/api_admin_test.go @@ -304,11 +304,11 @@ func TestAPICron(t *testing.T) { AddTokenAuth(token) resp := MakeRequest(t, req, http.StatusOK) - assert.Equal(t, "29", resp.Header().Get("X-Total-Count")) + assert.Equal(t, "30", resp.Header().Get("X-Total-Count")) var crons []api.Cron DecodeJSON(t, resp, &crons) - assert.Len(t, crons, 29) + assert.Len(t, crons, 30) }) t.Run("Execute", func(t *testing.T) {