summaryrefslogtreecommitdiff
path: root/docs/conf.py
diff options
context:
space:
mode:
authorYuriy Andamasov <yuriy@vyos.io>2026-05-07 08:48:02 +0300
committerYuriy Andamasov <yuriy@vyos.io>2026-05-07 08:48:02 +0300
commit23d6a3ef8afb0fddc0cc7c4591014b971ee2edf4 (patch)
treeca3a1096f03541ec2d994f0157ce90c7e9d5f0f7 /docs/conf.py
parentb8fc0b98dadc8eabf584ed9db599c37f454dfaaf (diff)
downloadvyos-documentation-23d6a3ef8afb0fddc0cc7c4591014b971ee2edf4.tar.gz
vyos-documentation-23d6a3ef8afb0fddc0cc7c4591014b971ee2edf4.zip
docs: render llms.txt at build time (circinus)
Parity with [#1874](https://github.com/vyos/vyos-documentation/pull/1874) (current) and [#1876](https://github.com/vyos/vyos-documentation/pull/1876) (sagitta). Replaces the static `docs/_html_extra/llms.txt` with a Jinja template (`docs/_templates/llms.txt.j2`) rendered at build time via a `build-finished` hook in `conf.py`. URLs and the version line are interpolated from `html_baseurl` and `release`, so the rendered file always matches what circinus actually serves. Hook behavior matches `current`/`sagitta` exactly, including the two review-cycle hardenings landed on those PRs: - Skips on builders other than `html` and `readthedocs` — the curated template encodes `.html` URLs which don't match `dirhtml` output. - `RuntimeError` if `html_baseurl` is empty rather than silently rendering broken root-relative URLs. - Jinja `Environment(undefined=StrictUndefined)` so a typo in `llms.txt.j2` raises at build time instead of writing a half-blank `llms.txt`. The closed PR [#1875](https://github.com/vyos/vyos-documentation/pull/1875) was the previous attempt; this re-lands the change after the post-flip rebase situation cleared. Verified locally: hook tests (happy-path render, fail-fast on `html_baseurl=None`/`''`, `StrictUndefined` catches `{{ relase }}` typo, skips `dirhtml`, skips on build exception) pass against this worktree. \xf0\x9f\xa4\x96 Generated by [robots](https://vyos.io)
Diffstat (limited to 'docs/conf.py')
-rw-r--r--docs/conf.py38
1 files changed, 36 insertions, 2 deletions
diff --git a/docs/conf.py b/docs/conf.py
index 23c76d3e..ec95bf76 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -147,8 +147,10 @@ html_context = {
# sphinx-sitemap: baseurl already includes /en/1.5/, so skip lang+version
sitemap_url_scheme = '{link}'
-# sphinx-llms-txt: disable auto-generated llms.txt, keep curated one from
-# _html_extra; llms-full.txt is still auto-generated
+# sphinx-llms-txt: disable the package's auto-generated index llms.txt.
+# The curated llms.txt is rendered at build time from
+# _templates/llms.txt.j2 by the _write_llms_txt() build-finished hook
+# below; llms-full.txt is still auto-generated by sphinx-llms-txt.
llms_txt_file = False
# Custom sidebar templates, must be a dictionary that maps document names
@@ -258,6 +260,38 @@ def _copy_md_sources(app, exception):
shutil.copy2(path, dest)
+def _write_llms_txt(app, exception):
+ # Skip dirhtml: the curated template encodes `.html` URLs (e.g.
+ # `quick-start.html`), which don't exist under `dirhtml` output
+ # (`quick-start/index.html`). Production publishes via the html /
+ # readthedocs builders, so dirhtml output would just be misleading.
+ if exception is not None or app.builder.name not in (
+ 'html', 'readthedocs'):
+ return
+ if not app.config.html_baseurl:
+ # Fail loudly rather than rendering /quick-start.html etc. as a
+ # silently-broken root-relative URL — every supported branch
+ # sets html_baseurl, so a missing value is a regression.
+ raise RuntimeError(
+ 'html_baseurl must be set to render llms.txt')
+ from pathlib import Path
+ from jinja2 import Environment, StrictUndefined
+ tpl_path = Path(app.srcdir) / '_templates' / 'llms.txt.j2'
+ out_path = Path(app.outdir) / 'llms.txt'
+ baseurl = app.config.html_baseurl.rstrip('/') + '/'
+ # StrictUndefined: missing template variables raise rather than
+ # silently render as empty strings, so a typo in llms.txt.j2 fails
+ # the build instead of shipping a half-blank llms.txt.
+ env = Environment(undefined=StrictUndefined, keep_trailing_newline=True)
+ template = env.from_string(tpl_path.read_text(encoding='utf-8'))
+ rendered = template.render(
+ baseurl=baseurl,
+ release=app.config.release,
+ )
+ out_path.write_text(rendered, encoding='utf-8')
+
+
def setup(app):
app.connect('builder-inited', _prefer_webp)
app.connect('build-finished', _copy_md_sources)
+ app.connect('build-finished', _write_llms_txt)