Files
wehub-resource-sync 91e75e620b
CI: cua-driver distro-compat matrix / Resolve release version (push) Waiting to run
CI: cua-driver distro-compat matrix / debian:12 (glibc 2.36) (push) Blocked by required conditions
CI: cua-driver distro-compat matrix / fedora:41 (glibc 2.40) (push) Blocked by required conditions
CI: cua-driver distro-compat matrix / rockylinux:9 (glibc 2.34) (push) Blocked by required conditions
CI: cua-driver distro-compat matrix / ubuntu:22.04 (glibc 2.35) (push) Blocked by required conditions
CI: cua-driver distro-compat matrix / ubuntu:24.04 (glibc 2.39) (push) Blocked by required conditions
CI: cua-driver distro-compat matrix / Distro compat summary (push) Blocked by required conditions
CI: Nix Linux Rust source / Nix / compositor build (push) Waiting to run
CI: Nix Linux Rust source / Nix / driver package (push) Waiting to run
CI: Nix Linux Rust source / Nix / Rust unit tests (push) Waiting to run
CI: Rust Linux unit / Rust Linux unit and compile (push) Waiting to run
CI: Rust Windows unit / Rust Windows unit and compile (push) Waiting to run
CI: SPDX Headers / Check SPDX headers (warn-only) (push) Waiting to run
CD: Docs MCP Server / build (linux/amd64) (push) Waiting to run
CD: Docs MCP Server / build (linux/arm64) (push) Waiting to run
CD: Docs MCP Server / merge (push) Blocked by required conditions
chore: import upstream snapshot with attribution
2026-07-13 13:03:19 +08:00

269 lines
8.3 KiB
TypeScript

