Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
69 changes: 69 additions & 0 deletions .github/workflows/offline-bundle.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

name: Generate Offline Documentation Bundle

on:
workflow_dispatch:
inputs:
camel_version:
description: 'Camel version to bundle (e.g. 4.18)'
required: true
type: string

jobs:
generate-bundle:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout
uses: actions/checkout@v5

- name: Install dependencies
run: yarn workspaces foreach --all install

- name: Build site
run: yarn build
env:
CAMEL_ENV: production
HUGO_OPTIONS: --buildFuture
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Generate versioned bundle
run: node scripts/generate-offline-bundle.js ${{ inputs.camel_version }}

- name: Create or update GitHub Release
run: |
TAG="docs-${{ inputs.camel_version }}"
FILE="public/camel-docs-${{ inputs.camel_version }}.zip"
TITLE="Apache Camel ${{ inputs.camel_version }} — Offline Documentation"
NOTES="Offline documentation bundle for Apache Camel ${{ inputs.camel_version }}.

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.

Download and unzip locally for AI coding agents or environments with restricted internet access."

# delete existing release if present so we can recreate it
gh release delete "$TAG" --yes 2>/dev/null || true

gh release create "$TAG" "$FILE" \
--title "$TITLE" \
--notes "$NOTES" \
--latest=false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
26 changes: 25 additions & 1 deletion llms-txt-template.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,30 @@ For example:
- HTML: `https://camel.apache.org/components/next/languages/simple-language.html`
- Markdown: `https://camel.apache.org/components/next/languages/simple-language.md`

