Wiki Development

Local setup

You need uv and Python 3.14+.

make install

That’s the whole setup. No virtualenv juggling, no pip – uv does it all.

Previewing the wiki

The wiki has ~3,500 pages total, so building everything takes a while. You almost certainly want to scope your build to whatever section you’re working on:

## Just the PSF wiki (~400 pages, under a minute)
make docs-serve-fast WIKI=psf

## A single subsection (~100 pages, a few seconds)
make docs-serve-fast WIKI=psf SECTION=Packaging

## The full Python wiki (~3,400 pages)
make docs-serve-fast WIKI=python

These start a live-reload server. Save a file, the browser refreshes.

For a complete build of all sections (what CI runs):

make docs

How the CMS authentication works

Decap CMS needs an OAuth handshake with GitHub to get a token for the user. GitHub’s OAuth flow requires a server-side component (client secrets can’t live in browser JS), so there’s a small Litestar app in the oauth/ directory that handles this.

The flow goes like this:

  1. User clicks “Login with GitHub” in the CMS.

  2. The CMS redirects to GET /auth on the OAuth proxy.

  3. The proxy redirects to GitHub’s authorize page with the app’s client ID.

  4. User approves, GitHub redirects back to GET /callback?code=....

  5. The proxy exchanges that code for an access token server-side.

  6. The proxy returns a small HTML page that postMessages the token back to the CMS window.

That’s the entire app. Three routes: /auth, /callback, and /_health/ for load balancers. The source is oauth/app.py – about 70 lines. For the full reference, see the OAuth API docs.

OAuth proxy stack

The proxy is its own Python project with its own pyproject.toml and uv.lock, separate from the main wiki’s Sphinx dependencies. It uses:

  • Litestar for the web framework

  • httpx for the async HTTP call to GitHub’s token endpoint

  • uvicorn as the ASGI server

In production, it runs on Cabotage (the PSF’s deployment platform) behind an nginx ingress, binding to a Unix socket at /var/run/cabotage/cabotage.sock. The Dockerfile and Procfile in oauth/ handle that. It’s reachable at api.wiki.python.org.

The OpenAPI docs for the proxy are auto-generated by Litestar and served at / via the Scalar UI plugin – hit http://localhost:8000/ when running locally and you’ll see them.

Running the OAuth proxy locally

## Set your GitHub OAuth app credentials
export GITHUB_CLIENT_ID=...
export GITHUB_CLIENT_SECRET=...

## Or put them in oauth/.env or .env at the repo root

make oauth-serve

This starts the proxy at http://localhost:8000 with auto-reload. Run the test suite with:

make oauth-test

CI/CD

Three GitHub Actions workflows keep things running:

docs.yml – Builds the full Sphinx site on every push to main and deploys to GitHub Pages. Also runs on PRs to catch build errors before merge. Uses the Sphinx doctree cache so incremental builds don’t start from scratch.

preview.yml – Generates a deploy preview for every pull request. It detects which wiki section(s) changed and scopes the build accordingly – if you only touched files in psf/, it only builds the PSF wiki. The preview URL gets posted as a comment on the PR.

linkcheck.yml – Runs weekly (Monday 6am UTC) to scan for dead external links across all pages. Results get uploaded as artifacts.

Project layout

python/                 Python wiki content (3,274 pages)
psf/                    PSF wiki content (378 pages)
jython/                 Jython wiki content (433 pages)
oauth/                  GitHub OAuth proxy for Decap CMS
  app.py                The Litestar application (3 routes)
  Dockerfile            Production container image
  Procfile              Cabotage process definition
  pyproject.toml        Proxy-specific dependencies
  tests/                Proxy test suite
  k8s/                  Kubernetes manifests (ingress)
_extra/admin/           Decap CMS frontend (config.yml + loader HTML)
_static/                CSS and static assets
_templates/             Sphinx HTML template overrides
_redirects.json         All 5,400+ redirect mappings
_redirects_html/        Generated static redirect pages
scripts/                Conversion and maintenance tooling
  convert.py            MoinMoin HTML → MyST Markdown converter
  gen_old_wiki_redirects.py   Builds the old-URL redirect map
  gen_redirect_pages.py       Generates static HTML redirect files
  strip_attrs.py        Strips pandoc attribute cruft from .md files
  fix_moin_links.py     Fixes remaining MoinMoin-style links
  reorganize.py         Moves pages into subdirectories
  sync.sh               Pulls raw HTML from the wiki server
conf.py                 Sphinx configuration
pyproject.toml          Wiki build dependencies (uv-managed)
Makefile                All the build/serve/lint commands

Make targets

Run make help to see everything, but here’s the short version:

make install            Install all dependencies
make sync               Sync raw HTML from wiki server
make convert            Re-run the HTML → Markdown conversion
make docs               Build the full Sphinx site
make docs-serve         Serve with live reload (all sections)
make docs-serve-fast    Scoped build (WIKI=python|psf|jython [SECTION=subdir])
make docs-clean         Remove Sphinx build output
make lint               Run pre-commit hooks
make redirects          Regenerate redirect mapping and static HTML files
make oauth-serve        Run OAuth proxy locally (needs GITHUB_CLIENT_ID/SECRET)
make oauth-test         Run OAuth proxy tests
make clean              Remove all build artifacts