From 5b47f1f0025e10714a138b42c9c0bad1e8f8a105 Mon Sep 17 00:00:00 2001 From: hwipl Date: Thu, 19 Mar 2026 01:21:50 +0100 Subject: [PATCH] feat: add wiki git info to API (#11589) Add information about the git repository for wiki pages to the Repository response in the API: - has_wiki_contents: info whether wiki git repository already exists - wiki_clone_url: the git clone URL of the wiki git repository - wiki_ssh_url: the git SSH URL of the wiki git repository Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/11589 Reviewed-by: Gusted Co-authored-by: hwipl Co-committed-by: hwipl --- modules/structs/repo.go | 63 ++++++++++++++++-------------- services/convert/repository.go | 3 ++ templates/swagger/v1_json.tmpl | 14 +++++++ tests/integration/api_repo_test.go | 58 +++++++++++++++++++++++++++ 4 files changed, 109 insertions(+), 29 deletions(-) diff --git a/modules/structs/repo.go b/modules/structs/repo.go index a8e8249f18..059f19c2bb 100644 --- a/modules/structs/repo.go +++ b/modules/structs/repo.go @@ -80,35 +80,40 @@ type Repository struct { // swagger:strfmt date-time Created time.Time `json:"created_at"` // swagger:strfmt date-time - Updated time.Time `json:"updated_at"` - ArchivedAt time.Time `json:"archived_at"` - Permissions *Permission `json:"permissions,omitempty"` - HasIssues bool `json:"has_issues"` - InternalTracker *InternalTracker `json:"internal_tracker,omitempty"` - ExternalTracker *ExternalTracker `json:"external_tracker,omitempty"` - HasWiki bool `json:"has_wiki"` - ExternalWiki *ExternalWiki `json:"external_wiki,omitempty"` - WikiBranch string `json:"wiki_branch,omitempty"` - GloballyEditableWiki bool `json:"globally_editable_wiki"` - HasPullRequests bool `json:"has_pull_requests"` - HasProjects bool `json:"has_projects"` - HasReleases bool `json:"has_releases"` - HasPackages bool `json:"has_packages"` - HasActions bool `json:"has_actions"` - IgnoreWhitespaceConflicts bool `json:"ignore_whitespace_conflicts"` - AllowMerge bool `json:"allow_merge_commits"` - AllowRebase bool `json:"allow_rebase"` - AllowRebaseMerge bool `json:"allow_rebase_explicit"` - AllowSquash bool `json:"allow_squash_merge"` - AllowFastForwardOnly bool `json:"allow_fast_forward_only_merge"` - AllowRebaseUpdate bool `json:"allow_rebase_update"` - DefaultDeleteBranchAfterMerge bool `json:"default_delete_branch_after_merge"` - DefaultMergeStyle string `json:"default_merge_style"` - DefaultAllowMaintainerEdit bool `json:"default_allow_maintainer_edit"` - DefaultUpdateStyle string `json:"default_update_style"` - AvatarURL string `json:"avatar_url"` - Internal bool `json:"internal"` - MirrorInterval string `json:"mirror_interval"` + Updated time.Time `json:"updated_at"` + ArchivedAt time.Time `json:"archived_at"` + Permissions *Permission `json:"permissions,omitempty"` + HasIssues bool `json:"has_issues"` + InternalTracker *InternalTracker `json:"internal_tracker,omitempty"` + ExternalTracker *ExternalTracker `json:"external_tracker,omitempty"` + // is the wiki enabled + HasWiki bool `json:"has_wiki"` + // have wiki pages ever been created + HasWikiContents bool `json:"has_wiki_contents"` + ExternalWiki *ExternalWiki `json:"external_wiki,omitempty"` + WikiBranch string `json:"wiki_branch,omitempty"` + WikiSSHURL string `json:"wiki_ssh_url"` + WikiCloneURL string `json:"wiki_clone_url"` + GloballyEditableWiki bool `json:"globally_editable_wiki"` + HasPullRequests bool `json:"has_pull_requests"` + HasProjects bool `json:"has_projects"` + HasReleases bool `json:"has_releases"` + HasPackages bool `json:"has_packages"` + HasActions bool `json:"has_actions"` + IgnoreWhitespaceConflicts bool `json:"ignore_whitespace_conflicts"` + AllowMerge bool `json:"allow_merge_commits"` + AllowRebase bool `json:"allow_rebase"` + AllowRebaseMerge bool `json:"allow_rebase_explicit"` + AllowSquash bool `json:"allow_squash_merge"` + AllowFastForwardOnly bool `json:"allow_fast_forward_only_merge"` + AllowRebaseUpdate bool `json:"allow_rebase_update"` + DefaultDeleteBranchAfterMerge bool `json:"default_delete_branch_after_merge"` + DefaultMergeStyle string `json:"default_merge_style"` + DefaultAllowMaintainerEdit bool `json:"default_allow_maintainer_edit"` + DefaultUpdateStyle string `json:"default_update_style"` + AvatarURL string `json:"avatar_url"` + Internal bool `json:"internal"` + MirrorInterval string `json:"mirror_interval"` // ObjectFormatName of the underlying git repository // enum: ["sha1", "sha256"] ObjectFormatName string `json:"object_format_name"` diff --git a/services/convert/repository.go b/services/convert/repository.go index 3823fd3a09..b95e7675a8 100644 --- a/services/convert/repository.go +++ b/services/convert/repository.go @@ -227,7 +227,10 @@ func innerToRepo(ctx stdCtx.Context, repo *repo_model.Repository, permissionInRe ExternalTracker: externalTracker, InternalTracker: internalTracker, HasWiki: hasWiki, + HasWikiContents: repo.HasWiki(), WikiBranch: repo.WikiBranch, + WikiSSHURL: repo.WikiCloneLink().SSH, + WikiCloneURL: repo.WikiCloneLink().HTTPS, GloballyEditableWiki: globallyEditableWiki, HasProjects: hasProjects, HasReleases: hasReleases, diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index efa8b939f5..28a171ad84 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -29016,9 +29016,15 @@ "x-go-name": "HasReleases" }, "has_wiki": { + "description": "is the wiki enabled", "type": "boolean", "x-go-name": "HasWiki" }, + "has_wiki_contents": { + "description": "have wiki pages ever been created", + "type": "boolean", + "x-go-name": "HasWikiContents" + }, "html_url": { "type": "string", "x-go-name": "HTMLURL" @@ -29158,6 +29164,14 @@ "wiki_branch": { "type": "string", "x-go-name": "WikiBranch" + }, + "wiki_clone_url": { + "type": "string", + "x-go-name": "WikiCloneURL" + }, + "wiki_ssh_url": { + "type": "string", + "x-go-name": "WikiSSHURL" } }, "x-go-package": "forgejo.org/modules/structs" diff --git a/tests/integration/api_repo_test.go b/tests/integration/api_repo_test.go index ee2f1fc568..9587383862 100644 --- a/tests/integration/api_repo_test.go +++ b/tests/integration/api_repo_test.go @@ -1049,6 +1049,64 @@ func TestAPIViewRepoObjectFormat(t *testing.T) { assert.Equal(t, "sha1", repo.ObjectFormatName) } +// TestAPIViewRepoWikiGitInfo tests wiki git information +func TestAPIViewRepoWikiGitInfo(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + for _, test := range []struct { + name string + user string + repo string + hasWiki bool + hasContents bool + }{ + { + name: "wiki enabled, wiki contents", + user: "user2", + repo: "repo1", + hasWiki: true, + hasContents: true, + }, + { + name: "wiki enabled, no wiki contents", + user: "user5", + repo: "repo4", + hasWiki: true, + hasContents: false, + }, + { + name: "wiki disabled, no wiki contents", + user: "user12", + repo: "repo10", + hasWiki: false, + hasContents: false, + }, + } { + t.Run(test.name, func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + // get repo + url := fmt.Sprintf("/api/v1/repos/%s/%s", test.user, test.repo) + req := NewRequest(t, "GET", url) + resp := MakeRequest(t, req, http.StatusOK) + var repo api.Repository + DecodeJSON(t, resp, &repo) + + // check repo + sshURL := fmt.Sprintf("ssh://%s@%s:%d/%s/%s.wiki.git", + setting.SSH.User, setting.SSH.Domain, setting.SSH.Port, + test.user, test.repo) + cloneURL := fmt.Sprintf("http://%s:%s/%s/%s.wiki.git", + setting.Domain, setting.HTTPPort, + test.user, test.repo) + assert.Equal(t, test.hasWiki, repo.HasWiki) + assert.Equal(t, test.hasContents, repo.HasWikiContents) + assert.Equal(t, sshURL, repo.WikiSSHURL) + assert.Equal(t, cloneURL, repo.WikiCloneURL) + }) + } +} + func TestAPIRepoCommitPull(t *testing.T) { defer tests.PrepareTestEnv(t)()