Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
0e217d8
pkg(sphinx-vite-builder): scaffold backend + Sphinx-extension package
tony May 3, 2026
3d166e7
pkg(gp-furo-theme): switch build-backend to sphinx_vite_builder.build
tony May 3, 2026
4c1f3f8
ci: wire pnpm/Node + SKIP env-var for sphinx-vite-builder backend
tony May 3, 2026
1ab7554
ci: register sphinx-vite-builder docs page in toctree + SKIP for root…
tony May 3, 2026
2820307
feat(sphinx-vite-builder[ci]): self-healing PnpmMissingError with pla…
tony May 3, 2026
3c796e5
release(workspace): bump v0.0.1a15 → v0.0.1a16.dev0 (dev release)
tony May 3, 2026
9e3079c
docs(sphinx-vite-builder): add AGENTS.md with the design contract
tony May 3, 2026
b0276ed
docs(sphinx-vite-builder): add CLAUDE.md as a passthrough to AGENTS.md
tony May 3, 2026
78f6b47
docs(sphinx-vite-builder[README]): document the wheel-vs-source contract
tony May 3, 2026
e5c2fdf
docs(index): drop hardcoded package counts from homepage
tony May 3, 2026
25b98de
docs(packages): drop hardcoded counts; let dynamic grid be source of …
tony May 3, 2026
fc1db1e
docs(architecture): drop hardcoded package counts
tony May 3, 2026
48a769e
docs(architecture): cover sphinx-vite-builder and gp-sphinx-vite as b…
tony May 3, 2026
39ab6d3
docs(architecture): convert Tier 2 prose-table to sphinx-design grid
tony May 3, 2026
38a33de
docs(architecture): cross-link package mentions + finish vocabulary c…
tony May 3, 2026
f8a98d2
docs(README): drift-proof package counts and complete the family map
tony May 3, 2026
d3938e5
docs(whats-new): drop "six domain packages" hardcode
tony May 3, 2026
cda1c11
release(workspace): bump v0.0.1a16.dev0 → v0.0.1a16.dev1
tony May 3, 2026
8e18bda
AGENTS.md: Version commit, remove this fragile note later
tony May 3, 2026
a62cff5
pkg(sphinx-vite-builder): add logging.NullHandler to package __init__
tony May 3, 2026
f07108a
pkg(sphinx-vite-builder): drop hatchling from runtime dependencies
tony May 3, 2026
1bf54b0
pkg(sphinx-vite-builder[process]): SIGTERM/SIGKILL the process group …
tony May 3, 2026
a490a93
pkg(sphinx-vite-builder): add doctests to PEP 517 hooks + setup() + r…
tony May 3, 2026
dd1e319
docs(sphinx-vite-builder): walk back Phase-1 placeholder Sphinx-exten…
tony May 3, 2026
3e88e5a
fix(workspace[deps]): add hatchling to dev group for build-backend ty…
tony May 3, 2026
f202d79
ai(rules[AGENTS{sphinx-vite-builder}]): note Phase-1 placeholder for …
tony May 3, 2026
b6b0a55
fix(ci[smoke]): drop runtime import of sphinx_vite_builder.build
tony May 3, 2026
743fe67
fix(ci[smoke]): split sphinx-vite-builder smoke into runtime + build-…
tony May 3, 2026
a164e1f
docs(sphinx-vite-builder[README]): drop comment inside console code b…
tony May 3, 2026
80055d4
ai(rules[AGENTS{sphinx-vite-builder}]): strip hardcoded version refer…
tony May 3, 2026
1fa424f
ai(rules[AGENTS{sphinx-vite-builder}]): drop hardcoded permutation count
tony May 3, 2026
56215dd
pkg(sphinx-vite-builder[build]): exercise build_wheel via SKIP-env-va…
tony May 3, 2026
b15534c
fix(ci[smoke]): assert hatchling absence in Scenario 1 of sphinx-vite…
tony May 3, 2026
581566a
release(workspace): bump v0.0.1a16.dev1 → v0.0.1a16.dev2
tony May 3, 2026
c1f0d7d
pkg(sphinx-vite-builder[extension]): port config layer for Phase 2 se…
tony May 3, 2026
2d975df
pkg(sphinx-vite-builder[extension]): replace setup() placeholder with…
tony May 3, 2026
c2ededd
test(sphinx-vite-builder[extension]): port hooks + config + integrati…
tony May 3, 2026
40ce20b
refactor(workspace): switch consumers from gp_sphinx_vite to sphinx_v…
tony May 3, 2026
e70e9e3
pkg(sphinx-vite-builder[hatch]): hatchling build-hook variant for [to…
tony May 3, 2026
de39bbc
fix(sphinx-vite-builder[hatch-plugin][test]): drop unused arg-type ig…
tony May 3, 2026
919d189
docs(sphinx-vite-builder[README]): external-adoption guide for Phase …
tony May 3, 2026
75f63ba
pkg(workspace): retire gp-sphinx-vite — sphinx-vite-builder is the so…
tony May 3, 2026
1758096
release(workspace): bump v0.0.1a16.dev2 → v0.0.1a16.dev3
tony May 3, 2026
f1de85f
test(sphinx-vite-builder[hooks]): cover Mode.PROD one-shot run_vite_b…
tony May 3, 2026
8b5ead0
test(sphinx-vite-builder[bus]): port AsyncioBus lifecycle suite from …
tony May 3, 2026
fc5c123
pkg(sphinx-vite-builder[hooks]): wrap handler exceptions as Extension…
tony May 3, 2026
5c192cc
ci(smoke[sphinx-vite-builder]): add Scenario 3 — hatch build-hook ent…
tony May 3, 2026
d40ceba
docs(whats-new): document sphinx-vite-builder Phase 1+2+3
tony May 3, 2026
06cc946
docs(architecture+README): mark sphinx-vite-builder as publishable ex…
tony May 3, 2026
5d1a142
docs(CHANGES) sphinx-vite-builder consolidation and a15 unstyled-whee…
tony May 3, 2026
b01ab69
docs: split sphinx-vite-builder out of Internal toctree + drop stale …
tony May 3, 2026
83e841d
docs(sphinx-vite-builder[test-process]): drop stale gp-sphinx-vite su…
tony May 3, 2026
d24e3b0
release(workspace): bump v0.0.1a16.dev3 → v0.0.1a16.dev4
tony May 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,25 @@ jobs:
print(f"{key}={value}")
PY

# `gp-furo-theme`'s build backend (`sphinx_vite_builder.build`)
# runs `pnpm exec vite build` during sdist + wheel construction.
# Without pnpm + Node the backend fast-fails with PnpmMissingError
# and the release pipeline aborts before publish — exactly the
# invariant we want, but it requires the toolchain to be set up
# first. The backend short-circuits cleanly inside the unpacked
# sdist (no `web/` → assume pre-baked) so end users `pip install`
# without pnpm/Node.
- name: Set up pnpm
uses: pnpm/action-setup@v6
with:
version: 10

- name: Set up Node
uses: actions/setup-node@v6
with:
node-version: 22
cache: pnpm

- name: Install workspace dependencies
run: uv sync --all-packages --all-extras --group dev

Expand Down
52 changes: 52 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,17 @@ on:
jobs:
qa:
runs-on: ubuntu-latest
# `gp-furo-theme`'s build backend (`sphinx_vite_builder.build`) runs
# `pnpm exec vite build` during editable installs, which the qa
# runners can't satisfy (no pnpm/Node). qa's purpose is lint/types/
# tests against Python sources — the rendered theme isn't exercised
# here — so we set the documented escape-hatch env var on the whole
# job. The backend then short-circuits and the install succeeds.
# Test suites that build a Sphinx project with html_theme="gp-furo"
# behave the same as they do on a stock checkout without vite —
# Sphinx silently skips the missing static entries (no -W in qa).
env:
SPHINX_VITE_BUILDER_SKIP: "1"
strategy:
fail-fast: false
matrix:
Expand Down Expand Up @@ -75,6 +86,21 @@ jobs:
with:
enable-cache: true

# `gp-furo-theme`'s build backend (`sphinx_vite_builder.build`)
# runs vite during editable install, populating the gitignored
# `static/` tree so the docs build below picks up real CSS / JS.
# The pnpm/Node steps below give the backend a working toolchain.
- name: Set up pnpm
uses: pnpm/action-setup@v6
with:
version: 10

- name: Set up Node
uses: actions/setup-node@v6
with:
node-version: 22
cache: pnpm

- name: Install workspace dependencies
run: uv sync --all-packages --all-extras --group dev

Expand All @@ -97,6 +123,25 @@ jobs:
with:
enable-cache: true

# `gp-furo-theme`'s sdist + wheel both go through
# `sphinx_vite_builder.build`, which runs vite. The wheels
# produced here are consumed by the smoke matrix below — they
# MUST contain populated `static/`, otherwise downstream smoke
# installs render unstyled. pnpm + Node satisfy that toolchain
# requirement; the backend's wheel-from-sdist short-circuit
# (no `web/` in unpacked sdist → assume pre-baked) means smoke
# jobs themselves don't need this setup.
- name: Set up pnpm
uses: pnpm/action-setup@v6
with:
version: 10

- name: Set up Node
uses: actions/setup-node@v6
with:
node-version: 22
cache: pnpm

- name: Install workspace dependencies
run: uv sync --all-packages --all-extras --group dev

Expand Down Expand Up @@ -135,6 +180,7 @@ jobs:
- sphinx-autodoc-typehints-gp
- sphinx-ux-badges
- sphinx-ux-autodoc-layout
- sphinx-vite-builder
steps:
- uses: actions/checkout@v6

Expand All @@ -157,6 +203,12 @@ jobs:

- name: Smoke test root bootstrap install
if: matrix.target == 'root-install'
# The root install transitively builds `gp-furo-theme`, which
# routes through `sphinx_vite_builder.build`. The smoke target
# only verifies that imports resolve — it doesn't exercise the
# rendered theme — so we skip the vite invocation.
env:
SPHINX_VITE_BUILDER_SKIP: "1"
run: python scripts/ci/package_tools.py smoke root-install

- name: Smoke test built package artifact
Expand Down
24 changes: 24 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,30 @@ $ uv add gp-sphinx --prerelease allow

<!-- To maintainers and contributors: Please add notes for the forthcoming version below -->

### What's new

#### New package: `sphinx-vite-builder`

PEP 517 backend, hatchling build hook (`[tool.hatch.build.hooks.vite]`),
and Sphinx extension that orchestrate Vite via pnpm. Wheels ship with
the static tree pre-baked; source builds error loudly when pnpm or
Node isn't on PATH, with copy-pasteable CI setup recipes for GitHub
Actions, CircleCI, Azure Pipelines, and GitLab CI inlined into the
error. `SPHINX_VITE_BUILDER_SKIP=1` short-circuits the orchestration
when an external pipeline owns Vite. Replaces and supersedes
`gp-sphinx-vite`; `merge_sphinx_config(vite_orchestration=True)`
auto-injects the new extension. (#29)

### Bug fixes

#### `gp-furo-theme`: Wheels now ship with vite-built CSS and JS

`0.0.1a15` published wheels with an empty `static/` tree, leaving
docs sites across every consumer unstyled. The new
`sphinx-vite-builder.build` backend runs Vite at release time and
hatchling packs the resulting assets, so a `pip install` from PyPI
gets styled docs without the consumer rebuilding assets locally. (#29)

## gp-sphinx 0.0.1a15 (2026-05-02)

### What's new
Expand Down
24 changes: 14 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# gp-sphinx &middot; [![Python Package](https://img.shields.io/pypi/v/gp-sphinx.svg)](https://pypi.org/project/gp-sphinx/) [![License](https://img.shields.io/github/license/git-pull/gp-sphinx.svg)](https://github.com/git-pull/gp-sphinx/blob/main/LICENSE)

Integrated autodoc design system for Sphinx. Twelve packages in three tiers
that replace ~300 lines of duplicated `docs/conf.py` with ~10 lines and
produce beautiful, consistent API documentation.
An integrated autodoc design system for Sphinx that replaces ~300 lines
of duplicated `docs/conf.py` with ~10 lines and produces beautiful,
consistent API documentation.

## Requirements

Expand Down Expand Up @@ -50,21 +50,25 @@ Out of the box, `merge_sphinx_config()` activates:
- **Componentized layouts** (`sphinx-ux-autodoc-layout`) — card containers, parameter folding, managed signatures
- **Clean type hints** (`sphinx-autodoc-typehints-gp`) — simplified annotations with cross-referenced links, replacing `sphinx-autodoc-typehints` and `sphinx.ext.napoleon`
- **Unified badge system** (`sphinx-ux-badges`) — type and modifier badges with a shared colour palette
- **Six domain autodocumenters** — Python API, argparse CLIs, pytest fixtures, FastMCP tools, docutils directives, Sphinx config values
- **Autodoc extensions** — Python API, argparse CLIs, pytest fixtures, FastMCP tools, docutils directives, Sphinx config values
- **IBM Plex fonts** via Fontsource with preloaded web fonts
- **Full dark mode** theming via CSS custom properties

See the [Gallery](https://gp-sphinx.git-pull.com/gallery.html) for live demos of every component.

## Three-tier architecture
## Workspace architecture

The workspace is organized into three tiers — lower layers never depend on higher ones:
Lower layers never depend on higher ones:

- **Shared infrastructure**: `sphinx-ux-badges`, `sphinx-ux-autodoc-layout`, `sphinx-autodoc-typehints-gp`
- **Domain packages**: `sphinx-autodoc-api-style`, `sphinx-autodoc-docutils`, `sphinx-autodoc-fastmcp`, `sphinx-autodoc-pytest-fixtures`, `sphinx-autodoc-sphinx`
- **Theme and coordinator**: `gp-sphinx`, `sphinx-gp-theme`, `sphinx-fonts`, `sphinx-autodoc-argparse`
- **Common libraries** — `sphinx-ux-badges`, `sphinx-ux-autodoc-layout`, `sphinx-autodoc-typehints-gp`, `sphinx-fonts`
- **Autodoc extensions** — `sphinx-autodoc-api-style`, `sphinx-autodoc-argparse`, `sphinx-autodoc-docutils`, `sphinx-autodoc-fastmcp`, `sphinx-autodoc-pytest-fixtures`, `sphinx-autodoc-sphinx`
- **Build utils** — `sphinx-vite-builder` ([PEP 517](https://peps.python.org/pep-0517/) backend + hatchling build hook + Sphinx extension that runs Vite via pnpm; publishable to PyPI for use outside this workspace)
- **Theme and coordinator** — `gp-sphinx`, `sphinx-gp-theme`, `gp-furo-theme`
- **SEO** — `sphinx-gp-opengraph`, `sphinx-gp-sitemap` (auto-loaded by `gp-sphinx` when `docs_url` is set)

See the [Architecture](https://gp-sphinx.git-pull.com/architecture.html) page for the full package map.
See the [Architecture](https://gp-sphinx.git-pull.com/architecture.html)
and [Packages](https://gp-sphinx.git-pull.com/packages/) pages for the
full package map; the docs site auto-enumerates as the workspace grows.

## More information

Expand Down
2 changes: 1 addition & 1 deletion docs/_ext/package_reference.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
>>> package = workspace_packages()[0]
>>> package["name"] in {
... "gp-furo-theme",
... "gp-sphinx-vite",
... "sphinx-vite-builder",
... "sphinx-gp-opengraph",
... "sphinx-gp-sitemap",
... "gp-sphinx",
Expand Down
132 changes: 107 additions & 25 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@

# Architecture

Twelve workspace packages in three tiers. Lower layers never depend on
higher ones — domain packages consume shared infrastructure, and the
Workspace packages organized in tiers. Lower layers never depend on
higher ones — autodoc extensions consume shared infrastructure, and the
presentation layer wires everything together for downstream projects.

The sidebar groups these twelve packages into four navigation buckets
(Domain Packages, UX, Utils, Internal) — a reader-facing grouping that
is orthogonal to the dependency-ordered tier map below.
The sidebar groups these packages into navigation buckets (Domain Packages,
UX, Utils, Internal) — a reader-facing grouping that is orthogonal to the
dependency-ordered tier map below.

## Tier 1: Shared infrastructure

The rendering pipeline that all domain packages consume:
The rendering pipeline that every autodoc extension consumes:

::::{grid} 1 1 3 3
:gutter: 2
Expand Down Expand Up @@ -43,39 +43,121 @@ Replaces `sphinx-autodoc-typehints` + `sphinx.ext.napoleon`.

::::

## Tier 2: Domain packages
## Tier 2: Autodoc extensions

Domain-specific autodoc extensions that consume Tier 1 and add
project-specific rendering logic:
Domain-specific [autodoc extensions](https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html)
that consume Tier 1 and add project-specific rendering logic. Each
ships directives that generate documentation from a particular
source-construct family:

| Package | Domain | Directives |
|---------|--------|------------|
| {doc}`sphinx-autodoc-api-style <packages/sphinx-autodoc-api-style>` | Standard Python | `autofunction`, `autoclass`, `automodule` |
| {doc}`sphinx-autodoc-argparse <packages/sphinx-autodoc-argparse>` | Custom `argparse` domain — programs, options, subcommands, positionals | `argparse` |
| {doc}`sphinx-autodoc-docutils <packages/sphinx-autodoc-docutils>` | docutils | `autodirective`, `autorole` |
| {doc}`sphinx-autodoc-fastmcp <packages/sphinx-autodoc-fastmcp>` | FastMCP tools | `fastmcp-tool`, `fastmcp-tool-summary` |
| {doc}`sphinx-autodoc-pytest-fixtures <packages/sphinx-autodoc-pytest-fixtures>` | pytest fixtures (extends `py` domain) | `autofixture`, `autofixtures`, `auto-pytest-plugin` |
| {doc}`sphinx-autodoc-sphinx <packages/sphinx-autodoc-sphinx>` | Sphinx config | `autoconfigvalue`, `autoconfigvalues` |
::::{grid} 1 1 2 3
:gutter: 2

:::{grid-item-card} sphinx-autodoc-api-style
:link: packages/sphinx-autodoc-api-style
:link-type: doc

**Subject**: standard Python.
**Directives**: `autofunction`, `autoclass`, `automodule`.
:::

:::{grid-item-card} sphinx-autodoc-argparse
:link: packages/sphinx-autodoc-argparse
:link-type: doc

**Subject**: argparse parsers — programs, options, subcommands, positionals.
**Directives**: `argparse` (custom `argparse` domain).
:::

:::{grid-item-card} sphinx-autodoc-docutils
:link: packages/sphinx-autodoc-docutils
:link-type: doc

**Subject**: docutils directives and roles.
**Directives**: `autodirective`, `autorole`.
:::

:::{grid-item-card} sphinx-autodoc-fastmcp
:link: packages/sphinx-autodoc-fastmcp
:link-type: doc

**Subject**: FastMCP tools, prompts, resources.
**Directives**: `fastmcp-tool`, `fastmcp-tool-summary`.
:::

:::{grid-item-card} sphinx-autodoc-pytest-fixtures
:link: packages/sphinx-autodoc-pytest-fixtures
:link-type: doc

Each domain package calls `app.setup_extension()` to auto-register its
**Subject**: pytest fixtures (extends the `py` domain).
**Directives**: `autofixture`, `autofixtures`, `auto-pytest-plugin`.
:::

:::{grid-item-card} sphinx-autodoc-sphinx
:link: packages/sphinx-autodoc-sphinx
:link-type: doc

**Subject**: Sphinx config values.
**Directives**: `autoconfigvalue`, `autoconfigvalues`.
:::

::::

Each autodoc extension calls `app.setup_extension()` to auto-register its
infrastructure dependencies — downstream projects only need to add the
domain package to their `extensions` list.
package to their `extensions` list.

## Tier 3: Theme and coordinator

| Package | Role |
|---------|------|
| {doc}`gp-sphinx <packages/gp-sphinx>` | Coordinator. `merge_sphinx_config()` wires up the full stack. |
| {doc}`sphinx-gp-theme <packages/sphinx-gp-theme>` | Furo-based theme with CSS variables and SPA navigation. |
| {doc}`gp-furo-theme <packages/gp-furo-theme>` | Tailwind v4 port of upstream Furo for git-pull projects. |
| {doc}`sphinx-fonts <packages/sphinx-fonts>` | IBM Plex via Fontsource — preloaded web fonts. |

## Build tooling

Cross-cutting build utilities that operate outside the docs-build
runtime — one is a [PEP 517](https://peps.python.org/pep-0517/) build
backend invoked when wheels are produced; the other is an opt-in
extension that drives the Vite watcher during `sphinx-autobuild`.
Both let theme authors keep build artefacts (`static/styles/*.css`,
`static/scripts/*.js`) out of VCS while still shipping working wheels
and seamless live-reload during authoring.

::::{grid} 1 1 2 2
:gutter: 2

:::{grid-item-card} sphinx-vite-builder
:link: packages/sphinx-vite-builder
:link-type: doc

[PEP 517](https://peps.python.org/pep-0517/) build backend (or
hatchling build hook via `[tool.hatch.build.hooks.vite]`) that runs
`pnpm exec vite build` before delegating wheel/sdist construction to
hatchling. Also a Sphinx extension that auto-orchestrates
`vite build --watch` during `sphinx-autobuild` and one-shot
`vite build` during plain `sphinx-build`.
Source builds error loudly without pnpm/Node; wheels ship turn-key.
**Publishable for use outside this workspace** — any vite + Sphinx
project can adopt either activation path without depending on the
gp-sphinx coordinator.
:::

::::

## How the tiers connect

Every domain package shares the same badge palette, the same componentized
HTML output structure, and the same type annotation pipeline — so Python
APIs, pytest fixtures, Sphinx config values, docutils directives, and
FastMCP tools all look like they belong together.
Every autodoc extension shares the same badge palette, the same
componentized HTML output structure, and the same type annotation
pipeline — so [Python APIs](packages/sphinx-autodoc-api-style.md),
[pytest fixtures](packages/sphinx-autodoc-pytest-fixtures.md),
[Sphinx config values](packages/sphinx-autodoc-sphinx.md),
[docutils directives](packages/sphinx-autodoc-docutils.md), and
[FastMCP tools](packages/sphinx-autodoc-fastmcp.md) all look like
they belong together.

This is the **one autodoc design system** principle: a change to the shared
infrastructure propagates instantly and consistently across all six
domain packages.
infrastructure propagates instantly and consistently across every autodoc
extension in the workspace.
11 changes: 6 additions & 5 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,12 @@
pytest_fixture_lint_level="none",
rediraffe_redirects="redirects.txt",
intersphinx_mapping=intersphinx_mapping,
# Enable Vite orchestration: under `sphinx-autobuild`, gp-sphinx-vite
# spawns `pnpm exec vite build --watch` so contributors editing
# gp-furo-theme/web/src see fresh CSS/JS on disk without remembering
# a separate command. No-op for `sphinx-build` (mode resolves to
# "prod"), so wheel publishes carry no Node runtime requirement.
# Enable Vite orchestration: under `sphinx-autobuild`,
# sphinx-vite-builder spawns `pnpm exec vite build --watch` so
# contributors editing gp-furo-theme/web/src see fresh CSS/JS on
# disk without remembering a separate command. No-op for
# `sphinx-build` (mode resolves to "prod"), so wheel publishes
# carry no Node runtime requirement.
vite_orchestration=True,
)
globals().update(conf)
Expand Down
Loading
Loading