From 81cba2765a1452d376be8e139a89daae9dc7c784 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arnaud=20He=CC=81ritier?= Date: Thu, 16 Jul 2026 21:10:45 +0200 Subject: [PATCH] feat(docs): generate self-maintaining llms.txt from nav.yml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a spec-compliant (https://llmstxt.org/) /llms.txt to the edge docs site, generated at Hugo build time from docs/data/nav.yml so it stays in sync as pages are added — deployed by the existing docs-deploy workflow with no new CI or cron. - hugo.yaml: register a plain-text 'llms' output format attached to the home page (outputs.home) so it is actually rendered. - layouts/home.llms.txt: template emitting the H1, blockquote summary, and one H2 per nav section with '- [title](url): note' links (absolute URLs via absURL; note from each page's front-matter description). Flattens both items and groups[].items, and hard-fails the build when a nav page is unresolved or its description is empty. - layouts/_partials/footer.html: add an llms.txt discoverability link. - scripts/docs-check-llms-txt.sh: CI-invoked semantic validator that rebuilds the site and asserts llms.txt matches nav.yml's sections, titles, order, count and per-entry URL — catching template regressions a green build would miss. - docs-lint.yml: wire in the new llms-txt-check job (pinned Hugo). - STYLE.md: document generation, the description requirement, and CI. --- .github/workflows/docs-lint.yml | 38 ++++- docs/STYLE.md | 17 ++ docs/hugo.yaml | 13 ++ docs/layouts/_partials/footer.html | 1 + docs/layouts/home.llms.txt | 24 +++ scripts/docs-check-llms-txt.sh | 243 +++++++++++++++++++++++++++++ 6 files changed, 331 insertions(+), 5 deletions(-) create mode 100644 docs/layouts/home.llms.txt create mode 100755 scripts/docs-check-llms-txt.sh diff --git a/.github/workflows/docs-lint.yml b/.github/workflows/docs-lint.yml index ce7f12ec2..e48e8bb67 100644 --- a/.github/workflows/docs-lint.yml +++ b/.github/workflows/docs-lint.yml @@ -1,8 +1,10 @@ -# Lints the documentation source in docs/**: markdownlint for style -# and lychee (offline) to verify that every relative Markdown link -# resolves to a real file. Portable relative links are what the Hugo -# module mount on docs.docker.com resolves, so broken ones would break -# the downstream build (see docs-upstream.yml). +# Lints the documentation source in docs/**: markdownlint for style, +# lychee (offline) to verify that every relative Markdown link resolves +# to a real file, and a semantic check of the generated /llms.txt +# (https://llmstxt.org/) against its data/nav.yml source of truth. +# Portable relative links are what the Hugo module mount on +# docs.docker.com resolves, so broken ones would break the downstream +# build (see docs-upstream.yml). name: docs-lint permissions: @@ -19,11 +21,18 @@ on: - ".github/workflows/docs-lint.yml" - "docs/**" - "scripts/docs-check-canonical.sh" + - "scripts/docs-check-llms-txt.sh" pull_request: paths: - ".github/workflows/docs-lint.yml" - "docs/**" - "scripts/docs-check-canonical.sh" + - "scripts/docs-check-llms-txt.sh" + +env: + # Kept in sync with docs-deploy.yml / docs-a11y.yml so every docs + # workflow builds with the same Hugo release. + HUGO_VERSION: 0.163.0 jobs: markdownlint: @@ -67,3 +76,22 @@ jobs: --exclude-path docs/index.md --exclude-path docs/404.md "docs/**/*.md" fail: true + + llms-txt-check: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + + - name: Install Hugo + run: | + curl -fsSL "https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.tar.gz" \ + | sudo tar -xz -C /usr/local/bin hugo + hugo version + + # llms.txt is generated at build time from data/nav.yml (STYLE.md + # "llms.txt"); a broken groups[].items traversal or similar template + # regression can silently drop entries while the build stays green, + # so this asserts the built output's content, not just build success. + - name: Check llms.txt + run: ./scripts/docs-check-llms-txt.sh diff --git a/docs/STYLE.md b/docs/STYLE.md index 617986cfb..9e863439c 100644 --- a/docs/STYLE.md +++ b/docs/STYLE.md @@ -93,6 +93,23 @@ scaffolding a new page from an existing one. The homepage, `404.md` and section `_index.md` files are not mirrored pages and don't set one. +## llms.txt + +`/llms.txt` is generated at build time from `data/nav.yml` (see +`layouts/home.llms.txt`): adding a page to the nav automatically adds +it to llms.txt, and pages absent from `nav.yml` never appear there. +Every nav entry must resolve to a real page with a non-empty +`description:` in its front matter (whitespace-only counts as empty) +— the build fails with an `errorf` naming the offending title/url +otherwise, since the spec's `- [title](url): note` shape requires a +note. CI (`docs-lint` / `scripts/docs-check-llms-txt.sh`) additionally +rebuilds the site and asserts the generated `llms.txt` matches +`nav.yml`'s sections, titles, order, count **and per-entry URL** +(each rendered link must match the nav url at the same position, not +just share the site's base URL), catching regressions (e.g. a broken +`groups[].items` traversal, or every entry rendering the same link) +that a successful build alone would not. + ## Availability badges When a page documents a feature that is merged on `main` but not yet diff --git a/docs/hugo.yaml b/docs/hugo.yaml index c70e37883..160ee64f7 100644 --- a/docs/hugo.yaml +++ b/docs/hugo.yaml @@ -15,6 +15,19 @@ title: Docker Agent # emitted at /404.html, which is where GitHub Pages looks for it. disableKinds: [section, taxonomy, term, rss, sitemap, "404"] +# llms.txt (https://llmstxt.org/): a plain-text output format attached to +# the home page only. Without the explicit `outputs.home` entry below the +# format is defined but never rendered, since home currently emits HTML only. +outputFormats: + llms: + mediaType: text/plain + baseName: llms + isPlainText: true + permalinkable: false + +outputs: + home: [html, llms] + params: tagline: Run AI agents like containers. description: Run AI agents from a YAML file. Define them once, share them through any OCI registry, and run them anywhere — by Docker. diff --git a/docs/layouts/_partials/footer.html b/docs/layouts/_partials/footer.html index d819dd5f6..9b6873ce2 100644 --- a/docs/layouts/_partials/footer.html +++ b/docs/layouts/_partials/footer.html @@ -25,6 +25,7 @@

Resources

  • GitHub
  • Agent Catalog
  • Blog
  • +
  • llms.txt