From 4e1b8db1fc08a667328c145c4f92ad8b26ddedd5 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Mon, 1 Sep 2025 11:12:58 -0700 Subject: [PATCH] Move HasWiki to repository service package (#33912) Move HasWiki out of the models package to avoid referencing the absolute wiki path directly. --- models/repo/repo.go | 6 +++++- models/repo/wiki.go | 10 ---------- models/repo/wiki_test.go | 8 -------- modules/gitrepo/gitrepo.go | 3 ++- routers/web/repo/wiki.go | 11 ++++++----- routers/web/repo/wiki_test.go | 3 ++- services/migrations/gitea_uploader_test.go | 2 +- services/mirror/mirror_pull.go | 8 ++++---- services/mirror/mirror_push.go | 7 ++++--- services/repository/delete.go | 10 +++++++--- services/repository/hooks.go | 7 ++++--- services/repository/migrate.go | 6 ++++-- services/repository/repository.go | 9 +++++++++ services/repository/repository_test.go | 9 +++++++++ services/repository/transfer.go | 11 +++-------- services/wiki/wiki.go | 17 ++++++++++++++--- services/wiki/wiki_test.go | 3 ++- 17 files changed, 76 insertions(+), 54 deletions(-) diff --git a/models/repo/repo.go b/models/repo/repo.go index 73c7e304d2..f9b412cfe8 100644 --- a/models/repo/repo.go +++ b/models/repo/repo.go @@ -229,6 +229,10 @@ func RelativePath(ownerName, repoName string) string { return strings.ToLower(ownerName) + "/" + strings.ToLower(repoName) + ".git" } +func RelativeWikiPath(ownerName, repoName string) string { + return strings.ToLower(ownerName) + "/" + strings.ToLower(repoName) + ".wiki.git" +} + // RelativePath should be an unix style path like username/reponame.git func (repo *Repository) RelativePath() string { return RelativePath(repo.OwnerName, repo.Name) @@ -242,7 +246,7 @@ func (sr StorageRepo) RelativePath() string { } func (repo *Repository) WikiStorageRepo() StorageRepo { - return StorageRepo(strings.ToLower(repo.OwnerName) + "/" + strings.ToLower(repo.Name) + ".wiki.git") + return StorageRepo(RelativeWikiPath(repo.OwnerName, repo.Name)) } // SanitizedOriginalURL returns a sanitized OriginalURL diff --git a/models/repo/wiki.go b/models/repo/wiki.go index 832e15ae0d..9f41445bf8 100644 --- a/models/repo/wiki.go +++ b/models/repo/wiki.go @@ -11,7 +11,6 @@ import ( "strings" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/util" ) @@ -86,12 +85,3 @@ func WikiPath(userName, repoName string) string { func (repo *Repository) WikiPath() string { return WikiPath(repo.OwnerName, repo.Name) } - -// HasWiki returns true if repository has wiki. -func (repo *Repository) HasWiki() bool { - isDir, err := util.IsDir(repo.WikiPath()) - if err != nil { - log.Error("Unable to check if %s is a directory: %v", repo.WikiPath(), err) - } - return isDir -} diff --git a/models/repo/wiki_test.go b/models/repo/wiki_test.go index 103420a392..41e53d93d9 100644 --- a/models/repo/wiki_test.go +++ b/models/repo/wiki_test.go @@ -35,11 +35,3 @@ func TestRepository_WikiPath(t *testing.T) { expected := filepath.Join(setting.RepoRootPath, "user2/repo1.wiki.git") assert.Equal(t, expected, repo.WikiPath()) } - -func TestRepository_HasWiki(t *testing.T) { - unittest.PrepareTestEnv(t) - repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) - assert.True(t, repo1.HasWiki()) - repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}) - assert.False(t, repo2.HasWiki()) -} diff --git a/modules/gitrepo/gitrepo.go b/modules/gitrepo/gitrepo.go index 5da65e2452..d350ca3e66 100644 --- a/modules/gitrepo/gitrepo.go +++ b/modules/gitrepo/gitrepo.go @@ -69,7 +69,8 @@ func IsRepositoryExist(ctx context.Context, repo Repository) (bool, error) { return util.IsExist(repoPath(repo)) } -// DeleteRepository deletes the repository directory from the disk +// DeleteRepository deletes the repository directory from the disk, it will return +// nil if the repository does not exist. func DeleteRepository(ctx context.Context, repo Repository) error { return util.RemoveAll(repoPath(repo)) } diff --git a/routers/web/repo/wiki.go b/routers/web/repo/wiki.go index a35b7b86e1..289db11a4f 100644 --- a/routers/web/repo/wiki.go +++ b/routers/web/repo/wiki.go @@ -33,6 +33,7 @@ import ( "code.gitea.io/gitea/services/forms" git_service "code.gitea.io/gitea/services/git" notify_service "code.gitea.io/gitea/services/notify" + repo_service "code.gitea.io/gitea/services/repository" wiki_service "code.gitea.io/gitea/services/wiki" ) @@ -474,7 +475,7 @@ func Wiki(ctx *context.Context) { return } - if !ctx.Repo.Repository.HasWiki() { + if !repo_service.HasWiki(ctx, ctx.Repo.Repository) { ctx.Data["Title"] = ctx.Tr("repo.wiki") ctx.HTML(http.StatusOK, tplWikiStart) return @@ -510,7 +511,7 @@ func Wiki(ctx *context.Context) { func WikiRevision(ctx *context.Context) { ctx.Data["CanWriteWiki"] = ctx.Repo.CanWrite(unit.TypeWiki) && !ctx.Repo.Repository.IsArchived - if !ctx.Repo.Repository.HasWiki() { + if !repo_service.HasWiki(ctx, ctx.Repo.Repository) { ctx.Data["Title"] = ctx.Tr("repo.wiki") ctx.HTML(http.StatusOK, tplWikiStart) return @@ -540,7 +541,7 @@ func WikiRevision(ctx *context.Context) { // WikiPages render wiki pages list page func WikiPages(ctx *context.Context) { - if !ctx.Repo.Repository.HasWiki() { + if !repo_service.HasWiki(ctx, ctx.Repo.Repository) { ctx.Redirect(ctx.Repo.RepoLink + "/wiki") return } @@ -648,7 +649,7 @@ func WikiRaw(ctx *context.Context) { func NewWiki(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("repo.wiki.new_page") - if !ctx.Repo.Repository.HasWiki() { + if !repo_service.HasWiki(ctx, ctx.Repo.Repository) { ctx.Data["title"] = "Home" } if ctx.FormString("title") != "" { @@ -701,7 +702,7 @@ func NewWikiPost(ctx *context.Context) { func EditWiki(ctx *context.Context) { ctx.Data["PageIsWikiEdit"] = true - if !ctx.Repo.Repository.HasWiki() { + if !repo_service.HasWiki(ctx, ctx.Repo.Repository) { ctx.Redirect(ctx.Repo.RepoLink + "/wiki") return } diff --git a/routers/web/repo/wiki_test.go b/routers/web/repo/wiki_test.go index 0aafe462de..409d7c9a05 100644 --- a/routers/web/repo/wiki_test.go +++ b/routers/web/repo/wiki_test.go @@ -16,6 +16,7 @@ import ( "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/services/contexttest" "code.gitea.io/gitea/services/forms" + repo_service "code.gitea.io/gitea/services/repository" wiki_service "code.gitea.io/gitea/services/wiki" "github.com/stretchr/testify/assert" @@ -240,7 +241,7 @@ func TestDefaultWikiBranch(t *testing.T) { // repo with no wiki repoWithNoWiki := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}) - assert.False(t, repoWithNoWiki.HasWiki()) + assert.False(t, repo_service.HasWiki(t.Context(), repoWithNoWiki)) assert.NoError(t, wiki_service.ChangeDefaultWikiBranch(t.Context(), repoWithNoWiki, "main")) // repo with wiki diff --git a/services/migrations/gitea_uploader_test.go b/services/migrations/gitea_uploader_test.go index 5e6fe6cc05..97d829ce2b 100644 --- a/services/migrations/gitea_uploader_test.go +++ b/services/migrations/gitea_uploader_test.go @@ -63,7 +63,7 @@ func TestGiteaUploadRepo(t *testing.T) { assert.NoError(t, err) repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerID: user.ID, Name: repoName}) - assert.True(t, repo.HasWiki()) + assert.True(t, repo_service.HasWiki(ctx, repo)) assert.Equal(t, repo_model.RepositoryReady, repo.Status) milestones, err := db.Find[issues_model.Milestone](t.Context(), issues_model.FindMilestoneOptions{ diff --git a/services/mirror/mirror_pull.go b/services/mirror/mirror_pull.go index cb90af5894..82be0052c3 100644 --- a/services/mirror/mirror_pull.go +++ b/services/mirror/mirror_pull.go @@ -52,7 +52,7 @@ func UpdateAddress(ctx context.Context, m *repo_model.Mirror, addr string) error return err } - if m.Repo.HasWiki() { + if repo_service.HasWiki(ctx, m.Repo) { wikiPath := m.Repo.WikiPath() wikiRemotePath := repo_module.WikiRemoteURL(ctx, addr) // Remove old remote of wiki @@ -347,7 +347,7 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo endpoint := lfs.DetermineEndpoint(remoteURL.String(), m.LFSEndpoint) lfsClient := lfs.NewClient(endpoint, nil) if err = repo_module.StoreMissingLfsObjectsInRepository(ctx, m.Repo, gitRepo, lfsClient); err != nil { - log.Error("SyncMirrors [repo: %-v]: failed to synchronize LFS objects for repository: %v", m.Repo, err) + log.Error("SyncMirrors [repo: %-v]: failed to synchronize LFS objects for repository: %v", m.Repo.FullName(), err) } } @@ -364,10 +364,10 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo log.Trace("SyncMirrors [repo: %-v]: updating size of repository", m.Repo) if err := repo_module.UpdateRepoSize(ctx, m.Repo); err != nil { - log.Error("SyncMirrors [repo: %-v]: failed to update size for mirror repository: %v", m.Repo, err) + log.Error("SyncMirrors [repo: %-v]: failed to update size for mirror repository: %v", m.Repo.FullName(), err) } - if m.Repo.HasWiki() { + if repo_service.HasWiki(ctx, m.Repo) { log.Trace("SyncMirrors [repo: %-v Wiki]: running git remote update...", m.Repo) stderrBuilder.Reset() stdoutBuilder.Reset() diff --git a/services/mirror/mirror_push.go b/services/mirror/mirror_push.go index 9b57427d98..a0fffde797 100644 --- a/services/mirror/mirror_push.go +++ b/services/mirror/mirror_push.go @@ -23,6 +23,7 @@ import ( "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" + repo_service "code.gitea.io/gitea/services/repository" ) var stripExitStatus = regexp.MustCompile(`exit status \d+ - `) @@ -47,7 +48,7 @@ func AddPushMirrorRemote(ctx context.Context, m *repo_model.PushMirror, addr str return err } - if m.Repo.HasWiki() { + if repo_service.HasWiki(ctx, m.Repo) { wikiRemoteURL := repository.WikiRemoteURL(ctx, addr) if len(wikiRemoteURL) > 0 { if err := addRemoteAndConfig(wikiRemoteURL, m.Repo.WikiPath()); err != nil { @@ -68,7 +69,7 @@ func RemovePushMirrorRemote(ctx context.Context, m *repo_model.PushMirror) error return err } - if m.Repo.HasWiki() { + if repo_service.HasWiki(ctx, m.Repo) { if _, _, err := cmd.RunStdString(ctx, &git.RunOpts{Dir: m.Repo.WikiPath()}); err != nil { // The wiki remote may not exist log.Warn("Wiki Remote[%d] could not be removed: %v", m.ID, err) @@ -183,7 +184,7 @@ func runPushSync(ctx context.Context, m *repo_model.PushMirror) error { return err } - if m.Repo.HasWiki() { + if repo_service.HasWiki(ctx, m.Repo) { _, err := git.GetRemoteAddress(ctx, m.Repo.WikiPath(), m.RemoteName) if err == nil { err := performPush(m.Repo, true) diff --git a/services/repository/delete.go b/services/repository/delete.go index 65dfc2a3b7..040280c8a8 100644 --- a/services/repository/delete.go +++ b/services/repository/delete.go @@ -315,9 +315,13 @@ func DeleteRepositoryDirectly(ctx context.Context, repoID int64, ignoreOrgTeams } } - // Remove wiki files - if repo.HasWiki() { - system_model.RemoveAllWithNotice(ctx, "Delete repository wiki", repo.WikiPath()) + // Remove wiki files if it exists. + if err := gitrepo.DeleteRepository(ctx, repo.WikiStorageRepo()); err != nil { + desc := fmt.Sprintf("Delete wiki repository files [%s]: %v", repo.FullName(), err) + // Note we use the db.DefaultContext here rather than passing in a context as the context may be cancelled + if err = system_model.CreateNotice(graceful.GetManager().ShutdownContext(), system_model.NoticeRepository, desc); err != nil { + log.Error("CreateRepositoryNotice: %v", err) + } } // Remove archives diff --git a/services/repository/hooks.go b/services/repository/hooks.go index c13b272550..c5bd3b8a7e 100644 --- a/services/repository/hooks.go +++ b/services/repository/hooks.go @@ -32,11 +32,12 @@ func SyncRepositoryHooks(ctx context.Context) error { } if err := gitrepo.CreateDelegateHooks(ctx, repo); err != nil { - return fmt.Errorf("SyncRepositoryHook: %w", err) + return fmt.Errorf("CreateDelegateHooks: %w", err) } - if repo.HasWiki() { + + if HasWiki(ctx, repo) { if err := gitrepo.CreateDelegateHooks(ctx, repo.WikiStorageRepo()); err != nil { - return fmt.Errorf("SyncRepositoryHook: %w", err) + return fmt.Errorf("CreateDelegateHooks: %w", err) } } return nil diff --git a/services/repository/migrate.go b/services/repository/migrate.go index 0a3dc45339..2a5fe74846 100644 --- a/services/repository/migrate.go +++ b/services/repository/migrate.go @@ -280,7 +280,9 @@ func CleanUpMigrateInfo(ctx context.Context, repo *repo_model.Repository) (*repo if err := gitrepo.CreateDelegateHooks(ctx, repo); err != nil { return repo, fmt.Errorf("createDelegateHooks: %w", err) } - if repo.HasWiki() { + + hasWiki := HasWiki(ctx, repo) + if hasWiki { if err := gitrepo.CreateDelegateHooks(ctx, repo.WikiStorageRepo()); err != nil { return repo, fmt.Errorf("createDelegateHooks.(wiki): %w", err) } @@ -291,7 +293,7 @@ func CleanUpMigrateInfo(ctx context.Context, repo *repo_model.Repository) (*repo return repo, fmt.Errorf("CleanUpMigrateInfo: %w", err) } - if repo.HasWiki() { + if hasWiki { if err := cleanUpMigrateGitConfig(ctx, repo.WikiPath()); err != nil { return repo, fmt.Errorf("cleanUpMigrateGitConfig (wiki): %w", err) } diff --git a/services/repository/repository.go b/services/repository/repository.go index df5462bf5a..acc5ce56cf 100644 --- a/services/repository/repository.go +++ b/services/repository/repository.go @@ -20,6 +20,7 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/graceful" issue_indexer "code.gitea.io/gitea/modules/indexer/issues" "code.gitea.io/gitea/modules/log" @@ -336,3 +337,11 @@ func updateRepository(ctx context.Context, repo *repo_model.Repository, visibili return nil } + +func HasWiki(ctx context.Context, repo *repo_model.Repository) bool { + hasWiki, err := gitrepo.IsRepositoryExist(ctx, repo.WikiStorageRepo()) + if err != nil { + log.Error("gitrepo.IsRepositoryExist: %v", err) + } + return hasWiki && err == nil +} diff --git a/services/repository/repository_test.go b/services/repository/repository_test.go index ba02e7601c..5673a4a161 100644 --- a/services/repository/repository_test.go +++ b/services/repository/repository_test.go @@ -61,3 +61,12 @@ func TestUpdateRepositoryVisibilityChanged(t *testing.T) { assert.NoError(t, err) assert.True(t, act.IsPrivate) } + +func TestRepository_HasWiki(t *testing.T) { + unittest.PrepareTestEnv(t) + repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) + assert.True(t, HasWiki(t.Context(), repo1)) + + repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}) + assert.False(t, HasWiki(t.Context(), repo2)) +} diff --git a/services/repository/transfer.go b/services/repository/transfer.go index 5ad63cca67..c30a540137 100644 --- a/services/repository/transfer.go +++ b/services/repository/transfer.go @@ -358,14 +358,9 @@ func changeRepositoryName(ctx context.Context, repo *repo_model.Repository, newR return fmt.Errorf("rename repository directory: %w", err) } - wikiPath := repo.WikiPath() - isExist, err := util.IsExist(wikiPath) - if err != nil { - log.Error("Unable to check if %s exists. Error: %v", wikiPath, err) - return err - } - if isExist { - if err = util.Rename(wikiPath, repo_model.WikiPath(repo.Owner.Name, newRepoName)); err != nil { + if HasWiki(ctx, repo) { + if err = gitrepo.RenameRepository(ctx, repo.WikiStorageRepo(), repo_model.StorageRepo( + repo_model.RelativeWikiPath(repo.OwnerName, newRepoName))); err != nil { return fmt.Errorf("rename repository wiki: %w", err) } } diff --git a/services/wiki/wiki.go b/services/wiki/wiki.go index 2edfa90acf..3d212ba973 100644 --- a/services/wiki/wiki.go +++ b/services/wiki/wiki.go @@ -19,6 +19,7 @@ import ( "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/globallock" + "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/log" repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/util" @@ -35,7 +36,10 @@ func getWikiWorkingLockKey(repoID int64) string { // InitWiki initializes a wiki for repository, // it does nothing when repository already has wiki. func InitWiki(ctx context.Context, repo *repo_model.Repository) error { - if repo.HasWiki() { + // don't use HasWiki because the error should not be ignored. + if exist, err := gitrepo.IsRepositoryExist(ctx, repo.WikiStorageRepo()); err != nil { + return err + } else if exist { return nil } @@ -355,7 +359,14 @@ func DeleteWiki(ctx context.Context, repo *repo_model.Repository) error { return err } - system_model.RemoveAllWithNotice(ctx, "Delete repository wiki", repo.WikiPath()) + if err := gitrepo.DeleteRepository(ctx, repo.WikiStorageRepo()); err != nil { + desc := fmt.Sprintf("Delete wiki repository files [%s]: %v", repo.FullName(), err) + // Note we use the db.DefaultContext here rather than passing in a context as the context may be cancelled + if err = system_model.CreateNotice(graceful.GetManager().ShutdownContext(), system_model.NoticeRepository, desc); err != nil { + log.Error("CreateRepositoryNotice: %v", err) + } + } + return nil } @@ -369,7 +380,7 @@ func ChangeDefaultWikiBranch(ctx context.Context, repo *repo_model.Repository, n return fmt.Errorf("unable to update database: %w", err) } - if !repo.HasWiki() { + if !repo_service.HasWiki(ctx, repo) { return nil } diff --git a/services/wiki/wiki_test.go b/services/wiki/wiki_test.go index a54f2c96d5..e571e093b6 100644 --- a/services/wiki/wiki_test.go +++ b/services/wiki/wiki_test.go @@ -13,6 +13,7 @@ import ( user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/gitrepo" + repo_service "code.gitea.io/gitea/services/repository" _ "code.gitea.io/gitea/models/actions" @@ -149,7 +150,7 @@ func TestRepository_InitWiki(t *testing.T) { // repo2 does not already have a wiki repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}) assert.NoError(t, InitWiki(t.Context(), repo2)) - assert.True(t, repo2.HasWiki()) + assert.True(t, repo_service.HasWiki(t.Context(), repo2)) } func TestRepository_AddWikiPage(t *testing.T) {