You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2026/09/18 11:58:25 UTC
(camel-website) 01/01: Fix #1764: generate the offline bundle list in llms.txt from the documented Camel versions
This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch fix/1764
in repository https://gitbox.apache.org/repos/asf/camel-website.git
commit f88f51c7cef9eebb9f702006eeecbaa622eb2d19
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Fri Sep 18 13:58:18 2026 +0200
Fix #1764: generate the offline bundle list in llms.txt from the documented Camel versions
The bundle section of llms.txt was hand-written and listed only Camel 4.18,
although a docs-4.22 bundle has been published since August. The list is
now generated from the versioned component directories Antora builds
(public/components/<major.minor>.x), so it follows the branches in the
Antora playbook. The stale note that the 4.18 bundle lacks the YAML DSL
schema is gone (the bundle script has fetched it since), and llms.txt now
says next has no bundle and is read online.
Co-Authored-By: Claude Opus 5 (1M context) <no...@anthropic.com>
---
README.md | 7 ++++++
gulp/helpers/llms-txt.js | 57 ++++++++++++++++++++++++++++++++++++++++++++----
llms-txt-template.md | 6 ++---
test/llms-txt-test.js | 47 +++++++++++++++++++++++++++++++++++++++
4 files changed, 110 insertions(+), 7 deletions(-)
diff --git a/README.md b/README.md
index 449b3b35..f1c7618c 100644
--- a/README.md
+++ b/README.md
@@ -497,6 +497,13 @@ For example, the 4.18 bundle would be at `https://github.com/apache/camel-websit
Trigger this after each Camel release. For example, when Camel `4.18.1` ships, run the workflow
with version `4.18` to update the `docs-4.18` bundle with the latest documentation.
+The bundle list in [`/llms.txt`](https://camel.apache.org/llms.txt) is not maintained by hand:
+`gulp/helpers/llms-txt.js` fills the `<!-- offline-bundles -->` placeholder in
+`llms-txt-template.md` with one link per versioned directory under `public/components/`, that is
+per branch listed for the component docs in `antora-playbook-production.yml`. So when a new LTS
+branch is added to the playbook, run this workflow for that version too, otherwise `llms.txt`
+links to a bundle that does not exist yet. There is no bundle for `next`.
+
## Search Indexing Configuration
The website uses [Algolia DocSearch](https://docsearch.algolia.com/) to provide site-wide search functionality. The search configuration is defined in [`.docsearch.config.json`](.docsearch.config.json).
diff --git a/gulp/helpers/llms-txt.js b/gulp/helpers/llms-txt.js
index 8f23afdc..0b01ca38 100644
--- a/gulp/helpers/llms-txt.js
+++ b/gulp/helpers/llms-txt.js
@@ -1,22 +1,71 @@
const fs = require('fs');
const path = require('path');
+// Placeholder in llms-txt-template.md that the generated bundle list replaces
+const BUNDLE_MARKER = '<!-- offline-bundles -->';
+const BUNDLE_DOWNLOAD_BASE = 'https://github.com/apache/camel-website/releases/download';
+
+/**
+ * Lists the Camel versions that have versioned documentation built into the site, newest first.
+ * Antora writes one directory per documented branch (4.18.x, 4.22.x, ...) next to the unversioned
+ * `latest` and `next` aliases, so the site build itself is the source of truth for which offline
+ * bundles exist.
+ *
+ * @param {string} componentsDir - Directory holding the versioned component docs
+ * @returns {Array<string>} major.minor versions, e.g. ['4.22', '4.18']
+ */
+function bundleVersions(componentsDir = 'public/components') {
+ if (!fs.existsSync(componentsDir)) {
+ return [];
+ }
+ return fs.readdirSync(componentsDir)
+ .filter(name => /^\d+\.\d+\.x$/.test(name))
+ .map(name => name.replace(/\.x$/, ''))
+ .sort((a, b) => {
+ const [aMajor, aMinor] = a.split('.').map(Number);
+ const [bMajor, bMinor] = b.split('.').map(Number);
+ return bMajor - aMajor || bMinor - aMinor;
+ });
+}
+
+/**
+ * Fills the template with the offline bundle list.
+ *
+ * @param {string} template - Content of llms-txt-template.md
+ * @param {Array<string>} versions - Camel versions with an offline bundle, as returned by bundleVersions
+ * @returns {string} the llms.txt content
+ */
+function renderLlmsTxt(template, versions) {
+ if (!template.includes(BUNDLE_MARKER)) {
+ throw new Error(`llms.txt template has no ${BUNDLE_MARKER} placeholder for the offline bundle list`);
+ }
+ if (versions.length === 0) {
+ throw new Error('No versioned component docs found, cannot list the offline documentation bundles');
+ }
+ const bundleList = versions
+ .map(version => `- [Camel ${version}](${BUNDLE_DOWNLOAD_BASE}/docs-${version}/camel-docs-${version}.zip)`)
+ .join('\n');
+ return template.replace(BUNDLE_MARKER, bundleList);
+}
+
/**
* Generates the /llms.txt file as per https://llmstxt.org/ specification.
* This file helps LLMs discover and understand the structure of the documentation.
- * Reads from llms-txt-template.md and uses it as content.
+ * Reads from llms-txt-template.md and fills in the offline bundle list from the built site.
*
* @param {Array<string>} pages - Array of page URLs that were converted to markdown
*/
function generateLlmsTxt(pages) {
// Read the template file
const templatePath = path.join(__dirname, '../../llms-txt-template.md');
- let llmsTxtContent = fs.readFileSync(templatePath, 'utf8');
+ const template = fs.readFileSync(templatePath, 'utf8');
- fs.writeFileSync('public/llms.txt', llmsTxtContent, 'utf8');
+ fs.writeFileSync('public/llms.txt', renderLlmsTxt(template, bundleVersions()), 'utf8');
console.log('Generated /llms.txt');
}
module.exports = {
- generateLlmsTxt
+ generateLlmsTxt,
+ bundleVersions,
+ renderLlmsTxt
};
diff --git a/llms-txt-template.md b/llms-txt-template.md
index f5b13d97..3c08defc 100644
--- a/llms-txt-template.md
+++ b/llms-txt-template.md
@@ -9,10 +9,10 @@ For example:
## Offline documentation bundles
-For agents or environments with no or restricted internet access, versioned offline documentation bundles are available as zip archives of all Markdown files:
-- [Camel 4.18](https://github.com/apache/camel-website/releases/download/docs-4.18/camel-docs-4.18.zip) (does not include the canonical YAML DSL JSON Schema — use the [online schema](https://github.com/apache/camel/blob/camel-4.18.x/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json) instead)
+For agents or environments with no or restricted internet access, versioned offline documentation bundles are available as zip archives of all Markdown files, one per documented Camel version:
+<!-- offline-bundles -->
-Download the zip matching your Camel version, unzip it locally, and read the files from there. Each bundle contains:
+Download the zip matching your Camel version, unzip it locally, and read the files from there. There is no bundle for `next` (the unreleased main branch): that documentation changes daily, read it online under `https://camel.apache.org/components/next/`. Each bundle contains:
```
components/<version>/ — 350+ connector/component docs (Markdown)
diff --git a/test/llms-txt-test.js b/test/llms-txt-test.js
new file mode 100644
index 00000000..77753a62
--- /dev/null
+++ b/test/llms-txt-test.js
@@ -0,0 +1,47 @@
+'use strict'
+
+const assert = require('node:assert/strict')
+const fs = require('node:fs')
+const os = require('node:os')
+const path = require('node:path')
+const test = require('node:test')
+
+const { bundleVersions, renderLlmsTxt } = require('../gulp/helpers/llms-txt')
+
+function componentsDir (...names) {
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'llms-txt-'))
+ for (const name of names) fs.mkdirSync(path.join(dir, name))
+ return dir
+}
+
+const TEMPLATE = 'Bundles:\n<!-- offline-bundles -->\n\nUnzip it.\n'
+
+test('the built versioned component docs give the bundle versions, newest first', () => {
+ const dir = componentsDir('4.18.x', 'latest', 'next', '4.22.x', '4.8.x', 'README.md')
+
+ assert.deepEqual(bundleVersions(dir), ['4.22', '4.18', '4.8'])
+})
+
+test('a missing components directory yields no versions', () => {
+ assert.deepEqual(bundleVersions(path.join(os.tmpdir(), 'llms-txt-missing')), [])
+})
+
+test('the placeholder becomes one download link per version', () => {
+ assert.equal(renderLlmsTxt(TEMPLATE, ['4.22', '4.18']), `Bundles:
+- [Camel 4.22](https://github.com/apache/camel-website/releases/download/docs-4.22/camel-docs-4.22.zip)
+- [Camel 4.18](https://github.com/apache/camel-website/releases/download/docs-4.18/camel-docs-4.18.zip)
+
+Unzip it.
+`)
+})
+
+test('rendering fails rather than publishing an empty or unfilled bundle list', () => {
+ assert.throws(() => renderLlmsTxt(TEMPLATE, []), /No versioned component docs/)
+ assert.throws(() => renderLlmsTxt('no placeholder', ['4.22']), /placeholder/)
+})
+
+test('the real template carries the placeholder', () => {
+ const template = fs.readFileSync(path.join(__dirname, '../llms-txt-template.md'), 'utf8')
+
+ assert.match(renderLlmsTxt(template, ['4.22']), /^- \[Camel 4\.22\]\(.*camel-docs-4\.22\.zip\)$/m)
+})