158 lines
4.4 KiB
JavaScript
158 lines
4.4 KiB
JavaScript
|
|
// scripts/generate-automation-map.js
|
|||
|
|
// 自动化地图生成器 · Automation Map Generator
|
|||
|
|
//
|
|||
|
|
// 功能:扫描 .github/workflows/ 和自动化脚本,生成 brain/automation-map.json
|
|||
|
|
// 触发方式:
|
|||
|
|
// - GitHub Actions: daily-maintenance.yml
|
|||
|
|
// - 本地:node scripts/generate-automation-map.js
|
|||
|
|
|
|||
|
|
'use strict';
|
|||
|
|
|
|||
|
|
const fs = require('fs');
|
|||
|
|
const path = require('path');
|
|||
|
|
|
|||
|
|
const ROOT = path.join(__dirname, '..');
|
|||
|
|
const BRAIN_DIR = path.join(ROOT, 'brain');
|
|||
|
|
const OUT_PATH = path.join(BRAIN_DIR, 'automation-map.json');
|
|||
|
|
|
|||
|
|
const now = new Date();
|
|||
|
|
const nowISO = now.toISOString();
|
|||
|
|
|
|||
|
|
// ── 工具函数 ──
|
|||
|
|
|
|||
|
|
function safeRead(filePath) {
|
|||
|
|
try { return fs.readFileSync(filePath, 'utf8'); } catch { return ''; }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function listFiles(dirPath, ext) {
|
|||
|
|
try {
|
|||
|
|
return fs.readdirSync(dirPath)
|
|||
|
|
.filter(f => !ext || f.endsWith(ext))
|
|||
|
|
.filter(f => !f.startsWith('.'));
|
|||
|
|
} catch { return []; }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ── 解析单个 workflow ──
|
|||
|
|
|
|||
|
|
function parseWorkflow(file, content) {
|
|||
|
|
const nameMatch = content.match(/^name:\s*(.+)/m);
|
|||
|
|
const name = nameMatch ? nameMatch[1].trim().replace(/^["']|["']$/g, '') : file;
|
|||
|
|
|
|||
|
|
const triggers = [];
|
|||
|
|
if (/^\s{2,4}push:/m.test(content)) triggers.push('push');
|
|||
|
|
if (/^\s{2,4}pull_request:/m.test(content)) triggers.push('pull_request');
|
|||
|
|
if (/^\s{2,4}issues:/m.test(content)) triggers.push('issues');
|
|||
|
|
if (/^\s{2,4}issue_comment:/m.test(content)) triggers.push('issue_comment');
|
|||
|
|
if (/workflow_dispatch/m.test(content)) triggers.push('workflow_dispatch');
|
|||
|
|
if (/workflow_call/m.test(content)) triggers.push('workflow_call');
|
|||
|
|
|
|||
|
|
const cronMatch = content.match(/cron:\s*['"]?([^'"#\n]+)/);
|
|||
|
|
const cron = cronMatch ? cronMatch[1].trim() : null;
|
|||
|
|
if (cron) triggers.push('schedule');
|
|||
|
|
|
|||
|
|
// Detect scripts used
|
|||
|
|
const scriptRefs = [];
|
|||
|
|
const scriptMatches = content.matchAll(/node\s+scripts\/([^\s'"]+)/g);
|
|||
|
|
for (const m of scriptMatches) {
|
|||
|
|
scriptRefs.push('scripts/' + m[1]);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const result = {
|
|||
|
|
file,
|
|||
|
|
name,
|
|||
|
|
triggers: triggers.length ? triggers : ['unknown']
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
if (cron) result.cron = cron;
|
|||
|
|
if (scriptRefs.length) result.scripts = scriptRefs;
|
|||
|
|
|
|||
|
|
return result;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ── 扫描所有 workflow ──
|
|||
|
|
|
|||
|
|
function scanWorkflows() {
|
|||
|
|
const wfDir = path.join(ROOT, '.github/workflows');
|
|||
|
|
const files = listFiles(wfDir, '.yml');
|
|||
|
|
return files.map(f => {
|
|||
|
|
const content = safeRead(path.join(wfDir, f));
|
|||
|
|
return parseWorkflow(f, content);
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ── 提取 cron jobs ──
|
|||
|
|
|
|||
|
|
function extractCronJobs(workflows) {
|
|||
|
|
return workflows
|
|||
|
|
.filter(w => w.cron)
|
|||
|
|
.map(w => ({
|
|||
|
|
cron: w.cron,
|
|||
|
|
workflow: w.file,
|
|||
|
|
name: w.name
|
|||
|
|
}))
|
|||
|
|
.sort((a, b) => a.cron.localeCompare(b.cron));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ── 扫描部署脚本 ──
|
|||
|
|
|
|||
|
|
function scanDeployScripts(workflows) {
|
|||
|
|
return workflows
|
|||
|
|
.filter(w => /deploy/i.test(w.file) || /deploy/i.test(w.name))
|
|||
|
|
.map(w => ({
|
|||
|
|
workflow: w.file,
|
|||
|
|
name: w.name,
|
|||
|
|
triggers: w.triggers
|
|||
|
|
}));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ── 扫描同步脚本 ──
|
|||
|
|
|
|||
|
|
function scanSyncScripts() {
|
|||
|
|
const scriptDir = path.join(ROOT, 'scripts');
|
|||
|
|
const files = listFiles(scriptDir, '.js');
|
|||
|
|
const syncScripts = [];
|
|||
|
|
|
|||
|
|
for (const file of files) {
|
|||
|
|
const content = safeRead(path.join(scriptDir, file));
|
|||
|
|
if (/sync|bridge|push-broadcast|receive|process-broadcast/i.test(file) ||
|
|||
|
|
/notion.*api|@notionhq/i.test(content)) {
|
|||
|
|
syncScripts.push({
|
|||
|
|
script: 'scripts/' + file,
|
|||
|
|
type: /sync/i.test(file) ? 'sync' :
|
|||
|
|
/bridge/i.test(file) ? 'bridge' :
|
|||
|
|
/broadcast/i.test(file) ? 'broadcast' : 'other'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return syncScripts;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ── 主生成 ──
|
|||
|
|
|
|||
|
|
function generate() {
|
|||
|
|
if (!fs.existsSync(BRAIN_DIR)) fs.mkdirSync(BRAIN_DIR, { recursive: true });
|
|||
|
|
|
|||
|
|
const workflows = scanWorkflows();
|
|||
|
|
const cronJobs = extractCronJobs(workflows);
|
|||
|
|
const deployScripts = scanDeployScripts(workflows);
|
|||
|
|
const syncScripts = scanSyncScripts();
|
|||
|
|
|
|||
|
|
const map = {
|
|||
|
|
version: '4.0',
|
|||
|
|
generated_at: nowISO,
|
|||
|
|
generated_by: 'scripts/generate-automation-map.js',
|
|||
|
|
description: '数字地球系统自动化地图 · 所有工作流与自动化脚本',
|
|||
|
|
workflows,
|
|||
|
|
cron_jobs: cronJobs,
|
|||
|
|
deploy_scripts: deployScripts,
|
|||
|
|
data_sync_scripts: syncScripts,
|
|||
|
|
status: 'stable'
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
fs.writeFileSync(OUT_PATH, JSON.stringify(map, null, 2));
|
|||
|
|
console.log(`✅ automation-map.json 已生成 · ${workflows.length} 个工作流 · ${cronJobs.length} 个定时任务`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
generate();
|