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
ee9c1ea
workspace(feat[packages]): scaffold gp-opengraph and gp-sitemap
tony Apr 21, 2026
c989913
gp-opengraph(feat[parsers]): port description, title, meta parsers
tony Apr 21, 2026
d5feb2e
gp-opengraph(feat[extension]): connect html-page-context hook + meta …
tony Apr 21, 2026
0243f43
gp-sitemap(feat[extension]): sitemap.xml generator with Sphinx 8.1+ i…
tony Apr 21, 2026
1e1ed9a
gp-sphinx(feat[defaults]): integrate gp-opengraph and gp-sitemap
tony Apr 21, 2026
e07b752
docs(feat[gp-sphinx]): document gp-opengraph and gp-sitemap
tony Apr 21, 2026
18f433b
ci(fix[seo-packages]): satisfy -W strict docs build on remote
tony Apr 22, 2026
359bb29
gp-opengraph, gp-sitemap(fix[logging]): attach NullHandler + drop tra…
tony Apr 22, 2026
2755c1c
gp-opengraph(refactor[imports]): use namespace imports for stdlib mod…
tony Apr 22, 2026
a109ace
docs(fix[packages]): correct package-index prose for SEO tier
tony Apr 25, 2026
8f8b5ef
gp-opengraph(fix[setup]): add env_version and tighten setup return type
tony Apr 25, 2026
7d7939a
gp-sitemap(refactor[setup]): tighten setup return type to ExtensionMe…
tony Apr 25, 2026
9f3df96
gp-sitemap(fix[parallel]): drop unsupported parallel_write_safe claim
tony Apr 25, 2026
9ffa05f
gp-sitemap(test[urlset]): cover custom html_file_suffix
tony Apr 25, 2026
cd32883
gp-sphinx(feat[config]): auto-set flat sitemap_url_scheme from docs_url
tony Apr 25, 2026
a69abdf
sphinx-autodoc-fastmcp(fix[collector]): use try/else over None re-ass…
tony Apr 25, 2026
2be0d3a
gp-opengraph(refactor[setup]): drop cargo-culted env_version key
tony Apr 25, 2026
fae236a
gp-opengraph(docs[surface]): split README install/reference from docs…
tony Apr 25, 2026
3883214
gp-sitemap(docs[surface]): refresh docs page parallel contract; close…
tony Apr 25, 2026
7f3d66f
gp-sphinx(docs[coordinator]): expand docs_url coverage and surface SE…
tony Apr 25, 2026
da4a14b
fixup! gp-sitemap(feat[extension]): sitemap.xml generator with Sphinx…
tony Apr 25, 2026
ee74156
sphinx-gp-opengraph(docs[config]) Use sphinx-autodoc-sphinx for confi…
tony Apr 25, 2026
20dbb7e
sphinx-gp-sitemap(docs[config]) Use sphinx-autodoc-sphinx for config …
tony Apr 25, 2026
254165a
sphinx-gp-opengraph(feat[config]) Add description= to every config value
tony Apr 25, 2026
cfacc56
sphinx-gp-sitemap(feat[config]) Add description= to every config value
tony Apr 25, 2026
5f6d251
sphinx-autodoc-fastmcp(feat[config]) Add description= to every config…
tony Apr 25, 2026
8977e1a
sphinx-ux-autodoc-layout(feat[config]) Add description= to every conf…
tony Apr 25, 2026
a15e4ad
sphinx-autodoc-fastmcp(docs[config]) Use sphinx-autodoc-sphinx for co…
tony Apr 25, 2026
61d12c2
sphinx-ux-autodoc-layout(docs[config]) Use sphinx-autodoc-sphinx for …
tony Apr 25, 2026
0fb1812
tests(seo) Mark Sphinx-build tests as integration
tony Apr 25, 2026
6119d0d
tests(seo) Module-scope the build_sitemap_site / build_og_site fixtures
tony Apr 25, 2026
66c8576
sphinx-gp-sitemap(fix[locales]) Treat any all-None sequence as the su…
tony Apr 25, 2026
462f090
sphinx-gp-{opengraph,sitemap}(refactor[logging]) Past-tense event mes…
tony Apr 25, 2026
021d3a0
sphinx-gp-sitemap(fix[urls]) Normalize trailing slash on resolved bas…
tony Apr 25, 2026
607b4fb
docs(_ext[package_reference]): drop surface tables; hand off to autod…
tony Apr 25, 2026
f19e46e
sphinx-autodoc-fastmcp(docs[surface]): autodoc directives and roles v…
tony Apr 25, 2026
8b5b361
sphinx-autodoc-docutils(feat[discovery]): register-aware directive an…
tony Apr 25, 2026
e0bbf64
sphinx-autodoc-sphinx(docs[surface]): autodoc directives via sphinx-a…
tony Apr 25, 2026
a729fb4
docs(packages[surface]): render rich descriptor blocks beside directi…
tony Apr 25, 2026
7f2cfa4
sphinx-autodoc-docutils(fix[discovery]): log a DEBUG breadcrumb when …
tony Apr 25, 2026
6e13de0
docs(CHANGES) Register-aware autodoc discovery + debug breadcrumb
tony Apr 25, 2026
19ab7b2
sphinx-autodoc-docutils(perf[discovery]): cache _replay_setup per module
tony Apr 25, 2026
98b7481
sphinx-autodoc-docutils(refactor[discovery]): expose SetupRecorder + …
tony Apr 25, 2026
5ee2f68
gp-sphinx(fix[linkcode]): thread source_branch into make_linkcode_res…
tony Apr 25, 2026
6393215
sphinx-gp-opengraph(fix[title]): ignore void HTML elements when track…
tony Apr 25, 2026
d7a1104
sphinx-gp-sitemap(fix[parallel]): declare parallel_write_safe=False e…
tony Apr 25, 2026
fecb3be
gp-sphinx(fix[config]): normalise ogp_site_url trailing slash like si…
tony Apr 25, 2026
0cf3f2e
sphinx-gp-opengraph(fix[escape]): centralise html.escape in _make_tag
tony Apr 25, 2026
855c275
docs(CHANGES) Conform to canonical section order and tighten entries
tony Apr 25, 2026
9afa902
sphinx-gp-sitemap(fix[architecture]): collect at build-finished via e…
tony Apr 25, 2026
7aaf86e
sphinx-gp-opengraph(fix[title]): symmetric void-element filter for XH…
tony Apr 25, 2026
22e29e9
docs(_ext[package_reference]): kwarg-aware Sphinx app argument extrac…
tony Apr 25, 2026
697d734
docs(CHANGES) Strip implementation depth from upgrade-time entries
tony Apr 25, 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
278 changes: 164 additions & 114 deletions CHANGES

