guanghulab/scripts/skyeye/asop-verifier.js

208 lines
6.5 KiB
JavaScript
Raw Normal View History

// scripts/skyeye/asop-verifier.js
// 天眼·ASOP 优化效果验证器
//
// 天眼运行时检查 data/asop-requests/executed/ 下已执行的优化
// 验证效果:
// ① 优化后相关 Workflow 是否正常运行?
// ② 是否引入了新的问题?
// ③ 连续 2 次失败 → 自动回滚
//
// 验证通过 → 移到 verified/ 归档
// 验证失败 → 回滚 + 记录
'use strict';
const fs = require('fs');
const path = require('path');
const ROOT = path.resolve(__dirname, '../..');
const ASOP_DIR = path.join(ROOT, 'data/asop-requests');
const EXECUTED_DIR = path.join(ASOP_DIR, 'executed');
const VERIFIED_DIR = path.join(ASOP_DIR, 'verified');
const SNAPSHOTS_DIR = path.join(ASOP_DIR, 'snapshots');
const SKYEYE_DIR = '/tmp/skyeye';
const BEIJING_OFFSET_MS = 8 * 3600 * 1000;
const CONSECUTIVE_FAILURE_THRESHOLD = 2;
// ━━━ 安全读取 JSON ━━━
function readJSON(filePath) {
try {
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
} catch (e) {
return null;
}
}
// ━━━ 检查受影响 Workflow 的最近运行状态 ━━━
function checkWorkflowHealth(affectedWorkflows) {
// 从天眼扫描结果中获取 Workflow 运行状态
const recentRunsPath = path.join(SKYEYE_DIR, 'recent-runs.json');
if (!fs.existsSync(recentRunsPath)) {
return { healthy: true, reason: '无最近运行数据可验证,默认通过' };
}
const recentRuns = readJSON(recentRunsPath) || [];
const failures = [];
for (const wf of affectedWorkflows) {
const wfRuns = recentRuns.filter(r => {
const name = r.name || '';
return name.includes(wf.replace('.yml', '').replace('.yaml', ''));
});
const recentFailures = wfRuns.filter(r => r.conclusion === 'failure');
if (recentFailures.length >= CONSECUTIVE_FAILURE_THRESHOLD) {
failures.push({
workflow: wf,
consecutive_failures: recentFailures.length
});
}
}
if (failures.length > 0) {
return {
healthy: false,
reason: `连续失败检测:${failures.map(f => `${f.workflow}(${f.consecutive_failures}次)`).join(', ')}`,
failures
};
}
return { healthy: true, reason: '所有受影响 Workflow 运行正常' };
}
// ━━━ 回滚优化 ━━━
function rollbackOptimization(request) {
const requestId = request.request_id;
const snapshotDir = path.join(SNAPSHOTS_DIR, requestId);
if (!fs.existsSync(snapshotDir)) {
console.log(`⚠️ ${requestId} 快照不存在,无法回滚`);
return false;
}
const snapshotFiles = fs.readdirSync(snapshotDir).filter(f => f !== '.gitkeep');
let restored = 0;
for (const file of snapshotFiles) {
const originalPath = file.replace(/__/g, '/');
const srcPath = path.join(snapshotDir, file);
const destPath = path.join(ROOT, originalPath);
try {
const destDir = path.dirname(destPath);
fs.mkdirSync(destDir, { recursive: true });
fs.copyFileSync(srcPath, destPath);
restored++;
} catch (e) {
console.error(`⚠️ 回滚 ${originalPath} 失败: ${e.message}`);
}
}
console.log(`🔄 ${requestId} 已回滚 ${restored}/${snapshotFiles.length} 个文件`);
return restored > 0;
}
// ━━━ 验证单个优化 ━━━
function verifyOptimization(request) {
const requestId = request.request_id;
const affectedWorkflows = (request.impact_assessment && request.impact_assessment.affected_workflows) || [];
console.log(`\n🔍 验证: ${requestId}`);
console.log(` 方案: ${request.proposed_optimization}`);
// 检查受影响 Workflow 的健康度
const health = checkWorkflowHealth(affectedWorkflows);
if (!health.healthy) {
console.log(` ❌ 验证失败: ${health.reason}`);
// 异常熔断:回滚
const rolled = rollbackOptimization(request);
return {
verified: false,
reason: health.reason,
rolled_back: rolled,
verified_at: new Date(Date.now() + BEIJING_OFFSET_MS).toISOString().replace('T', ' ').slice(0, 19) + '+08:00'
};
}
console.log(` ✅ 验证通过: ${health.reason}`);
return {
verified: true,
reason: health.reason,
rolled_back: false,
verified_at: new Date(Date.now() + BEIJING_OFFSET_MS).toISOString().replace('T', ' ').slice(0, 19) + '+08:00'
};
}
// ━━━ 主验证流程 ━━━
function verifyAll() {
console.log('🔍 天眼·ASOP 验证器启动');
console.log('═══════════════════════════════════════════\n');
if (!fs.existsSync(EXECUTED_DIR)) {
console.log(' executed/ 目录不存在,无待验证优化');
const result = { total: 0, verified: 0, rolled_back: 0 };
console.log(JSON.stringify(result, null, 2));
return result;
}
const files = fs.readdirSync(EXECUTED_DIR)
.filter(f => f.endsWith('.json') && f !== '.gitkeep');
if (files.length === 0) {
console.log('🔍 ASOP无待验证的已执行优化');
const result = { total: 0, verified: 0, rolled_back: 0 };
console.log(JSON.stringify(result, null, 2));
return result;
}
console.log(`🔍 ASOP发现 ${files.length} 条待验证的已执行优化\n`);
const summary = { total: files.length, verified: 0, rolled_back: 0, results: [] };
for (const file of files) {
const filePath = path.join(EXECUTED_DIR, file);
const req = readJSON(filePath);
if (!req) {
console.log(`⚠️ 无法解析 ${file},跳过`);
continue;
}
const result = verifyOptimization(req);
req.verification_result = result;
if (result.verified) {
// 验证通过 → 移到 verified/ 归档
fs.mkdirSync(VERIFIED_DIR, { recursive: true });
fs.writeFileSync(path.join(VERIFIED_DIR, file), JSON.stringify(req, null, 2) + '\n');
fs.unlinkSync(filePath);
summary.verified++;
} else {
// 验证失败 + 已回滚 → 标记在 executed/(保留记录)
fs.writeFileSync(filePath, JSON.stringify(req, null, 2) + '\n');
summary.rolled_back++;
}
summary.results.push({
request_id: req.request_id,
verified: result.verified,
rolled_back: result.rolled_back,
reason: result.reason
});
}
console.log(`\n📊 验证结果:通过 ${summary.verified} / 回滚 ${summary.rolled_back}`);
console.log(JSON.stringify(summary, null, 2));
return summary;
}
// ━━━ 导出 ━━━
module.exports = { verifyAll, verifyOptimization, rollbackOptimization };
// ━━━ 直接运行 ━━━
if (require.main === module) {
verifyAll();
}