Build the docs

Build and preview the Leoflow documentation site locally — Hugo + Docsy.

The documentation site is built with Hugo (extended) and the Docsy theme, pulled in as a Hugo Module. Everything lives under website/.

Prerequisites

  • Hugo extended, v0.110.0 or newer (hugo version must show +extended). CI pins 0.165.0. Docsy needs the extended build — the plain build cannot compile the theme’s SCSS.
  • Go 1.26+ — used two ways: Hugo fetches Docsy (and its Bootstrap/Font Awesome deps) as Hugo Modules, and the CLI/Go reference generators run go run against the root module.
  • Node.js 20+ — the PostCSS/autoprefixer toolchain the theme uses. Run npm install (or npm ci) inside website/ once.
  • Python 3.12+ — the reference generators post-process markdown and render the Python runtime API with pdoc. No system packages needed; gen-python.sh creates its own venv.

First-time setup

cd website
npm ci                      # PostCSS toolchain (package-lock.json is committed)
hugo mod get                # fetch the Docsy module at the version pinned in go.mod

Do not run hugo mod get -u — that upgrades past the pinned Docsy version. hugo auto-downloads modules at exactly the pinned versions on the next build.

Generate the reference (do this before building)

Four sections of the site are generated from source, mirroring the live MkDocs pipeline. The rendered trees are committed for preview convenience, but regenerate them whenever the source changes (and CI reruns all four before every build, so they can never drift):

# run from the repo root
./website/scripts/gen-cli.sh       # CLI reference from Cobra   -> content/reference/cli/
./website/scripts/gen-go.sh        # Go packages from gomarkdoc -> content/reference/go/
./website/scripts/gen-openapi.sh   # OpenAPI spec for Scalar    -> static/openapi.yaml
./website/scripts/gen-python.sh    # Python runtime API (pdoc)  -> static/python-api/

Each script is idempotent and documents itself in a header comment. What they do:

ScriptSourceOutputNotes
gen-cli.shgo run ./cmd/leoflow gen-docs (Cobra)content/reference/cli/*.mdAdds Hugo front matter; rewrites .md cross-links to pretty URLs.
gen-go.shgomarkdoc over a fixed package setcontent/reference/go/*.mdFlattens one page per package; adds front matter.
gen-openapi.shdocs/api/openapi.yamlstatic/openapi.yamlRead by static/api-reference.html (the embedded Scalar page).
gen-python.shruntime/python docstrings via pdocstatic/python-api/A self-contained sidecar subsite, linked (not embedded) from reference/python-api.

Preview & build

cd website
hugo server                 # live-reload preview at http://localhost:1313/leoflow/

To produce the exact artifact CI publishes:

cd website
hugo --gc --minify          # output in website/public/

--gc also flags broken internal reference links, so a clean build means the cross-references resolve.

Refresh the migrated pages

Most content pages were mechanically migrated from the live MkDocs tree by the converters under website/scripts/migration/. When docs/ changes upstream and you want to pull those edits into the Hugo tree, rerun them (they never touch hand-authored pages — see that directory’s README.md):

cd website/scripts/migration
python3 test_convert.py            # transforms are unit-tested (must pass)
python3 build_site.py              # regenerates the bulk pages + link-map.csv
python3 build_connections_index.py

How it is wired

  • website/hugo.toml — site config: Docsy module import, the dev/latest version selector, the compact sidebar, and the top-navbar menu.
  • website/layouts/ — the two local overrides: index.html (the landing page) and baseof.html (a one-line Mermaid cache fix; see the comment in the file).
  • website/content/ — the docs tree, one directory per IA section.
  • website/scripts/ — the four reference generators and, under migration/, the MkDocs→Hugo converters.
  • .github/workflows/website-build.yml — CI: installs the toolchains, runs the four generators, then hugo --gc --minify. It only builds an artifact; it never deploys (the live MkDocs site still ships until cutover).
  • .github/workflows/website-deploy.yml.draft — the staged cutover deploy workflow. It is a .draft file, so Actions ignores it; activating it (renaming to .yml) is a deliberate maintainer step, documented in its header.

Redirects (old URLs keep working)

The old MkDocs site serves flat .html URLs (use_directory_urls: false), and the Hugo IA moves most pages into sections. To keep bookmarks and external links alive, each moved page carries a Hugo aliases: block in its front matter — Hugo renders a redirecting stub at the old path, no server config needed (works on GitHub Pages under the /leoflow/ subpath).

That block is generated, not hand-maintained:

cd website
python3 scripts/migration/build_redirects.py   # idempotent; ~211 aliases
python3 scripts/migration/check_links.py        # 0 broken internal links

build_redirects.py reads scripts/migration/link-map.csv plus the generated CLI/Go trees; the marked AUTO redirect aliases block it writes is safe to regenerate. When you move or rename a page, update link-map.csv and rerun it.

The cutover runbook — the exact steps to switch the published site from MkDocs+mike to this one — is documented in the header of .github/workflows/website-deploy.yml.draft (the committed source of truth), and mirrored as a longer local note at website/spec/CUTOVER.md (spec/ is a local-only scratch dir, so that copy is not committed).