Skip to content

Commit 03dbbff

Browse files
feat: Add Web API with automated data generation
- Implements REST API at /api/ for web-based tool discovery - Provides client-side UI at /api/client/ for browsing tools - Auto-generates JSON data from YAML files via multiple methods: • Jekyll plugin for GitHub Pages builds • Git pre-commit hook for local development • GitHub Actions workflow for CI/CD • NPM scripts for manual control - Adds comprehensive documentation (AUTOMATION.md) - Includes 186 pre-built tool JSON files for instant access - Features semantic search with same scoring as MCP server The Web API complements the MCP server: - MCP: Local stdin/stdout for AI assistants - Web API: GitHub Pages compatible for browsers 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 95c49ff commit 03dbbff

File tree

199 files changed

+17086
-4
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

199 files changed

+17086
-4
lines changed

.github/workflows/build-api.yml

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
name: Build API Data
2+
3+
on:
4+
push:
5+
branches: [ main, master ]
6+
paths:
7+
- '_data/projects/**'
8+
- '_scripts/build-api-data.js'
9+
pull_request:
10+
branches: [ main, master ]
11+
paths:
12+
- '_data/projects/**'
13+
workflow_dispatch:
14+
15+
jobs:
16+
build-api:
17+
runs-on: ubuntu-latest
18+
permissions:
19+
contents: write
20+
21+
steps:
22+
- name: Checkout repository
23+
uses: actions/checkout@v4
24+
25+
- name: Setup Node.js
26+
uses: actions/setup-node@v4
27+
with:
28+
node-version: '18'
29+
cache: 'npm'
30+
31+
- name: Install dependencies
32+
run: npm ci
33+
34+
- name: Build API data
35+
run: node _scripts/build-api-data.js
36+
37+
- name: Commit API data
38+
run: |
39+
git config --local user.email "action@github.com"
40+
git config --local user.name "GitHub Action"
41+
git add api/data/
42+
if git diff --staged --quiet; then
43+
git commit -m "Auto-update API data [skip ci]"
44+
git push
45+
else
46+
echo "No changes to commit"
47+
fi

.gitignore

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,13 @@ _site/
22
.sass-cache
33
.jekyll-cache
44
.jekyll-metadata
5+
6+
# Node.js
7+
node_modules/
8+
package-lock.json
9+
10+
# Generated files (these are built, not edited)
11+
# Note: api/data/ is committed as it's needed for GitHub Pages
12+
drupaltools.log
13+
*.log
14+
.DS_Store

