Skip to content

Commit 32b61f8

Browse files
davsclausclaude
andauthored
CAMEL-23788: Generate versioned offline documentation bundles (#1667)
* CAMEL-23788: Generate versioned offline documentation bundles Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * CAMEL-23788: drop camel-spring-boot from offline bundle Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * CAMEL-23788: drop kamelets from offline bundle, keep pure core camel Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * CAMEL-23788: use connectors/components wording for AI clarity Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * CAMEL-23788: include Camel Catalog and YAML DSL schema in offline bundle Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * CAMEL-23788: document zip directory structure in llms.txt for AI agents Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: update llms.txt with camel-a2a component link and description Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent ef281f3 commit 32b61f8

3 files changed

Lines changed: 252 additions & 1 deletion

File tree

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
#
2+
# Licensed to the Apache Software Foundation (ASF) under one or more
3+
# contributor license agreements. See the NOTICE file distributed with
4+
# this work for additional information regarding copyright ownership.
5+
# The ASF licenses this file to You under the Apache License, Version 2.0
6+
# (the "License"); you may not use this file except in compliance with
7+
# the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
#
17+
18+
name: Generate Offline Documentation Bundle
19+
20+
on:
21+
workflow_dispatch:
22+
inputs:
23+
camel_version:
24+
description: 'Camel version to bundle (e.g. 4.18)'
25+
required: true
26+
type: string
27+
28+
jobs:
29+
generate-bundle:
30+
runs-on: ubuntu-latest
31+
permissions:
32+
contents: write
33+
steps:
34+
- name: Checkout
35+
uses: actions/checkout@v5
36+
37+
- name: Install dependencies
38+
run: yarn workspaces foreach --all install
39+
40+
- name: Build site
41+
run: yarn build
42+
env:
43+
CAMEL_ENV: production
44+
HUGO_OPTIONS: --buildFuture
45+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
46+
47+
- name: Generate versioned bundle
48+
run: node scripts/generate-offline-bundle.js ${{ inputs.camel_version }}
49+
50+
- name: Create or update GitHub Release
51+
run: |
52+
TAG="docs-${{ inputs.camel_version }}"
53+
FILE="public/camel-docs-${{ inputs.camel_version }}.zip"
54+
TITLE="Apache Camel ${{ inputs.camel_version }} — Offline Documentation"
55+
NOTES="Offline documentation bundle for Apache Camel ${{ inputs.camel_version }}.
56+
57+
Contains 350+ connector/component docs (Markdown), the user manual, the Camel Catalog (JSON metadata for all connectors/components, data formats, languages, and EIPs), and the YAML DSL canonical JSON Schema.
58+
59+
Download and unzip locally for AI coding agents or environments with restricted internet access."
60+
61+
# delete existing release if present so we can recreate it
62+
gh release delete "$TAG" --yes 2>/dev/null || true
63+
64+
gh release create "$TAG" "$FILE" \
65+
--title "$TITLE" \
66+
--notes "$NOTES" \
67+
--latest=false
68+
env:
69+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

llms-txt-template.md

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,30 @@ For example:
77
- HTML: `https://camel.apache.org/components/next/languages/simple-language.html`
88
- Markdown: `https://camel.apache.org/components/next/languages/simple-language.md`
99

10+
## Offline documentation bundles
11+
12+
For agents or environments with no or restricted internet access, versioned offline documentation bundles are available as zip archives of all Markdown files:
13+
- [Camel 4.18](https://github.com/apache/camel-website/releases/download/docs-4.18/camel-docs-4.18.zip)
14+
- [Camel 4.14](https://github.com/apache/camel-website/releases/download/docs-4.14/camel-docs-4.14.zip)
15+
16+
Download the zip matching your Camel version, unzip it locally, and read the files from there. Each bundle contains:
17+
18+
```
19+
components/<version>/ — 350+ connector/component docs (Markdown)
20+
manual/ — user manual (Markdown)
21+
catalog/
22+
components/ — 350+ connector/component metadata (JSON)
23+
dataformats/ — data format metadata (JSON)
24+
languages/ — expression language metadata (JSON)
25+
models/ — EIP model metadata (JSON)
26+
others/ — other component metadata (JSON)
27+
schema/
28+
camelYamlDsl-canonical.json — YAML DSL JSON Schema
29+
llms.txt — this file
30+
```
31+
32+
The `catalog/` JSON files contain machine-readable metadata for every connector/component, data format, language, and EIP — parameters, types, defaults, and descriptions. The YAML DSL schema is the definitive spec for validating and generating Camel YAML routes.
33+
1034
## Key facts
1135

1236
- Apache Camel is a **library**, not a platform — it embeds in your existing Spring Boot or Quarkus application
@@ -57,7 +81,7 @@ For example:
5781
- [Camel MCP Server](https://camel.apache.org/manual/camel-jbang-mcp.md): Model Context Protocol server for AI coding assistants (Claude Code, GitHub Copilot, Cursor, Gemini CLI).
5882
- [Camel LangChain4j](https://camel.apache.org/components/next/langchain4j-chat-component.md): LLM integration via LangChain4j.
5983
- [Camel OpenAI](https://camel.apache.org/components/next/openai-component.md): Native OpenAI component.
60-
- A2A (Agent-to-Agent): Camel supports the A2A protocol for connecting AI agents to enterprise systems.
84+
- [Camel A2A](https://camel.apache.org/components/next/a2a-component.md): Agent-to-Agent (A2A) protocol component — expose Camel routes as A2A agents or call remote A2A agents. Supports HTTP+JSON and JSONRPC bindings, OAuth/OIDC/API-key auth, and SSE streaming.
6185

6286
## Tooling
6387

scripts/generate-offline-bundle.js

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
const fs = require('fs');
2+
const path = require('path');
3+
const { execFileSync } = require('child_process');
4+
5+
const PUBLIC_DIR = 'public';
6+
const CAMEL_REPO = 'apache/camel';
7+
const CATALOG_BASE = 'catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog';
8+
const YAML_SCHEMA_PATH = 'dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl-canonical.json';
9+
10+
const CATALOG_DIRS = ['components', 'dataformats', 'languages', 'models', 'others'];
11+
12+
const VERSION_DIRS = [
13+
'components'
14+
];
15+
16+
const SHARED_DIRS = [
17+
'manual'
18+
];
19+
20+
function fetchCatalogAndSchema(version) {
21+
const branch = `camel-${version}.x`;
22+
const catalogDir = path.join(PUBLIC_DIR, 'catalog');
23+
24+
console.log(`\nFetching catalog and YAML DSL schema from ${CAMEL_REPO}@${branch}...`);
25+
26+
// fetch YAML DSL canonical schema
27+
const schemaDir = path.join(catalogDir, 'schema');
28+
fs.mkdirSync(schemaDir, { recursive: true });
29+
try {
30+
const schema = execFileSync('gh', [
31+
'api', `repos/${CAMEL_REPO}/contents/${YAML_SCHEMA_PATH}?ref=${branch}`,
32+
'--jq', '.content'
33+
], { encoding: 'utf8' });
34+
fs.writeFileSync(
35+
path.join(schemaDir, 'camelYamlDsl-canonical.json'),
36+
Buffer.from(schema.trim(), 'base64').toString('utf8')
37+
);
38+
console.log(' Fetched schema/camelYamlDsl-canonical.json');
39+
} catch (error) {
40+
console.warn(` Could not fetch YAML DSL schema: ${error.message}`);
41+
}
42+
43+
// fetch catalog JSON files for each category
44+
for (const dir of CATALOG_DIRS) {
45+
const targetDir = path.join(catalogDir, dir);
46+
fs.mkdirSync(targetDir, { recursive: true });
47+
try {
48+
const files = execFileSync('gh', [
49+
'api', `repos/${CAMEL_REPO}/contents/${CATALOG_BASE}/${dir}?ref=${branch}`,
50+
'--jq', '.[].name'
51+
], { encoding: 'utf8' }).trim().split('\n').filter(f => f.endsWith('.json'));
52+
53+
console.log(` Fetching catalog/${dir}/ (${files.length} files)...`);
54+
55+
for (const file of files) {
56+
try {
57+
const content = execFileSync('gh', [
58+
'api', `repos/${CAMEL_REPO}/contents/${CATALOG_BASE}/${dir}/${file}?ref=${branch}`,
59+
'--jq', '.content'
60+
], { encoding: 'utf8' });
61+
fs.writeFileSync(
62+
path.join(targetDir, file),
63+
Buffer.from(content.trim(), 'base64').toString('utf8')
64+
);
65+
} catch {
66+
// skip individual file failures silently
67+
}
68+
}
69+
} catch (error) {
70+
console.warn(` Could not fetch catalog/${dir}: ${error.message}`);
71+
}
72+
}
73+
74+
return fs.existsSync(catalogDir);
75+
}
76+
77+
function main() {
78+
const version = process.argv[2];
79+
if (!version) {
80+
console.error('Usage: node scripts/generate-offline-bundle.js <version>');
81+
console.error('Example: node scripts/generate-offline-bundle.js 4.18');
82+
process.exit(1);
83+
}
84+
85+
const versionDir = `${version}.x`;
86+
const bundleName = `camel-docs-${version}.zip`;
87+
const bundlePath = path.join(PUBLIC_DIR, bundleName);
88+
89+
if (!fs.existsSync(PUBLIC_DIR)) {
90+
console.error(`Cannot generate ${bundleName}: '${PUBLIC_DIR}' directory not found`);
91+
process.exit(1);
92+
}
93+
94+
// collect paths to include (relative to public/)
95+
const includePaths = [];
96+
97+
for (const dir of VERSION_DIRS) {
98+
const fullPath = path.join(PUBLIC_DIR, dir, versionDir);
99+
if (fs.existsSync(fullPath)) {
100+
includePaths.push(`${dir}/${versionDir}/*`);
101+
console.log(` Including ${dir}/${versionDir}/`);
102+
} else {
103+
console.warn(` Skipping ${dir}/${versionDir}/ (not found)`);
104+
}
105+
}
106+
107+
for (const dir of SHARED_DIRS) {
108+
const fullPath = path.join(PUBLIC_DIR, dir);
109+
if (fs.existsSync(fullPath)) {
110+
includePaths.push(`${dir}/*`);
111+
console.log(` Including ${dir}/`);
112+
}
113+
}
114+
115+
if (includePaths.length === 0) {
116+
console.error(`No documentation directories found for version ${version}`);
117+
process.exit(1);
118+
}
119+
120+
// always include llms.txt if present
121+
if (fs.existsSync(path.join(PUBLIC_DIR, 'llms.txt'))) {
122+
includePaths.push('llms.txt');
123+
}
124+
125+
// fetch catalog and YAML DSL schema from the camel repo
126+
if (fetchCatalogAndSchema(version)) {
127+
includePaths.push('catalog/*');
128+
}
129+
130+
// remove stale bundle
131+
if (fs.existsSync(bundlePath)) {
132+
fs.unlinkSync(bundlePath);
133+
}
134+
135+
// build zip: include .md files from doc dirs, plus .json from catalog, plus llms.txt
136+
const zipArgs = ['-r', '-q', bundleName, '.'];
137+
for (const p of includePaths) {
138+
if (p === 'llms.txt') {
139+
zipArgs.push('-i', 'llms.txt');
140+
} else if (p.startsWith('catalog/')) {
141+
zipArgs.push('-i', `${p}.json`);
142+
} else {
143+
zipArgs.push('-i', `${p}.md`);
144+
}
145+
}
146+
147+
try {
148+
execFileSync('zip', zipArgs, { cwd: PUBLIC_DIR, stdio: 'inherit' });
149+
150+
const sizeMb = (fs.statSync(bundlePath).size / (1024 * 1024)).toFixed(1);
151+
console.log(`\nGenerated ${bundleName} (${sizeMb} MB)`);
152+
} catch (error) {
153+
console.error(`Failed to generate ${bundleName}:`, error.message);
154+
process.exit(1);
155+
}
156+
}
157+
158+
main();

0 commit comments

Comments
 (0)