278 lines
8.1 KiB
JavaScript
Executable File
278 lines
8.1 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
|
|
/**
|
|
* Apply CLI documentation patches generated by audit-cli-documentation.js
|
|
* Usage: node apply-cli-patches.js [core|enterprise|both] [--dry-run]
|
|
*/
|
|
|
|
import { promises as fs } from 'fs';
|
|
import { join, dirname } from 'path';
|
|
import { fileURLToPath } from 'url';
|
|
import { process } from 'node:process';
|
|
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = dirname(__filename);
|
|
|
|
// Color codes
|
|
const Colors = {
|
|
RED: '\x1b[0;31m',
|
|
GREEN: '\x1b[0;32m',
|
|
YELLOW: '\x1b[1;33m',
|
|
BLUE: '\x1b[0;34m',
|
|
NC: '\x1b[0m', // No Color
|
|
};
|
|
|
|
async function fileExists(path) {
|
|
try {
|
|
await fs.access(path);
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
async function ensureDir(dir) {
|
|
await fs.mkdir(dir, { recursive: true });
|
|
}
|
|
|
|
async function extractFrontmatter(content) {
|
|
const lines = content.split('\n');
|
|
if (lines[0] !== '---') return { frontmatter: null, content };
|
|
|
|
const frontmatterLines = [];
|
|
let i = 1;
|
|
while (i < lines.length && lines[i] !== '---') {
|
|
frontmatterLines.push(lines[i]);
|
|
i++;
|
|
}
|
|
|
|
if (i >= lines.length) return { frontmatter: null, content };
|
|
|
|
const frontmatterText = frontmatterLines.join('\n');
|
|
const remainingContent = lines.slice(i + 1).join('\n');
|
|
|
|
return { frontmatter: frontmatterText, content: remainingContent };
|
|
}
|
|
|
|
async function getActualDocumentationPath(docPath, projectRoot) {
|
|
// Check if the documentation file exists and has a source field
|
|
const fullPath = join(projectRoot, docPath);
|
|
|
|
if (await fileExists(fullPath)) {
|
|
const content = await fs.readFile(fullPath, 'utf8');
|
|
const { frontmatter } = await extractFrontmatter(content);
|
|
|
|
if (frontmatter) {
|
|
// Look for source: field in frontmatter
|
|
const sourceMatch = frontmatter.match(/^source:\s*(.+)$/m);
|
|
if (sourceMatch) {
|
|
const sourcePath = sourceMatch[1].trim();
|
|
return sourcePath;
|
|
}
|
|
}
|
|
}
|
|
|
|
return docPath;
|
|
}
|
|
|
|
async function applyPatches(product, dryRun = false) {
|
|
const patchDir = join(
|
|
dirname(__dirname),
|
|
'output',
|
|
'cli-audit',
|
|
'patches',
|
|
product
|
|
);
|
|
const projectRoot = join(__dirname, '..', '..');
|
|
|
|
console.log(
|
|
`${Colors.BLUE}📋 Applying CLI documentation patches for ${product}${Colors.NC}`
|
|
);
|
|
if (dryRun) {
|
|
console.log(
|
|
`${Colors.YELLOW}🔍 DRY RUN - No files will be created${Colors.NC}`
|
|
);
|
|
}
|
|
console.log();
|
|
|
|
// Check if patch directory exists
|
|
if (!(await fileExists(patchDir))) {
|
|
console.log(`${Colors.YELLOW}No patches found for ${product}.${Colors.NC}`);
|
|
console.log("Run 'yarn audit:cli' first to generate patches.");
|
|
return;
|
|
}
|
|
|
|
// Read all patch files
|
|
const patchFiles = await fs.readdir(patchDir);
|
|
const mdFiles = patchFiles.filter((f) => f.endsWith('.md'));
|
|
|
|
if (mdFiles.length === 0) {
|
|
console.log(
|
|
`${Colors.YELLOW}No patch files found in ${patchDir}${Colors.NC}`
|
|
);
|
|
return;
|
|
}
|
|
|
|
console.log(`Found ${mdFiles.length} patch file(s) to apply:\n`);
|
|
|
|
// Map patch files to their destination
|
|
const baseCliPath = `content/influxdb3/${product}/reference/cli/influxdb3`;
|
|
const commandToFile = {
|
|
'create-database.md': `${baseCliPath}/create/database.md`,
|
|
'create-token.md': `${baseCliPath}/create/token/_index.md`,
|
|
'create-token-admin.md': `${baseCliPath}/create/token/admin.md`,
|
|
'create-trigger.md': `${baseCliPath}/create/trigger.md`,
|
|
'create-table.md': `${baseCliPath}/create/table.md`,
|
|
'create-last_cache.md': `${baseCliPath}/create/last_cache.md`,
|
|
'create-distinct_cache.md': `${baseCliPath}/create/distinct_cache.md`,
|
|
'show-databases.md': `${baseCliPath}/show/databases.md`,
|
|
'show-tokens.md': `${baseCliPath}/show/tokens.md`,
|
|
'delete-database.md': `${baseCliPath}/delete/database.md`,
|
|
'delete-table.md': `${baseCliPath}/delete/table.md`,
|
|
'query.md': `${baseCliPath}/query.md`,
|
|
'write.md': `${baseCliPath}/write.md`,
|
|
};
|
|
|
|
let applied = 0;
|
|
let skipped = 0;
|
|
|
|
for (const patchFile of mdFiles) {
|
|
const destinationPath = commandToFile[patchFile];
|
|
|
|
if (!destinationPath) {
|
|
console.log(
|
|
`${Colors.YELLOW}⚠️ Unknown patch file: ${patchFile}${Colors.NC}`
|
|
);
|
|
continue;
|
|
}
|
|
|
|
// Get the actual documentation path (handles source: frontmatter)
|
|
const actualPath = await getActualDocumentationPath(
|
|
destinationPath,
|
|
projectRoot
|
|
);
|
|
const fullDestPath = join(projectRoot, actualPath);
|
|
const patchPath = join(patchDir, patchFile);
|
|
|
|
// Check if destination already exists
|
|
if (await fileExists(fullDestPath)) {
|
|
console.log(
|
|
`${Colors.YELLOW}⏭️ Skipping${Colors.NC} ${patchFile} - destination already exists:`
|
|
);
|
|
console.log(` ${actualPath}`);
|
|
skipped++;
|
|
continue;
|
|
}
|
|
|
|
if (dryRun) {
|
|
console.log(`${Colors.BLUE}🔍 Would create${Colors.NC} ${actualPath}`);
|
|
console.log(` from patch: ${patchFile}`);
|
|
if (actualPath !== destinationPath) {
|
|
console.log(` (resolved from: ${destinationPath})`);
|
|
}
|
|
applied++;
|
|
} else {
|
|
try {
|
|
// Ensure destination directory exists
|
|
await ensureDir(dirname(fullDestPath));
|
|
|
|
// Copy patch to destination
|
|
const content = await fs.readFile(patchPath, 'utf8');
|
|
|
|
// Update the menu configuration based on product
|
|
let updatedContent = content;
|
|
if (product === 'enterprise') {
|
|
updatedContent = content
|
|
.replace('influxdb3/core/tags:', 'influxdb3/enterprise/tags:')
|
|
.replace(
|
|
'influxdb3_core_reference:',
|
|
'influxdb3_enterprise_reference:'
|
|
);
|
|
}
|
|
|
|
await fs.writeFile(fullDestPath, updatedContent);
|
|
|
|
console.log(`${Colors.GREEN}✅ Created${Colors.NC} ${actualPath}`);
|
|
console.log(` from patch: ${patchFile}`);
|
|
if (actualPath !== destinationPath) {
|
|
console.log(` (resolved from: ${destinationPath})`);
|
|
}
|
|
applied++;
|
|
} catch (error) {
|
|
console.log(
|
|
`${Colors.RED}❌ Error${Colors.NC} creating ${actualPath}:`
|
|
);
|
|
console.log(` ${error.message}`);
|
|
}
|
|
}
|
|
}
|
|
|
|
console.log();
|
|
console.log(`${Colors.BLUE}Summary:${Colors.NC}`);
|
|
console.log(`- Patches ${dryRun ? 'would be' : ''} applied: ${applied}`);
|
|
console.log(`- Files skipped (already exist): ${skipped}`);
|
|
console.log(`- Total patch files: ${mdFiles.length}`);
|
|
|
|
if (!dryRun && applied > 0) {
|
|
console.log();
|
|
console.log(
|
|
`${Colors.GREEN}✨ Success!${Colors.NC} Created ${applied} new ` +
|
|
'documentation file(s).'
|
|
);
|
|
console.log();
|
|
console.log('Next steps:');
|
|
console.log('1. Review the generated files and customize the content');
|
|
console.log('2. Add proper examples with placeholders');
|
|
console.log('3. Update descriptions and add any missing options');
|
|
console.log('4. Run tests: yarn test:links');
|
|
}
|
|
}
|
|
|
|
async function main() {
|
|
const args = process.argv.slice(2);
|
|
const product =
|
|
args.find((arg) => ['core', 'enterprise', 'both'].includes(arg)) || 'both';
|
|
const dryRun = args.includes('--dry-run');
|
|
|
|
if (args.includes('--help') || args.includes('-h')) {
|
|
console.log(
|
|
'Usage: node apply-cli-patches.js [core|enterprise|both] [--dry-run]'
|
|
);
|
|
console.log();
|
|
console.log('Options:');
|
|
console.log(
|
|
' --dry-run Show what would be done without creating files'
|
|
);
|
|
console.log();
|
|
console.log('Examples:');
|
|
console.log(
|
|
' node apply-cli-patches.js # Apply patches for both products'
|
|
);
|
|
console.log(
|
|
' node apply-cli-patches.js core --dry-run # Preview core patches'
|
|
);
|
|
console.log(
|
|
' node apply-cli-patches.js enterprise # Apply enterprise patches'
|
|
);
|
|
process.exit(0);
|
|
}
|
|
|
|
try {
|
|
if (product === 'both') {
|
|
await applyPatches('core', dryRun);
|
|
console.log();
|
|
await applyPatches('enterprise', dryRun);
|
|
} else {
|
|
await applyPatches(product, dryRun);
|
|
}
|
|
} catch (error) {
|
|
console.error(`${Colors.RED}Error:${Colors.NC}`, error.message);
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
// Run if called directly
|
|
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
main();
|
|
}
|