AUTOMATION.md

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
# API Data Automation
2+
3+
This document explains how the API data is automatically generated and kept in sync with the project YAML files.
4+
5+
## Overview
6+
7+
The Web API (`/api/client.html`) uses pre-built JSON data for performance and GitHub Pages compatibility. This data is automatically generated from the YAML files in `_data/projects/`.
8+
9+
## Automation Methods
10+
11+
### 1. Jekyll Plugin (Automatic on GitHub Pages)
12+
- **File**: `_plugins/api_generator.rb`
13+
- **When it runs**: During every Jekyll build
14+
- **How it works**:
15+
- Reads all YAML files from `_data/projects/`
16+
- Generates combined JSON (`api/data/projects.json`)
17+
- Creates JavaScript module (`api/data/drupal-tools-data.js`)
18+
- Creates individual JSON files for each project
19+
20+
### 2. Git Pre-commit Hook (Local Development)
21+
- **File**: `.git/hooks/pre-commit`
22+
- **When it runs**: Before every commit
23+
- **What it does**:
24+
- Checks if any YAML files were staged
25+
- Runs `npm run build-api` if changes detected
26+
- Automatically stages the generated API data
27+
28+
### 3. GitHub Actions (CI/CD)
29+
- **File**: `.github/workflows/build-api.yml`
30+
- **When it runs**: On pushes to main/master, pull requests, or manual trigger
31+
- **Features**:
32+
- Builds API data in isolated environment
33+
- Commits generated data back to repository
34+
- Ensures API is always up-to-date
35+
36+
### 4. NPM Scripts (Manual Control)
37+
```bash
38+
# Build API data once
39+
npm run build-api
40+
41+
# Watch for changes and rebuild automatically
42+
npm run watch-api
43+
44+
# Build and stage before committing
45+
npm run precommit
46+
```
47+
48+
## Data Flow
49+
50+
```
51+
┌─────────────────┐
52+
│ _data/projects/ │
53+
│ *.yml files │
54+
└────────┬────────┘
55+
56+
57+
┌─────────────────┐ ┌──────────────────┐
58+
│ Automation │────▶│ Generated │
59+
│ (Jekyll/Git/NPM) │ │ JSON Data │
60+
└─────────────────┘ └──────────────────┘
61+
62+
63+
┌──────────────────┐
64+
│ GitHub Pages │
65+
│ /api/* │
66+
└──────────────────┘
67+
```
68+
69+
## File Structure After Build
70+
71+
```
72+
api/
73+
├── data/
74+
│ ├── projects.json # All projects + metadata
75+
│ ├── drupal-tools-data.js # JavaScript module
76+
│ ├── blt.json # Individual project data
77+
│ ├── acquia-desktop.json # Individual project data
78+
│ └── ... # One JSON per project
79+
├── client.html # Web UI
80+
├── index.html # API documentation
81+
└── drupal-tools.js # Client library
82+
```
83+
84+
## Troubleshooting
85+
86+
### API data is out of sync
87+
Run: `npm run build-api`
88+
89+
### Pre-commit hook not working
90+
```bash
91+
chmod +x .git/hooks/pre-commit
92+
```
93+
94+
### GitHub Actions failing
95+
Check: `.github/workflows/build-api.yml`
96+
97+
### Local development with auto-rebuild
98+
```bash
99+
npm run watch-api
100+
```
101+
102+
## Performance
103+
104+
- Initial load: ~50KB of JSON data
105+
- Search: Instant (client-side)
106+
- No external dependencies on GitHub Pages
107+
- Cached by browsers for 1 hour

_plugins/api_generator.rb

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# Jekyll plugin to automatically generate API data
2+
# This runs during Jekyll build on GitHub Pages
3+
4+
require 'json'
5+
require 'yaml'
6+
7+
module Jekyll
8+
class ApiDataGenerator < Generator
9+
safe true
10+
priority :low
11+
12+
def generate(site)
13+
# Read all project YAML files
14+
projects = []
15+
categories = Set.new
16+
17+
Dir.glob('_data/projects/*.yml').each do |file|
18+
data = YAML.load_file(file)
19+
next unless data
20+
21+
id = File.basename(file, '.yml')
22+
project = data.merge('id' => id)
23+
projects << project
24+
25+
# Collect categories
26+
if data['category']
27+
data['category'].each { |cat| categories << cat }
28+
end
29+
end
30+
31+
# Create API data structure
32+
api_data = {
33+
'projects' => projects,
34+
'categories' => categories.to_a.sort,
35+
'total' => projects.length,
36+
'updated' => Time.now.iso8601
37+
}
38+
39+
# Write combined data file
40+
File.write('api/data/projects.json', JSON.pretty_generate(api_data))
41+
42+
# Write JavaScript module for easy client-side access
43+
js_content = "// Auto-generated Drupal Tools data\nwindow.DrupalToolsData = #{JSON.generate(api_data)};"
44+
File.write('api/data/drupal-tools-data.js', js_content)
45+
46+
# Create individual project files
47+
projects.each do |project|
48+
File.write(
49+
"api/data/#{project['id']}.json",
50+
JSON.pretty_generate(project)
51+
)
52+
end
53+
54+
Jekyll.logger.info "API Data Generator: Generated #{projects.length} projects with #{categories.size} categories"
55+
end
56+
end
57+
end

