Closes #146. The published site runs pagefind, not docsearch: `PUBLIC_DOCSEARCH_APP_ID` and `PUBLIC_DOCSEARCH_API_KEY` are not set, so `astro.config.mjs` falls back to `pagefind: true`, and a page of docs.gitea.com ships starlight's `#starlight__search` with the `/pagefind/` bundle. The version scoping we have is only implemented for docsearch (`docsearch:product` / `docsearch:version` meta tags plus `facetFilters` in `src/config/docsearch.ts`), so nothing consumes it today. Pagefind builds one index for the whole site, partitioned only by the html language, and nothing tags the pages with a version, so a search started in the 1.23 docs answers with every version of the same page and the latest one usually wins. That is what the issue reports; the original cause (the docusaurus search plugin) is gone, this one replaced it. **What this does** - `src/components/MarkdownContent.astro` wraps the content in two `data-pagefind-filter` elements, `product` and `version`, so the filters land in the index. One filter per element on purpose: pagefind reads the whole attribute as a single `name:value` pair, a comma separated value ends up as one filter named `product` with the value `docs,version:1.26`. - The same scope is written to a `gitea:search-filters` meta tag next to the docsearch ones (`src/lib/search.ts`). - Starlight builds the search ui from a build time configuration and has no option for per page filters, so its `@pagefind/default-ui` import is redirected to a subclass in `src/lib/pagefind-ui.ts` by the `gitea-pagefind-filters` plugin in `astro.config.mjs`, the same approach the `gitea-openapi-overview` plugin already uses. The subclass selects the filters of the page the modal was opened on. - A "Search all versions" checkbox below the search input drops the filters again and labels each result with the version (and the product for the api and the runner) it comes from, so the duplicates are distinguishable. Translated into 简体中文 and 繁體中文. - Pagefind's own filter panel is hidden, it is redundant next to the checkbox and starlight does not style it. The checkbox gets its native rendering back, pagefind resets everything it renders with `all: unset`. - A `gitea-pagefind-filters-check` integration fails the build if the redirect did not run, so a starlight upgrade that moves the import cannot silently bring back an unscoped search. `@pagefind/default-ui` is added to `sites/docs/package.json`; it was only reachable as a transitive dependency of starlight, and the version is the one already in the lockfile. **Verification** Built the whole site and drove the search with a headless browser against `astro preview` (the search does not run in `pnpm dev`): | Page | Query | Results | | --- | --- | --- | | `/1.23/usage/actions/comparison/` | email | only `/1.23/...` | | `/usage/actions/overview/` | email | only the version served at the root | | `/runner/registration/` | label | only `/runner/...` | | `/api/operations/tags/issue/` | label | only `/api/...` | | `/zh-cn/1.26/usage/actions/overview/` | email | only `/zh-cn/1.26/...` | | `/1.23/...` with the checkbox ticked | email | every version, results labelled `... · API 1.25`, `... · next` | `pnpm check` passes. The docsearch path is unchanged: when the credentials are set the plugin takes over, the redirect does not run and the check integration skips. <!-- cloudflare-preview --> Preview: https://pr-512.docs-gitea-com.pages.dev Reviewed-on: https://gitea.com/gitea/docs/pulls/512
Gitea Docs 
The sources of docs.gitea.com, built with Astro and Starlight.
The site covers three products, all served from this repository:
| Product | Content | Versions | Languages |
|---|---|---|---|
| Docs | docs/, versioned_docs/, i18n/ |
next, 1.27 … 1.22 | English, 简体中文, 繁體中文 |
| API | static/swagger-*.json |
next, 1.27 … 1.22 | English |
| Runner | runner-docs/, runner-docs_versioned_docs/ |
develop, 3 … 0 | English |
The enterprise documentation is built and deployed from its own repository and
reached through /enterprise/, which a Cloudflare worker maintained elsewhere
routes to that deployment.
Development
make install # pnpm install
make serve-fast # english, the version served at the root, starts in seconds
make serve # the whole matrix, every version and language
make build # production build into sites/docs/dist
make check # type checks the site and its components
Search is built by Pagefind while the site is built, so it only answers on the
built site: use make serve-built to try it. With the Algolia credentials
(PUBLIC_DOCSEARCH_APP_ID, PUBLIC_DOCSEARCH_API_KEY) set, DocSearch is used
instead and works in make serve too.
GITEA_DOCS_PRODUCTS, GITEA_DOCS_VERSIONS and GITEA_DOCS_LOCALES (comma
separated) restrict what is built, which is what make serve-fast uses.
GITEA_DOCS_STRICT_LINKS=true turns the warnings about unresolved relative
markdown links into a build error.
sites/docs/README.md documents how the sources are mapped onto the site.
Writing
Pages are plain markdown with the same frontmatter and admonitions as before:
frontmatter slug, sidebar_position and sidebar_label keep working, and so
do the :::note style admonitions and the @version@ style release variables.
Relative *.md links are rewritten to urls while the site is built.
The order of the top level sidebar groups comes from sidebars.js (and
versioned_sidebars/ for released versions), the label and order of every other
group from the _category_.json of its directory.
Cutting a version
make cut-version PRODUCT=docs VERSION=1.28
make cut-version PRODUCT=runner VERSION=4
This freezes the current tree, its translations and its sidebar. The label of
the version, its release variables (@version@, @dockerVersion@, ...) and
which version is served at the root live in
packages/content-loader/src/products.ts and are edited by hand afterwards.
That file is the single source of truth for the product, version and language
matrix.
API docs
The swagger definitions rendered under /api/ live in
static/swagger-latest.json (gitea main) and static/swagger-<minor>.json
(released versions).
make update-api-docs # refresh latest + every released version
make update-api-docs-latest # refresh only static/swagger-latest.json
static/swagger-latest.json is refreshed automatically: the update swagger files workflow runs every 12 hours and opens a pull request whenever gitea main
changed. Released versions are updated by hand when a new gitea version is
documented.
Runner docs
| Version | Content | URL |
|---|---|---|
| develop | runner-docs/ |
/runner/develop/ |
| current series | runner-docs_versioned_docs/version-3/ |
/runner/ |
| older series | runner-docs_versioned_docs/version-2/ |
/runner/2/ |
A version directory covers a whole release series (version-3 documents every
3.x release), so a patch release only needs a content update, not a new
folder. Use floating image tags (gitea/runner:3) and links to the runner's
main branch in those pages to keep them valid across patch releases.
Its sidebar is written by hand: runner-sidebars.js for develop, and
runner-docs_versioned_sidebars/version-<version>-sidebars.json per documented
version.
The pages under reference/ are generated from the runner sources — the command
line reference from --help, the example configuration from generate-config:
make update-runner-docs # develop docs, from the main branch of gitea/runner
make update-runner-docs-released # every series, from its newest stable tag
./update_runner_docs.sh v3.0.2 runner-docs_versioned_docs/version-3/reference
These are refreshed automatically as well: the update runner reference
workflow runs weekly and opens a pull request whenever the runner's CLI or
example configuration changed. Generating them needs Go, since the script builds
the runner binary.
Deployment
main is built and published by the Build and Publish Docs site workflow, to
S3/CloudFront and to Cloudflare Pages. cloudflare/_headers sets the cache
policy and cloudflare/_redirects keeps the urls the site used to serve; both
are copied next to the build output by that workflow.
cloudflare/docsearch-crawler.json is the Algolia crawler configuration.
/enterprise/ is served by another deployment and routed to it by a Cloudflare
worker that lives outside this repository.