A paste-to-an-agent runbook for giving a GitHub repo (that has no site
yet) a clean documentation site: markdown pages under docs/src/,
grouped into a table of contents, built automatically and published at
https://OWNER.github.io/REPO/. Replace OWNER, REPO and AUTHOR
throughout.
One markdown file per page. index.md is the overview every visitor
lands on; each further file is one section of the site:
docs/
zensical.toml # the table of contents + site settings
docs # the build script (below)
src/
index.md # Overview
topic-one.md # Section: Topic One
topic-two.md # Section: Topic Two
[project]
docs_dir = "src"
site_name = "REPO"
site_description = "One line on what the project is."
site_author = "AUTHOR"
nav = [
{ "Overview" = "index.md" },
{ "Topic One" = "topic-one.md" },
{ "Topic Two" = "topic-two.md" },
]
site_url = "https://OWNER.github.io/REPO/"
repo_url = "https://github.com/OWNER/REPO"
repo_name = "OWNER/REPO"
copyright = """
Copyright © YEAR AUTHOR
"""
[project.markdown_extensions.pymdownx.superfences]
custom_fences = [
{ name = "mermaid", class = "mermaid", format = "pymdownx.superfences.fence_code_format" }
]- The
navlist is the site's chapter structure, top to bottom; every entry needs a matching file indocs/src/. - A page is published at
/<file-slug>/-topic-one.mdbecomeshttps://OWNER.github.io/REPO/topic-one/. - To add a section later: drop the file in
docs/src/, add one nav line, push. - Pages are plain markdown; code blocks, tables and mermaid diagrams work out of the box.
A uv script - no virtualenv, no install step anywhere:
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.12"
# dependencies = ["zensical"]
# ///
import subprocess
from pathlib import Path
doc_dir = Path(__file__).resolve().parent
subprocess.run(["zensical", "build"], cwd=doc_dir, check=True)
Build locally any time with uv run --script docs/docs; output lands
in docs/site/ (gitignore it).
name: Documentation
on:
push:
branches: [main]
workflow_dispatch:
permissions:
contents: read
pages: write
id-token: write
concurrency:
group: pages
cancel-in-progress: true
jobs:
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
with:
python-version: "3.13"
- uses: actions/configure-pages@v6
- name: Build documentation
run: uv run --script docs/docs
- uses: actions/upload-pages-artifact@v5
with:
path: docs/site
- name: Deploy documentation
id: deployment
uses: actions/deploy-pages@v5git checkout main
printf 'docs/site/\n' >> .gitignore
git add docs .github .gitignore
git commit -m "docs: documentation site"
git push origin mainEnable Pages in ONE call, directly in workflow build mode:
gh api -X POST repos/OWNER/REPO/pages -F build_type=workflow
gh api repos/OWNER/REPO/pages --jq .build_type # must print: workflowCaution: do not create Pages with a branch source - that enables
legacy build mode, where GitHub's Jekyll renders the branch root
(the README) instead of the workflow's artifact, and the site shows
no chapter pages. If Pages was ever enabled on the repo before,
switch it over:
gh api -X PUT repos/OWNER/REPO/pages -F build_type=workflowgh workflow run docs.yml --ref main
gh run watch $(gh run list --workflow=docs.yml --limit 1 --json databaseId --jq '.[0].databaseId') --exit-statusThen check the live site:
SITE_URL=$(gh api repos/OWNER/REPO/pages --jq .html_url)
curl -s "$SITE_URL" | grep -oE 'generator" content="[^"]*' # zensical-x.y.z, not Jekyll
curl -s "$SITE_URL" | grep -oE '<a href="\./[a-z-]+/"' # the chapter links
curl -s -o /dev/null -w '%{http_code}\n' "$SITE_URL"topic-one/ # a section page: 200If the generator says Jekyll or a section page 404s, Pages is in
legacy mode: run the PUT in step 6, then gh workflow run docs.yml --ref main again. From now on every push to main rebuilds and
republishes the site automatically.