Large diffs are not rendered by default.

326 changes: 114 additions & 212 deletions docs/_ext/package_reference.py

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ For shared defaults and configuration options, see {doc}`configuration`.
.. autofunction:: gp_sphinx.config.merge_sphinx_config
```

When `docs_url` is provided, `merge_sphinx_config()` also auto-derives
the `ogp_*` and `sitemap_*` keys consumed by the SEO extensions
(`sphinx_gp_opengraph`, `sphinx_gp_sitemap`). See {ref}`from-docs_url` for the full
mapping.

## make_linkcode_resolve

```{eval-rst}
Expand Down
17 changes: 14 additions & 3 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ All parameters are keyword-only.
| `source_branch` | `str` | `"main"` | Source branch stored in `html_theme_options["source_branch"]` |
| `light_logo` | `str \| None` | `None` | Light-mode logo path merged into theme options |
| `dark_logo` | `str \| None` | `None` | Dark-mode logo path merged into theme options |
| `docs_url` | `str \| None` | `None` | Canonical docs URL used to derive Open Graph settings |
| `docs_url` | `str \| None` | `None` | Canonical docs URL. When set, auto-derives `ogp_site_url`, `ogp_site_name`, `ogp_image` (for `sphinx_gp_opengraph`) and `site_url`, `sitemap_url_scheme` (for `sphinx_gp_sitemap`) — see {ref}`from-docs_url` |
| `intersphinx_mapping` | `Mapping[str, tuple[str, str \| None]] \| None` | `None` | Mapping assigned to `intersphinx_mapping` when provided |
| `**overrides` | `Any` | none | Final escape hatch for any Sphinx config key; applied after all defaults and auto-computed values |

Expand All @@ -55,13 +55,24 @@ All parameters are keyword-only.
| `html_theme_options["source_repository"]` | repository URL |
| `html_theme_options["footer_icons"][0]["url"]` | repository URL for the GitHub footer icon |

(from-docs_url)=

### From `docs_url`

