Files
docs/update_runner_docs.sh
Lunny Xiaoandbircni b137a0e0e1 Rebuild the site with Astro and Starlight (#496)
Replace [#307](https://gitea.com/gitea/docs/issues/307)
Closes [#237](https://gitea.com/gitea/docs/issues/237)

## Summary

Rebuilds docs.gitea.com with [Astro](https://astro.build) and [Starlight](https://starlight.astro.build), replacing Docusaurus. Every
published url keeps working and no content file was moved: the site is a new rendering layer over the existing `docs/`, `versioned_docs/`, `i18n/`, `runner-docs/` and `static/swagger-*.json` trees.

For the first time the API reference is part of the site rather than a Redoc
bundle: all seven swagger documents are rendered into real pages, one per
operation, so they are linkable, crawlable and searchable.

## Why

- the Docusaurus build needs an 8 GB heap and about 2 minutes for 2478 pages;
  the Astro build produces 5751 pages, API reference included, in about 100
  seconds
- the API reference was a single client rendered Redoc page per version, absent
  from the site search and from search engines
- sidebars, version lists and language lists were configured in three different
  places and drifted apart

## What is in here

- **`packages/content-loader`** — the product × version × language matrix
  (`products.ts`) and an Astro content loader that reads the existing markdown
  trees directly. The matrix is the single source of truth: content loading,
  sidebars, the version and language pickers, the version banner, the search
  facets and the API schemas are all derived from it.
- **Content compatibility** — frontmatter `slug`, `sidebar_position` and
  `sidebar_label` keep working, `_category_.json` still drives sidebar labels,
  order and the generated category pages, `@version@` style release variables are
  still substituted, `:::note` admonitions become Starlight asides and the 2778
  relative `*.md` links are rewritten to urls while the site is built.
- **API reference** — `starlight-openapi` renders the seven swagger documents.
  Operation urls are hyphenated (`/api/operations/list-admin-workflow-jobs/`),
  the sidebar shows the operations of the version being read with a coloured
  HTTP method badge, and the overview page links to one page per tag instead of
  repeating all 484 operations.
- **Navigation** — a version picker that follows the reader to the same page in
  another version, a language picker that only offers the languages the current
  product is published in, and product links for Docs, API, Runner and
  Enterprise.
- **Theme** — the light and dark palettes of about.gitea.com, mapped onto the
  Starlight variables.
- **Search** — Pagefind by default; Algolia DocSearch takes over when
  `PUBLIC_DOCSEARCH_APP_ID` and `PUBLIC_DOCSEARCH_API_KEY` are set. Every page
  carries `docsearch:product`, `docsearch:version` and `docsearch:language` meta
  tags, so a search stays inside what is being read.
  `cloudflare/docsearch-crawler.json` holds the crawler configuration.

## Bugs fixed along the way

- `GET /user/applications/oauth2` and `GET /user/applications/oauth2/{id}` have
  operation ids that only differ in case, so they collapsed onto the same url
  and one of the two pages was silently dropped. They are now
  `user-get-oauth2-application` and `user-get-oauth2-application-by-id`.
- The API overview and every tag page were all titled "Overview".

## Url compatibility

The routes of both builds were compared page by page during the migration with
`scripts/url-diff.mjs`:

```
identical: 2368, missing: 0, accepted: 110, added: 3292
```

The 110 accepted ones are all redirected in `cloudflare/_redirects`: the
localized copies of the English-only API and Runner docs, the Docusaurus search
page, and `/1.27/`, `/runner/3/`, `/api/1.27/` which are aliases of the versions
served at the product root. The added ones are the API operation and tag pages
plus the routes the Starlight language fallback serves in English when a
translation is missing — Docusaurus answered those with a 404.

## Workflows

- `checks` builds the site and type checks it on every pull request
- `Build and Publish Docs site` publishes `sites/docs/dist` to S3/CloudFront and
  to Cloudflare Pages, and copies `cloudflare/_headers` and
  `cloudflare/_redirects` into the deployment
- `update swagger files` and `update runner reference` are unchanged, they only
  touch content
- `make cut-version PRODUCT=docs VERSION=1.28` replaces
  `docusaurus docs:version`

## Removed

`docusaurus.config.js`, the swizzled theme under `src/`, and the Docusaurus UI
translations (`i18n/*/code.json`, `i18n/*/docusaurus-theme-classic/`). The
strings those carried — the product names, the footer column titles and the
outdated translation notice — were ported to
`sites/docs/src/config/strings.ts`; Starlight ships the rest of its interface in
both Chinese locales. The documentation content itself is untouched.

## Follow ups

- nine relative links are broken in the sources and reported by every build, the
  same ones Docusaurus warned about; `GITEA_DOCS_STRICT_LINKS=true` turns them
  into an error once they are fixed
- `cloudflare/worker.js` has to be deployed for `/enterprise/` to keep resolving
- the Algolia index has to be created and crawled before the search credentials
  are set

## Testing

```shell
make install
make serve-fast   # english, the version served at the root
make serve        # the whole matrix
make build        # 5751 pages
make check        # 0 errors
make serve-built  # build and serve, the only way to try the search locally
```

## Screenshots

<img width="1371" alt="image.png" src="attachments/69acdd77-cc89-4635-a8bd-1163a34afa86">

<img width="1810" alt="image.png" src="attachments/76212416-4825-4b4d-bf59-3e549900c96f">

<img width="1789" alt="image.png" src="attachments/7be84cda-5abe-48bd-8839-7ae1ee7806e7">

---------

Co-authored-by: bircni <[email protected]>
Reviewed-on: https://gitea.com/gitea/docs/pulls/496
Reviewed-by: bircni <[email protected]>
2026-08-12 20:03:29 +00:00

197 lines
6.4 KiB
Bash
Executable File

#!/bin/bash
#
# Regenerates the generated pages of the runner documentation from the runner
# source: the command line reference and the example configuration file.
#
# Usage:
#
# ./update_runner_docs.sh develop docs, from the main branch
# ./update_runner_docs.sh --released every documented release series
# ./update_runner_docs.sh <git ref> <dir> one ref into one directory
#
# --released needs no version list: the documented series are the
# runner-docs_versioned_docs/version-<series>/reference directories, and the tag
# each one is generated from is the newest stable v<series>.x.y tag of
# gitea/runner, looked up through the Gitea API. A new series is documented by
# running `make cut-version PRODUCT=runner VERSION=<series>`, nothing here has
# to be edited.
set -euo pipefail
RUNNER_REMOTE="${RUNNER_REMOTE:-https://gitea.com/gitea/runner.git}"
RUNNER_API="${RUNNER_API:-https://gitea.com/api/v1/repos/gitea/runner}"
VERSIONED_DOCS="runner-docs_versioned_docs"
SRC_DIR=".tmp/upstream-runner"
BIN="$PWD/.tmp/gitea-runner"
TAGS_FILE=".tmp/runner-tags.txt"
mkdir -p .tmp
# prints every tag name of the runner repository, one per line
list_runner_tags() {
local page=1 names
while :; do
names="$(curl --silent --show-error --fail \
"$RUNNER_API/tags?limit=50&page=$page" |
grep -o '"name"[[:space:]]*:[[:space:]]*"[^"]*"' | cut -d '"' -f 4)"
[ -n "$names" ] || return 0
printf '%s\n' "$names"
page=$((page + 1))
done
}
# latest_stable_tag <series>, e.g. "3" -> "v3.0.2", empty if the series has no
# release yet. Pre-release tags (v3.0.0-rc1) are ignored on purpose.
latest_stable_tag() {
local series="$1"
if [ ! -s "$TAGS_FILE" ]; then
list_runner_tags > "$TAGS_FILE"
fi
grep -E "^v${series}\.[0-9]+\.[0-9]+$" "$TAGS_FILE" |
sed 's/^v//' |
sort -t . -k1,1n -k2,2n -k3,3n |
tail -n 1 |
sed 's/^/v/'
}
# checkout_runner <git ref>, builds the runner binary at $BIN
checkout_runner() {
local ref="$1"
if [ -d "$SRC_DIR/.git" ]; then
git -C "$SRC_DIR" remote set-url origin "$RUNNER_REMOTE"
else
rm -rf "$SRC_DIR"
git init --quiet "$SRC_DIR"
git -C "$SRC_DIR" remote add origin "$RUNNER_REMOTE"
fi
git -C "$SRC_DIR" fetch --quiet --depth 1 origin "$ref"
git -C "$SRC_DIR" checkout --quiet --detach FETCH_HEAD
(cd "$SRC_DIR" && go build -o "$BIN" .)
}
# the commands the reference documents, in the order they are presented
COMMANDS=(register daemon exec cache-server generate-config bug-report)
# one short paragraph per command, printed above its help output
describe_command() {
case "$1" in
register)
echo 'Registers the runner against a Gitea instance and writes the registration file. Interactive unless `--no-interactive` is given; the token can also come from `--token-file` or the `GITEA_RUNNER_REGISTRATION_TOKEN` environment variable. See [Registering a runner](../registration.md).'
;;
daemon)
echo 'Runs the runner: it polls the instance for jobs and executes them until it is stopped. `--labels` (default: `GITEA_RUNNER_LABELS`) overrides the labels of an already registered runner, and `--once` exits after a single job.'
;;
exec)
echo 'Runs a workflow from the current repository locally, without a Gitea instance and without the runner configuration file. Useful for debugging a workflow before pushing it. Runner YAML is not loaded, so hooks and cache settings do not apply.'
;;
cache-server)
echo 'Runs only the cache server, so several runners can share one cache. `--dir`, `--host` and `--port` override the matching `cache.*` keys; every other setting, `cache.external_secret` included, has to come from the config file. See [Caching](../cache.md#sharing-a-cache-between-runners).'
;;
generate-config)
echo 'Prints the commented example configuration on stdout, which is the starting point for a config file: `gitea-runner generate-config > config.yaml`.'
;;
bug-report)
echo 'Prints the runner version, Go version, OS/architecture and CPU count, for pasting into an issue.'
;;
esac
}
# generate_reference <git ref> <target directory>
generate_reference() {
local ref="$1" target="$2" cmd
mkdir -p "$target"
checkout_runner "$ref"
{
cat <<'EOF'
---
sidebar_position: 1
description: Every gitea-runner command and flag, generated from the runner sources.
---
# Command line reference
{/* Generated by update_runner_docs.sh from the gitea/runner sources, do not edit. */}
`gitea-runner` is a single binary with one subcommand per task. `--config` / `-c` is
global: every command that reads configuration accepts it, and commands that do not
read any ignore it.
EOF
printf '## gitea-runner\n\n```text\n'
"$BIN" --help
printf '```\n'
for cmd in "${COMMANDS[@]}"; do
printf '\n## %s\n\n' "$cmd"
describe_command "$cmd"
printf '\n```text\n'
"$BIN" "$cmd" --help
printf '```\n'
done
} > "$target/cli.md"
{
cat <<'EOF'
---
sidebar_position: 2
description: The commented example configuration of the runner, generated from the runner sources.
---
# Example configuration
{/* Generated by update_runner_docs.sh from the gitea/runner sources, do not edit. */}
This is the output of `gitea-runner generate-config`. It is safe to use unmodified,
and it is the authoritative list of every option the runner understands. See
[Configuration](../configuration.md) for what the options mean and how the file is
loaded.
```yaml
EOF
"$BIN" generate-config
printf '```\n'
} > "$target/config-example.md"
echo "wrote $target/cli.md and $target/config-example.md from $RUNNER_REMOTE@$ref"
}
# regenerates every documented release series from its newest stable tag
generate_released() {
local dir series tag found=0
for dir in "$VERSIONED_DOCS"/version-*/reference; do
[ -d "$dir" ] || continue
found=1
series="${dir#"$VERSIONED_DOCS"/version-}"
series="${series%/reference}"
tag="$(latest_stable_tag "$series")"
if [ -z "$tag" ]; then
echo "no stable v$series tag in $RUNNER_API, skipping $dir" >&2
continue
fi
generate_reference "$tag" "$dir"
done
if [ "$found" -eq 0 ]; then
echo "no $VERSIONED_DOCS/version-*/reference directory to regenerate" >&2
exit 1
fi
}
case "${1:-}" in
--released)
generate_released
;;
-h | --help)
sed -n '3,17s/^#\{1,2\} \{0,1\}//p' "$0"
;;
*)
generate_reference "${1:-main}" "${2:-runner-docs/reference}"
;;
esac