Two unrelated but small fixes, closes #258 and closes #32. **The reference does not name its types (#258)** Since the api reference is rendered from the openapi 3.0 document, a field of type `ReviewStateType` is rendered as the bare string enum the `$ref` points at: starlight-openapi dereferences everything and the schema has no `title`, so the name of the type is nowhere on the page. Searching the current reference for `ReviewStateType` returns nothing, which is what the issue reports (the plugin also has no route for `components/schemas`, so there is no page per type either). `normalizeSchema()` now titles every named schema of an openapi 3.0 document with its own name, which the plugin renders next to the type of a field: - before: `state` · `string` · Allowed values: APPROVED … - after: `state` · `ReviewStateType` · `string` · Allowed values: APPROVED … The go doc comments start with the name of the type, so the description would repeat it ("ChangeFilesOptions — ChangeFilesOptions options for creating …"); a leading `<name> ` is stripped from the description. For the record, how the three generations of documents describe that type: | Versions | Type name | Allowed values | | --- | --- | --- | | 1.22 – 1.25 (swagger, `ReviewStateType` definition without `enum`) | shown | missing | | 1.26 – 1.27 (swagger, enum inlined upstream) | missing | shown | | 1.27, next (openapi 3.0, with this change) | shown | shown | The released swagger documents are frozen, so 1.22 – 1.26 keep what they have; from 1.27 on the reference answers the question the issue asks. **Gmail needs an app password (#32)** The configuration in the guide is still correct after Google turned off password-only access, because it already asks for an app password. What was missing is why there is no alternative: Gitea's mailer authenticates with `CRAM-MD5`, `PLAIN` or `LOGIN` and has no XOAUTH2, so "Sign in with Google" is not an option. Added that, together with the two things people run into — an app password needs 2-step verification and a Workspace administrator can disable app passwords for the domain — and the `587` + `smtp+starttls` variant. Verified by building the api and the next docs: the type names show up on the operation pages, the descriptions are not duplicated, and 1.26 and older are unchanged. `pnpm check` passes. <!-- cloudflare-preview --> Preview: https://pr-541.docs-gitea-com.pages.dev --------- Co-authored-by: bircni <[email protected]> Reviewed-on: https://gitea.com/gitea/docs/pulls/541 Reviewed-by: bircni <[email protected]> Co-authored-by: Lunny Xiao <[email protected]>
Astro + Starlight site
Renders the gitea documentation with Starlight: the docs (6 versions x 3 languages), the runner docs (4 series) and the api reference (7 swagger documents). The enterprise docs stay in their own deployment and are linked from the header.
The content directories are the ones the site has always used — docs/,
versioned_docs/, i18n/, runner-docs/, runner-docs_versioned_docs/ and
static/swagger-*.json (plus static/openapi3-*.json since gitea 1.27) — so
the release scripts and the translation workflow stay unchanged.
Running it
Everything is driven from the repository root, see the README.md there:
make serve-fast # english, the version served at the root, starts in seconds
make serve # whole matrix, api included
make serve-built # build and serve it, the only way to try the search locally
make build
make check
GITEA_DOCS_PRODUCTS, GITEA_DOCS_VERSIONS and GITEA_DOCS_LOCALES (comma
separated) restrict the matrix, which is what make serve-fast uses.
How it is put together
packages/content-loader holds the product matrix and the astro content loader.
src/products.ts is the single source of truth for product x version x language
and drives everything else: content loading, sidebars, the version and language
pickers, the version banner, the search facets and the api schemas.
| Source | Route |
|---|---|
versioned_docs/version-1.27/ |
/ |
docs/ |
/next/ |
versioned_docs/version-1.26/ |
/1.26/ |
i18n/zh-cn/docusaurus-plugin-content-docs/version-1.27/ |
/zh-cn/ |
runner-docs_versioned_docs/version-3/ |
/runner/ |
runner-docs/ |
/runner/develop/ |
static/openapi3-27.json |
/api/ |
static/openapi3-latest.json |
/api/next/ |
static/swagger-26.json |
/api/1.26/ |
The loader reads the markdown in two passes. The first one works out the route of every file, the second normalizes and renders it:
- substitutes the version variables (
@version@,@dockerVersion@, ...) - lifts the leading
# headinginto the starlighttitle - maps
sidebar_positiontosidebar.orderand honours the docusaurusslug, including the translations that moved a page - rewrites the relative
*.mdlinks to urls, resolving them against the file and against the version root, and reports the ones that do not resolve - turns the docusaurus admonitions into starlight asides (
:::warningto:::caution,:::infoto:::note,:::note Titleto:::note[Title]) - recreates the index pages of
_category_.json(/category/installation/,/usage/actions/)
Sidebars are built per (product, version, language) from _category_.json and
from the docusaurus sidebar files (sidebars.js, versioned_sidebars/,
runner-sidebars.js, runner-docs_versioned_sidebars/), and selected in
src/routeData.ts, since starlight only supports a single static sidebar.
Set GITEA_DOCS_STRICT_LINKS=true to fail the build on a broken internal link
instead of warning.
Products and languages
The docs are the site itself, so their language comes first: /zh-cn/1.26/.
The runner and the api are separate products and keep their language inside the
product, /runner/zh-cn/3/, which is reserved: both are published in english
only today. Starlight builds a fallback page in every configured language for
every english page, so /zh-cn/runner/ would exist; the post build integration
removes those directories and cloudflare/_redirects sends them to the
english page.
The version served at the root of a product has no number in its url: docs 1.27
is /, runner 3 is /runner/ and api 1.27 is /api/. /1.27/, /runner/3/
and /api/1.27/ are redirected onto them in cloudflare/_redirects.
Api reference
starlight-openapi generates a page per operation from the documents
update_api_docs.sh maintains: the openapi 3.0 document for the versions that
have one (gitea >= 1.27), the swagger 2.0 one for the older versions. Both
describe the same api and use the same operation ids, so the urls of the
operation pages do not depend on which one is rendered. Three adjustments
happen at build time:
- the swagger 2.0 documents set
basePathto the fullhttps://gitea.com/api/v1url, which redoc accepted but is not valid swagger 2.0. A normalized copy is written to.cache/openapi/instead of touching the sources. The openapi 3.0 documents keep that url inservers, where it is valid, and only get the operation ids and the tag descriptions adjusted. - both documents of a version are linked for download from its overview page,
see
specDownloads()insrc/config/api.tsandsrc/components/ApiOverviewTags.astro. - the plugin slugifies the base path, so
/api/1.26/is generated as/api/126/.src/integrations/postbuild.tsrenames the directories and rewrites the links once the build is done.
Search
Algolia docsearch, enabled when PUBLIC_DOCSEARCH_APP_ID and
PUBLIC_DOCSEARCH_API_KEY are set, pagefind otherwise, so a fork without
credentials still gets search. Every page carries docsearch:product,
docsearch:version and docsearch:language meta tags; the crawler turns them
into facets and src/config/docsearch.ts filters the search on the product,
version and language being read. cloudflare/docsearch-crawler.json holds the
crawler configuration and only indexes the versions people read.
Pagefind builds one index for the whole site, so the same scope has to be applied on its side as well, otherwise a search started in the 1.23 docs answers with the pages of the latest release:
src/components/MarkdownContent.astrowraps the content in thedata-pagefind-filterelements that putproductandversioninto the index. One filter per element, pagefind reads the whole attribute as a singlename:valuepair.- the same scope is written to the
gitea:search-filtersmeta tag bysrc/lib/search.ts. - starlight builds the search ui with a build time configuration and has no
option for per page filters, so its
@pagefind/default-uiimport is redirected to the subclass insrc/lib/pagefind-ui.tsby thegitea-pagefind-filtersplugin inastro.config.mjs. It selects the filters of the page the modal was opened on and adds the "Search all versions" checkbox, which drops them again and labels the results with their version. A starlight upgrade that moves the import fails the build, thegitea-pagefind-filters-checkintegration verifies that the redirect ran.
Deployment
cloudflare/_headers and cloudflare/_redirects are copied next to the build
output by the publish workflow. /enterprise/* is routed to the enterprise
pages project by a Cloudflare worker maintained outside this repository.
Differences between pnpm dev and the deployed site
/api/1.26/is served at/api/126/, the renaming happens after the build./zh-cn/runner/still answers, the fallback pages are removed after the build./1.27/,/runner/3/and/api/1.27/are cloudflare redirects, so they only work on the deployed site.- search is built by pagefind at build time and only answers on the built site,
unless the algolia docsearch credentials are set;
make serve-builtbuilds and serves it locally. - a scoped run only builds part of the matrix, so the products and versions left
out answer with the 404 page;
pnpm devserves everything.