#!/usr/bin/env npx tsx
/**
* Documentation Generator Runner
*
* Orchestrates documentation generation across all configured libraries.
* Reads config.json and runs appropriate generators based on what changed.
*
* Usage:
* npx tsx scripts/docs-generators/runner.ts # Generate all enabled docs
* npx tsx scripts/docs-generators/runner.ts --check # Check for drift (CI mode)
* npx tsx scripts/docs-generators/runner.ts --library lume # Generate specific library
* npx tsx scripts/docs-generators/runner.ts --list # List all configured generators
* npx tsx scripts/docs-generators/runner.ts --changed # Only run for changed files (CI)
*/
import { execSync, spawnSync } from 'child_process';
import * as fs from 'fs';
import * as path from 'path';
// ============================================================================
// Types
// ============================================================================
interface GeneratorOutput {
type: string;
outputFile: string;
extractCommand: string | null;
}
interface GeneratorConfig {
name: string;
language: string;
sourcePath: string;
docsOutputPath: string;
generatorScript: string;
watchPaths: string[];
buildCommand: string | null;
buildDirectory: string;
extractionMethod: string;
outputs: GeneratorOutput[];
enabled: boolean;
notes?: string;
}
interface Config {
description: string;
generators: Record<string, GeneratorConfig>;
}
// ============================================================================
// Configuration
// ============================================================================
const ROOT_DIR = path.resolve(__dirname, '../..');
const CONFIG_PATH = path.join(__dirname, 'config.json');
// ============================================================================
// Main
// ============================================================================
async function main() {
const args = process.argv.slice(2);
// Parse arguments
const checkOnly = args.includes('--check') || args.includes('--check-only');
const listOnly = args.includes('--list');
const changedOnly = args.includes('--changed');
const libraryIndex = args.indexOf('--library');
const specificLibrary = libraryIndex !== -1 ? args[libraryIndex + 1] : null;
console.log('📚 Documentation Generator Runner');
console.log('==================================\n');
// Load config
if (!fs.existsSync(CONFIG_PATH)) {
console.error(`❌ Config file not found: ${CONFIG_PATH}`);
process.exit(1);
}
const config: Config = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf-8'));
// List mode
if (listOnly) {
listGenerators(config);
return;
}
// Determine which generators to run
let generatorsToRun: string[] = [];
if (specificLibrary) {
if (!config.generators[specificLibrary]) {
console.error(`❌ Unknown library: ${specificLibrary}`);
console.log('\nAvailable libraries:', Object.keys(config.generators).join(', '));
process.exit(1);
}
generatorsToRun = [specificLibrary];
} else if (changedOnly) {
generatorsToRun = getChangedGenerators(config);
if (generatorsToRun.length === 0) {
console.log('✅ No documentation-related changes detected.');
return;
}
} else {
// Run all enabled generators
generatorsToRun = Object.entries(config.generators)
.filter(([_, cfg]) => cfg.enabled)
.map(([key, _]) => key);
}
if (generatorsToRun.length === 0) {
console.log('⚠️ No generators to run. Enable generators in config.json.');
return;
}
console.log(`📋 Generators to run: ${generatorsToRun.join(', ')}\n`);
// Run generators
let hasErrors = false;
for (const generatorKey of generatorsToRun) {
const generator = config.generators[generatorKey];
console.log(`\n${'='.repeat(50)}`);
console.log(`📦 ${generator.name} (${generatorKey})`);
console.log(`${'='.repeat(50)}\n`);
if (!generator.enabled) {
console.log(`⏭️ Skipped (disabled)`);
if (generator.notes) {
console.log(` Note: ${generator.notes}`);
}
continue;
}
const success = await runGenerator(generator, generatorKey, checkOnly);
if (!success) {
hasErrors = true;
}
}
// Summary
console.log(`\n${'='.repeat(50)}`);
if (hasErrors) {
console.error('❌ Some generators failed or detected drift.');
if (checkOnly) {
console.log("\n💡 Run 'npx tsx scripts/docs-generators/runner.ts' to update documentation");
}
process.exit(1);
} else {
console.log('✅ All documentation is up to date!');
}
}
// ============================================================================
// Generator Execution
// ============================================================================
async function runGenerator(
generator: GeneratorConfig,
key: string,
checkOnly: boolean
): Promise<boolean> {
const generatorPath = path.join(ROOT_DIR, generator.generatorScript);
// Check if generator script exists
if (!fs.existsSync(generatorPath)) {
console.log(`⚠️ Generator script not found: ${generator.generatorScript}`);
console.log(` Create this file to enable documentation generation.`);
return true; // Not an error, just not implemented
}
try {
// Run the generator
const args = checkOnly ? ['--check'] : [];
const result = spawnSync('npx', ['tsx', generatorPath, ...args], {
cwd: ROOT_DIR,
stdio: 'inherit',
encoding: 'utf-8',
});
return result.status === 0;
} catch (error) {
console.error(`❌ Error running generator for ${key}:`, error);
return false;
}
}
// ============================================================================
// Changed Files Detection (for CI)
// ============================================================================
function getChangedGenerators(config: Config): string[] {
const changedGenerators: string[] = [];
try {
// Get changed files from git
// This works for both PRs (comparing to base) and pushes
const gitDiff = execSync(
'git diff --name-only HEAD~1 HEAD 2>/dev/null || git diff --name-only HEAD',
{ cwd: ROOT_DIR, encoding: 'utf-8' }
);
const changedFiles = gitDiff.split('\n').filter(Boolean);
console.log(`📝 Changed files: ${changedFiles.length}`);
for (const [key, generator] of Object.entries(config.generators)) {
if (!generator.enabled) continue;
// Check if any watch path matches changed files
const watchPatterns = generator.watchPaths.map(
(p) => new RegExp(p.replace(/\*\*/g, '.*').replace(/\*/g, '[^/]*').replace(/\//g, '\\/'))
);
const hasChanges = changedFiles.some((file) =>
watchPatterns.some((pattern) => pattern.test(file))
);
if (hasChanges) {
changedGenerators.push(key);
console.log(` 📌 ${key}: changes detected`);
}
}
} catch (error) {
console.warn('⚠️ Could not detect changed files, running all generators');
return Object.entries(config.generators)
.filter(([_, cfg]) => cfg.enabled)
.map(([key, _]) => key);
}
return changedGenerators;
}
// ============================================================================
// List Generators
// ============================================================================
function listGenerators(config: Config): void {
console.log('Configured Documentation Generators:\n');
const maxKeyLen = Math.max(...Object.keys(config.generators).map((k) => k.length));
for (const [key, generator] of Object.entries(config.generators)) {
const status = generator.enabled ? '✅' : '⏸️ ';
const paddedKey = key.padEnd(maxKeyLen);
console.log(`${status} ${paddedKey} ${generator.name} (${generator.language})`);
if (!generator.enabled && generator.notes) {
console.log(` ${''.padEnd(maxKeyLen)} └─ ${generator.notes}`);
}
}
console.log('\n');
console.log('Legend:');
console.log(' ✅ = Enabled (will run)');
console.log(' ⏸️ = Disabled (not yet implemented)');
}
// ============================================================================
// Run
// ============================================================================
main().catch((error) => {
console.error('Error:', error);
process.exit(1);
});