_scripts/build-api-data.js

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
#!/usr/bin/env node
2+
3+
// Build script to generate JSON data for the API
4+
// Run this with: node _scripts/build-api-data.js
5+
6+
import fs from 'fs/promises';
7+
import path from 'path';
8+
import yaml from 'js-yaml';
9+
import { fileURLToPath } from 'url';
10+
11+
const __filename = fileURLToPath(import.meta.url);
12+
const __dirname = path.dirname(__filename);
13+
const projectsDir = path.join(__dirname, '../_data/projects');
14+
const outputDir = path.join(__dirname, '../api/data');
15+
16+
async function buildApiData() {
17+
try {
18+
// Ensure output directory exists
19+
await fs.mkdir(outputDir, { recursive: true });
20+
21+
const files = await fs.readdir(projectsDir);
22+
const projects = [];
23+
const categories = new Set();
24+
25+
// Process each YAML file
26+
for (const file of files) {
27+
if (file.endsWith('.yml') || file.endsWith('.yaml')) {
28+
const filePath = path.join(projectsDir, file);
29+
const content = await fs.readFile(filePath, 'utf-8');
30+
const data = yaml.load(content);
31+
32+
const id = file.replace(/\.(yml|yaml)$/, '');
33+
const project = {
34+
...data,
35+
id,
36+
path: filePath
37+
};
38+
39+
projects.push(project);
40+
41+
// Collect categories
42+
if (data.category) {
43+
data.category.forEach(cat => categories.add(cat));
44+
}
45+
}
46+
}
47+
48+
// Write combined data file
49+
const indexData = {
50+
projects,
51+
categories: Array.from(categories).sort(),
52+
total: projects.length,
53+
updated: new Date().toISOString()
54+
};
55+
56+
await fs.writeFile(
57+
path.join(outputDir, 'projects.json'),
58+
JSON.stringify(indexData, null, 2)
59+
);
60+
61+
// Write individual project files
62+
for (const project of projects) {
63+
await fs.writeFile(
64+
path.join(outputDir, `${project.id}.json`),
65+
JSON.stringify(project, null, 2)
66+
);
67+
}
68+
69+
console.log(`✅ Built API data:`);
70+
console.log(` - ${projects.length} projects processed`);
71+
console.log(` - ${categories.size} categories found`);
72+
console.log(` - Data written to api/data/`);
73+
74+
// Generate JavaScript module for GitHub Pages
75+
const jsModule = `// Auto-generated Drupal Tools data
76+
window.DrupalToolsData = ${JSON.stringify(indexData, null, 2)};`;
77+
78+
await fs.writeFile(
79+
path.join(outputDir, 'drupal-tools-data.js'),
80+
jsModule
81+
);
82+
83+
console.log(` - JavaScript module created for GitHub Pages`);
84+
85+
} catch (error) {
86+
console.error('❌ Error building API data:', error);
87+
process.exit(1);
88+
}
89+
}
90+
91+
buildApiData();

_scripts/watch-api.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#!/usr/bin/env node
2+
3+
// Watch for changes in _data/projects and rebuild API data
4+
import fs from 'fs';
5+
import path from 'path';
6+
import { exec } from 'child_process';
7+
8+
const projectsDir = '_data/projects';
9+
10+
console.log('Watching for changes in _data/projects...');
11+
console.log('Press Ctrl+C to stop\n');
12+
13+
// Initial build
14+
console.log('Building initial API data...');
15+
exec('node _scripts/build-api-data.js', (error, stdout, stderr) => {
16+
if (error) {
17+
console.error('Build error:', error);
18+
return;
19+
}
20+
console.log(stdout);
21+
});
22+
23+
// Watch for file changes
24+
fs.watch(projectsDir, { recursive: true }, (eventType, filename) => {
25+
if (filename && (filename.endsWith('.yml') || filename.endsWith('.yaml'))) {
26+
console.log(`\n📝 Detected change in: ${filename}`);
27+
28+
// Debounce rapid changes
29+
setTimeout(() => {
30+
console.log('🔄 Rebuilding API data...');
31+
exec('node _scripts/build-api-data.js', (error, stdout, stderr) => {
32+
if (error) {
33+
console.error('❌ Build error:', error.message);
34+
return;
35+
}
36+
console.log('✅ ' + stdout);
37+
});
38+
}, 500);
39+
}
40+
});

0 commit comments

Comments
 (0)