## 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)
- [Camel 4.14](https://github.com/apache/camel-website/releases/download/docs-4.14/camel-docs-4.14.zip)

Download the zip matching your Camel version, unzip it locally, and read the files from there. Each bundle contains:

```
components/<version>/ — 350+ connector/component docs (Markdown)
manual/ — user manual (Markdown)
catalog/
components/ — 350+ connector/component metadata (JSON)
dataformats/ — data format metadata (JSON)
languages/ — expression language metadata (JSON)
models/ — EIP model metadata (JSON)
others/ — other component metadata (JSON)
schema/
camelYamlDsl-canonical.json — YAML DSL JSON Schema
llms.txt — this file
```

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.

## Key facts

- Apache Camel is a **library**, not a platform — it embeds in your existing Spring Boot or Quarkus application
Expand Down Expand Up @@ -57,7 +81,7 @@ For example:
- [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).
- [Camel LangChain4j](https://camel.apache.org/components/next/langchain4j-chat-component.md): LLM integration via LangChain4j.
- [Camel OpenAI](https://camel.apache.org/components/next/openai-component.md): Native OpenAI component.
- A2A (Agent-to-Agent): Camel supports the A2A protocol for connecting AI agents to enterprise systems.
- [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.

## Tooling

Expand Down
158 changes: 158 additions & 0 deletions scripts/generate-offline-bundle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
const fs = require('fs');
const path = require('path');
const { execFileSync } = require('child_process');

const PUBLIC_DIR = 'public';
const CAMEL_REPO = 'apache/camel';
const CATALOG_BASE = 'catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog';
const YAML_SCHEMA_PATH = 'dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl-canonical.json';

const CATALOG_DIRS = ['components', 'dataformats', 'languages', 'models', 'others'];

const VERSION_DIRS = [
'components'
];

const SHARED_DIRS = [
'manual'
];

function fetchCatalogAndSchema(version) {
const branch = `camel-${version}.x`;
const catalogDir = path.join(PUBLIC_DIR, 'catalog');

console.log(`\nFetching catalog and YAML DSL schema from ${CAMEL_REPO}@${branch}...`);

// fetch YAML DSL canonical schema
const schemaDir = path.join(catalogDir, 'schema');
fs.mkdirSync(schemaDir, { recursive: true });
try {
const schema = execFileSync('gh', [
'api', `repos/${CAMEL_REPO}/contents/${YAML_SCHEMA_PATH}?ref=${branch}`,
'--jq', '.content'
], { encoding: 'utf8' });
fs.writeFileSync(
path.join(schemaDir, 'camelYamlDsl-canonical.json'),
Buffer.from(schema.trim(), 'base64').toString('utf8')
);
console.log(' Fetched schema/camelYamlDsl-canonical.json');
} catch (error) {
console.warn(` Could not fetch YAML DSL schema: ${error.message}`);
}

// fetch catalog JSON files for each category
for (const dir of CATALOG_DIRS) {
const targetDir = path.join(catalogDir, dir);
fs.mkdirSync(targetDir, { recursive: true });
try {
const files = execFileSync('gh', [
'api', `repos/${CAMEL_REPO}/contents/${CATALOG_BASE}/${dir}?ref=${branch}`,
'--jq', '.[].name'
], { encoding: 'utf8' }).trim().split('\n').filter(f => f.endsWith('.json'));

console.log(` Fetching catalog/${dir}/ (${files.length} files)...`);

for (const file of files) {
try {
const content = execFileSync('gh', [
'api', `repos/${CAMEL_REPO}/contents/${CATALOG_BASE}/${dir}/${file}?ref=${branch}`,
'--jq', '.content'
], { encoding: 'utf8' });
fs.writeFileSync(
path.join(targetDir, file),
Buffer.from(content.trim(), 'base64').toString('utf8')
);
} catch {
// skip individual file failures silently
}
}
} catch (error) {
console.warn(` Could not fetch catalog/${dir}: ${error.message}`);
}
}

return fs.existsSync(catalogDir);
}

function main() {
const version = process.argv[2];
if (!version) {
console.error('Usage: node scripts/generate-offline-bundle.js <version>');
console.error('Example: node scripts/generate-offline-bundle.js 4.18');
process.exit(1);
}

const versionDir = `${version}.x`;
const bundleName = `camel-docs-${version}.zip`;
const bundlePath = path.join(PUBLIC_DIR, bundleName);

if (!fs.existsSync(PUBLIC_DIR)) {
console.error(`Cannot generate ${bundleName}: '${PUBLIC_DIR}' directory not found`);
process.exit(1);
}

// collect paths to include (relative to public/)
const includePaths = [];

for (const dir of VERSION_DIRS) {
const fullPath = path.join(PUBLIC_DIR, dir, versionDir);
if (fs.existsSync(fullPath)) {
includePaths.push(`${dir}/${versionDir}/*`);
console.log(` Including ${dir}/${versionDir}/`);
} else {
console.warn(` Skipping ${dir}/${versionDir}/ (not found)`);
}
}

for (const dir of SHARED_DIRS) {
const fullPath = path.join(PUBLIC_DIR, dir);
if (fs.existsSync(fullPath)) {
includePaths.push(`${dir}/*`);
console.log(` Including ${dir}/`);
}
}

if (includePaths.length === 0) {
console.error(`No documentation directories found for version ${version}`);
process.exit(1);
}

// always include llms.txt if present
if (fs.existsSync(path.join(PUBLIC_DIR, 'llms.txt'))) {
includePaths.push('llms.txt');
}

// fetch catalog and YAML DSL schema from the camel repo
if (fetchCatalogAndSchema(version)) {
includePaths.push('catalog/*');
}

// remove stale bundle
if (fs.existsSync(bundlePath)) {
fs.unlinkSync(bundlePath);
}

// build zip: include .md files from doc dirs, plus .json from catalog, plus llms.txt
const zipArgs = ['-r', '-q', bundleName, '.'];
for (const p of includePaths) {
if (p === 'llms.txt') {
zipArgs.push('-i', 'llms.txt');
} else if (p.startsWith('catalog/')) {
zipArgs.push('-i', `${p}.json`);
} else {
zipArgs.push('-i', `${p}.md`);
}
}

try {
execFileSync('zip', zipArgs, { cwd: PUBLIC_DIR, stdio: 'inherit' });

const sizeMb = (fs.statSync(bundlePath).size / (1024 * 1024)).toFixed(1);
console.log(`\nGenerated ${bundleName} (${sizeMb} MB)`);
} catch (error) {
console.error(`Failed to generate ${bundleName}:`, error.message);
process.exit(1);
}
}

main();
Loading