| Key | Value |
| --- | --- |
| `ogp_site_url` | `docs_url` |
| `ogp_site_url` | `docs_url` (trailing-slash normalized) |
| `ogp_site_name` | `project` |
| `ogp_image` | `"_static/img/icons/icon-192x192.png"` |
| `site_url` | `docs_url` (trailing-slash normalized) |
| `sitemap_url_scheme` | `"{link}"` |

`sitemap_url_scheme` overrides upstream sphinx-sitemap's default of
`"{lang}{version}{link}"` because git-pull.com sites deploy flat at the
project root with no language or version path segment. Multilingual or
version-pinned hosting can pass an explicit `sitemap_url_scheme` to
`merge_sphinx_config()` to restore the prefixed scheme — `**overrides`
is applied after auto-computation, so the explicit value wins.

### From `**overrides`

Expand Down Expand Up @@ -100,7 +111,7 @@ These are injected even though they are not exposed as `DEFAULT_*` constants:

| Constant | Value |
| --- | --- |
| `DEFAULT_EXTENSIONS` | `["sphinx.ext.autodoc", "sphinx_fonts", "sphinx.ext.intersphinx", "sphinx_autodoc_typehints_gp", "sphinx.ext.todo", "sphinx_inline_tabs", "sphinx_copybutton", "sphinxext.opengraph", "sphinxext.rediraffe", "sphinx_design", "myst_parser", "linkify_issues"]` |
| `DEFAULT_EXTENSIONS` | `["sphinx.ext.autodoc", "sphinx_fonts", "sphinx.ext.intersphinx", "sphinx_autodoc_typehints_gp", "sphinx.ext.todo", "sphinx_inline_tabs", "sphinx_copybutton", "sphinx_gp_opengraph", "sphinx_gp_sitemap", "sphinxext.rediraffe", "sphinx_design", "myst_parser", "linkify_issues"]` |
| `DEFAULT_SOURCE_SUFFIX` | `{".rst": "restructuredtext", ".md": "markdown"}` |
| `DEFAULT_MYST_EXTENSIONS` | `["colon_fence", "substitution", "replacements", "strikethrough", "linkify"]` |
| `DEFAULT_MYST_HEADING_ANCHORS` | `4` |
Expand Down
8 changes: 8 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,3 +133,11 @@ packages/sphinx-autodoc-typehints-gp
packages/gp-sphinx
packages/sphinx-gp-theme
```

```{toctree}
:caption: SEO
:hidden:

packages/sphinx-gp-opengraph
packages/sphinx-gp-sitemap
```
13 changes: 12 additions & 1 deletion docs/packages/gp-sphinx.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,23 @@ globals().update(conf)
## What it injects

- Shared extension defaults, theme defaults, fonts, MyST, napoleon, copybutton, and rediraffe settings.
- Auto-computed values like `issue_url_tpl`, `ogp_site_url`, `ogp_site_name`, and `ogp_image` when repository and docs URLs are provided.
- Auto-computed `issue_url_tpl` and theme source-repository wiring from `source_repository`.
- Auto-computed SEO values when `docs_url` is set: `ogp_site_url`, `ogp_site_name`, `ogp_image` for {doc}`sphinx-gp-opengraph`, plus `site_url` and `sitemap_url_scheme` for {doc}`sphinx-gp-sitemap`. See {ref}`from-docs_url` for the canonical mapping.
- A `setup(app)` hook that registers `js/spa-nav.js` and removes `tabs.js` after HTML builds.
- Support for appending {py:mod}`sphinx:sphinx.ext.linkcode` automatically when `linkcode_resolve` is supplied in `**overrides`.

See {doc}`/configuration` for the complete parameter reference and every shared `DEFAULT_*` constant.

## SEO emission for free

`sphinx_gp_opengraph` and `sphinx_gp_sitemap` are members of
{py:data}`~gp_sphinx.defaults.DEFAULT_EXTENSIONS`, so every project
that calls `merge_sphinx_config()` loads them automatically. Passing
`docs_url=` is the only step required for default SEO emission —
gp-sphinx fills in the upstream config keys both extensions need.
Per-package details live on the {doc}`sphinx-gp-opengraph` and
{doc}`sphinx-gp-sitemap` pages.

:::{admonition} Live example
This site is built with `gp-sphinx`, using the same integration pattern shown
above. See
Expand Down
6 changes: 5 additions & 1 deletion docs/packages/index.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Packages

Twelve workspace packages in three tiers.
Fourteen workspace packages in four tiers.

**Shared infrastructure** — the rendering pipeline that all domain packages consume:
- `sphinx-ux-badges` — badge primitives and colour palette
Expand All @@ -18,6 +18,10 @@ directives, roles, and per-domain indices:
assets:
- `gp-sphinx`, `sphinx-gp-theme`, `sphinx-fonts`

**SEO** — meta-tag and crawlability extensions auto-loaded by
`gp-sphinx` when `docs_url` is set:
- `sphinx-gp-opengraph`, `sphinx-gp-sitemap`

`gp-sphinx` is the umbrella entry point: `merge_sphinx_config()` wires up the
full stack for downstream projects.

Expand Down
28 changes: 28 additions & 0 deletions docs/packages/sphinx-autodoc-fastmcp.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,34 @@ for a plain inline reference.
.. fastmcp-tool-summary::
```

