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:
User clicks “Login with GitHub” in the CMS.
The CMS redirects to
GET /authon the OAuth proxy.The proxy redirects to GitHub’s authorize page with the app’s client ID.
User approves, GitHub redirects back to
GET /callback?code=....The proxy exchanges that code for an access token server-side.
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