## Config reference

Generated from `app.add_config_value()` registrations in
[`sphinx_autodoc_fastmcp/__init__.py`](https://github.com/git-pull/gp-sphinx/tree/main/packages/sphinx-autodoc-fastmcp/src/sphinx_autodoc_fastmcp/__init__.py).

```{eval-rst}
.. autoconfigvalue-index:: sphinx_autodoc_fastmcp
.. autoconfigvalues:: sphinx_autodoc_fastmcp
```

## Directive and role reference

Generated from `app.add_directive()` and `app.add_role()` registrations in
[`sphinx_autodoc_fastmcp/__init__.py`](https://github.com/git-pull/gp-sphinx/tree/main/packages/sphinx-autodoc-fastmcp/src/sphinx_autodoc_fastmcp/__init__.py)
via `sphinx-autodoc-docutils`. The summary tables index every entry;
the descriptor blocks below carry the per-item signature, badges, and
options.

```{eval-rst}
.. autodirective-index:: sphinx_autodoc_fastmcp

.. autorole-index:: sphinx_autodoc_fastmcp

.. autodirectives:: sphinx_autodoc_fastmcp

.. autoroles:: sphinx_autodoc_fastmcp
```

## Package reference

```{package-reference} sphinx-autodoc-fastmcp
Expand Down
18 changes: 12 additions & 6 deletions docs/packages/sphinx-autodoc-sphinx.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,16 +80,22 @@ Renders all config values from a module at once:
.. autoconfigvalues:: sphinx_config_demo
```

### Document the extension's own directive helper
## Directive reference

Generated from `app.add_directive()` registrations in
[`sphinx_autodoc_sphinx/__init__.py`](https://github.com/git-pull/gp-sphinx/tree/main/packages/sphinx-autodoc-sphinx/src/sphinx_autodoc_sphinx/__init__.py)
via `sphinx-autodoc-docutils` — a meta-loop where the package that
documents config values uses its sibling package to document its own
directives. The summary table indexes every directive; the descriptor
blocks below carry the per-item signature, badge, and options.

```{eval-rst}
.. autodirective:: sphinx_autodoc_sphinx._directives.AutoconfigvalueDirective
:no-index:
.. autodirective-index:: sphinx_autodoc_sphinx

.. autodirectives:: sphinx_autodoc_sphinx
```

The extension itself registers documentation directives rather than new roles
or config values. The generated package reference below lists its registered
surface from the live `setup()` calls.
## Package reference

```{package-reference} sphinx-autodoc-sphinx
```
Expand Down
117 changes: 117 additions & 0 deletions docs/packages/sphinx-gp-opengraph.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
(sphinx-gp-opengraph)=

# sphinx-gp-opengraph

```{gp-sphinx-package-meta} sphinx-gp-opengraph
```

:::{admonition} Alpha
:class: warning

Rendered output is stable. The Python API and Sphinx config value names
may change without a major version bump. Pin your dependency to a
specific version range in production.
:::

OpenGraph meta-tag emission for Sphinx. The package registers every
`ogp_*` config value the upstream
[`sphinxext-opengraph`](https://github.com/sphinx-doc/sphinxext-opengraph)
exposes and emits the same `<meta>` tags, with one deliberate
omission: the matplotlib-based social-card generator is not bundled.
That is why the package has zero non-Sphinx runtime dependencies.

For install, per-page overrides, Twitter-card markup, and the
verbatim deprecation-warning text, see the package
[README](https://github.com/git-pull/gp-sphinx/tree/main/packages/sphinx-gp-opengraph#readme).
This page covers integration with gp-sphinx, the emission pipeline,
the trade-offs, and the auto-generated config-value reference.

## Integration with gp-sphinx

`sphinx_gp_opengraph` ships in {py:data}`~gp_sphinx.defaults.DEFAULT_EXTENSIONS`,
so projects that build through {py:func}`~gp_sphinx.config.merge_sphinx_config`
load it automatically. Passing `docs_url=` to that function auto-derives
three of the most common config values:

| Auto-derived | Source |
| --- | --- |
| `ogp_site_url` | `docs_url` |
| `ogp_site_name` | `project` |
| `ogp_image` | `"_static/img/icons/icon-192x192.png"` |

The canonical reference for these and the other auto-derived values
lives in {ref}`from-docs_url`. Any value passed via `**overrides` to
`merge_sphinx_config()` wins over the auto-derived default —
auto-computation runs first, overrides apply last.

## How the page-level meta tags are built

For every page rendered by an HTML-family builder, the extension's
`html-page-context` handler walks the resolved doctree and emits the
following tags into `context["metatags"]`. The page is skipped when its
front-matter sets `ogp_disable: true`.

| Tag | Source |
| --- | --- |
| `og:title` | First heading of the page, with HTML stripped (`_title.py`) |
| `og:type` | `ogp_type` (default `"website"`) |
| `og:url` | `ogp_canonical_url or ogp_site_url`, joined with the page's relative URL |
| `og:site_name` | `ogp_site_name`, or `project` when unset; suppressed when set to `False` |
| `og:description` | First non-title body paragraph, truncated to `ogp_description_length`, HTML-escaped (`_description.py`) |
| `og:image` | Page front-matter `og:image`, else `ogp_image`, else first in-page image when `ogp_use_first_image=True` |
| `og:image:alt` | Front-matter `og:image:alt`, else `ogp_image_alt`, falling back to site name, then page title |
| `<meta name="description">` | Mirror of `og:description` when `ogp_enable_meta_description=True` and the page does not already define one (`_meta.py`) |

The description extractor walks the document, skips title nodes and
empty paragraphs, takes the first prose paragraph, and truncates at the
configured cap. Embedded HTML quote characters are escaped with
`&quot;` before emission, so user content cannot break out of the
attribute value.

Custom raw markup listed in `ogp_custom_meta_tags` is appended verbatim
after the structured tags — that is the supported escape hatch for
Twitter card declarations and `og:image:width`/`og:image:height` hints.

## Event hooks

```text
config-inited → _warn_if_social_cards_used (deprecation warning)
html-page-context → html_page_context (per-page meta-tag emission)
```

Both hooks live in
[`sphinx_gp_opengraph/__init__.py`](https://github.com/git-pull/gp-sphinx/tree/main/packages/sphinx-gp-opengraph/src/sphinx_gp_opengraph/__init__.py).
There is no `builder-inited` or `build-finished` work — the extension
is purely a per-page transformer.

## Trade-offs

**`ogp_social_cards` is accepted but ignored.** The upstream extension
ships a matplotlib renderer that builds per-page PNGs at
`builder-inited`. sphinx-gp-opengraph deliberately omits the dependency to
keep the install graph small. The config key remains registered so
existing `conf.py` files do not error; setting it logs a single
`WARNING` at `config-inited` directing users to the static-image
workflow documented in the README.

**`parallel_read_safe` and `parallel_write_safe` are both `True`.**
The extension never writes shared state — every emission is
self-contained inside the per-page hook — so it is safe under any
`sphinx-build -j N` value.

## Config reference

Generated from `app.add_config_value()` registrations in
[`sphinx_gp_opengraph/__init__.py`](https://github.com/git-pull/gp-sphinx/tree/main/packages/sphinx-gp-opengraph/src/sphinx_gp_opengraph/__init__.py).

```{eval-rst}
.. autoconfigvalue-index:: sphinx_gp_opengraph
.. autoconfigvalues:: sphinx_gp_opengraph
```

## Package reference

```{package-reference} sphinx-gp-opengraph
```

[Source on GitHub](https://github.com/git-pull/gp-sphinx/tree/main/packages/sphinx-gp-opengraph) · [PyPI](https://pypi.org/project/sphinx-gp-opengraph/)
Loading