204 lines
6.1 KiB
JavaScript
204 lines
6.1 KiB
JavaScript
#!/usr/bin/env node
|
||
/**
|
||
* skyeye/hibernation/distributor.js
|
||
* ═══════════════════════════════════════════════
|
||
* 🔺 Sovereign: TCS-0002∞ | Root: SYS-GLW-0001
|
||
* 📜 Copyright: 国作登字-2026-A-00037559
|
||
* ═══════════════════════════════════════════════
|
||
*
|
||
* 📦 分布式升级传播引擎
|
||
* 读取升级包中的 soldier_upgrade_manifest,
|
||
* 通过 repository_dispatch 向子仓库分发升级指令。
|
||
*
|
||
* 用法:
|
||
* node distributor.js [--pack=path/to/upgrade-pack.json]
|
||
*
|
||
* 环境变量:
|
||
* GITHUB_TOKEN - 用于 API 调用
|
||
*/
|
||
|
||
'use strict';
|
||
|
||
const fs = require('fs');
|
||
const path = require('path');
|
||
const https = require('https');
|
||
|
||
const UPGRADE_DIR = path.join(__dirname, 'upgrade-packs');
|
||
const DIST_DIR = path.join(__dirname, 'distribution-reports');
|
||
|
||
// ━━━ 工具函数 ━━━
|
||
|
||
function getTimestamp() {
|
||
return new Date().toISOString();
|
||
}
|
||
|
||
function getDateStr() {
|
||
return new Date().toISOString().slice(0, 10).replace(/-/g, '');
|
||
}
|
||
|
||
function loadJSON(filePath) {
|
||
try {
|
||
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
||
} catch (e) {
|
||
return null;
|
||
}
|
||
}
|
||
|
||
function saveJSON(filePath, data) {
|
||
const dir = path.dirname(filePath);
|
||
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
||
fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + '\n', 'utf8');
|
||
}
|
||
|
||
// ━━━ GitHub API dispatch ━━━
|
||
|
||
function dispatchUpgrade(owner, repo, manifest) {
|
||
return new Promise((resolve, reject) => {
|
||
const token = process.env.GITHUB_TOKEN || process.env.HUB_TOKEN;
|
||
if (!token) {
|
||
console.log(` ⚠️ 无 GITHUB_TOKEN,跳过 dispatch 到 ${owner}/${repo}`);
|
||
resolve({ target: `${owner}/${repo}`, status: 'skipped', reason: 'no_token' });
|
||
return;
|
||
}
|
||
|
||
const payload = JSON.stringify({
|
||
event_type: 'skyeye-upgrade',
|
||
client_payload: {
|
||
template_version: manifest.template_version,
|
||
changes: manifest.changes,
|
||
issued_by: 'skyeye-weekly-hibernation',
|
||
issued_at: getTimestamp()
|
||
}
|
||
});
|
||
|
||
const options = {
|
||
hostname: 'api.github.com',
|
||
port: 443,
|
||
path: `/repos/${owner}/${repo}/dispatches`,
|
||
method: 'POST',
|
||
headers: {
|
||
'Authorization': `token ${token}`,
|
||
'Accept': 'application/vnd.github.v3+json',
|
||
'Content-Type': 'application/json',
|
||
'Content-Length': Buffer.byteLength(payload),
|
||
'User-Agent': 'skyeye-distributor'
|
||
}
|
||
};
|
||
|
||
const req = https.request(options, (res) => {
|
||
let body = '';
|
||
res.on('data', (chunk) => body += chunk);
|
||
res.on('end', () => {
|
||
if (res.statusCode === 204 || res.statusCode === 200) {
|
||
resolve({
|
||
target: `${owner}/${repo}`,
|
||
status: 'dispatched',
|
||
dispatched_at: getTimestamp()
|
||
});
|
||
} else {
|
||
resolve({
|
||
target: `${owner}/${repo}`,
|
||
status: 'failed',
|
||
http_status: res.statusCode,
|
||
error: body
|
||
});
|
||
}
|
||
});
|
||
});
|
||
|
||
req.on('error', (e) => {
|
||
resolve({
|
||
target: `${owner}/${repo}`,
|
||
status: 'failed',
|
||
error: e.message
|
||
});
|
||
});
|
||
|
||
req.write(payload);
|
||
req.end();
|
||
});
|
||
}
|
||
|
||
// ━━━ 主流程 ━━━
|
||
|
||
async function distributeUpgrades(manifest) {
|
||
const results = [];
|
||
const owner = 'qinfendebingshuo';
|
||
|
||
for (const target of manifest.targets) {
|
||
console.log(` 📡 分发升级到: ${owner}/${target}`);
|
||
const result = await dispatchUpgrade(owner, target, manifest);
|
||
results.push(result);
|
||
console.log(` → ${result.status}`);
|
||
}
|
||
|
||
return {
|
||
total: manifest.targets.length,
|
||
success: results.filter(r => r.status === 'dispatched').length,
|
||
failed: results.filter(r => r.status === 'failed').length,
|
||
skipped: results.filter(r => r.status === 'skipped').length,
|
||
details: results
|
||
};
|
||
}
|
||
|
||
async function run() {
|
||
console.log('═══════════════════════════════════════════════');
|
||
console.log('📦 分布式升级传播引擎');
|
||
console.log('═══════════════════════════════════════════════');
|
||
|
||
const args = process.argv.slice(2);
|
||
const packArg = args.find(a => a.startsWith('--pack='));
|
||
|
||
// 查找升级包
|
||
let upgradePack = null;
|
||
if (packArg) {
|
||
const packPath = packArg.split('=')[1];
|
||
upgradePack = loadJSON(packPath);
|
||
} else {
|
||
// 查找最新升级包
|
||
if (fs.existsSync(UPGRADE_DIR)) {
|
||
const packFiles = fs.readdirSync(UPGRADE_DIR)
|
||
.filter(f => f.startsWith('upgrade-pack-') && f.endsWith('.json'))
|
||
.sort();
|
||
if (packFiles.length > 0) {
|
||
upgradePack = loadJSON(path.join(UPGRADE_DIR, packFiles[packFiles.length - 1]));
|
||
}
|
||
}
|
||
}
|
||
|
||
if (!upgradePack || !upgradePack.soldier_upgrade_manifest) {
|
||
console.log('⚠️ 未找到升级包或无 soldier_upgrade_manifest,跳过分发');
|
||
return { status: 'skipped', reason: 'no_manifest' };
|
||
}
|
||
|
||
const manifest = upgradePack.soldier_upgrade_manifest;
|
||
console.log(`📋 升级模板版本: ${manifest.template_version}`);
|
||
console.log(`🎯 目标子仓库: ${manifest.targets.length} 个`);
|
||
console.log('');
|
||
|
||
const result = await distributeUpgrades(manifest);
|
||
|
||
// 写入分发报告
|
||
const report = {
|
||
report_id: `DIST-REPORT-${getDateStr()}`,
|
||
upgrade_pack_id: upgradePack.upgrade_pack_id,
|
||
timestamp: getTimestamp(),
|
||
template_version: manifest.template_version,
|
||
distribution: result
|
||
};
|
||
|
||
const reportPath = path.join(DIST_DIR, `dist-report-${getDateStr()}.json`);
|
||
saveJSON(reportPath, report);
|
||
console.log('');
|
||
console.log(`📋 分发报告: ${reportPath}`);
|
||
console.log(`✅ 成功: ${result.success} / 失败: ${result.failed} / 跳过: ${result.skipped}`);
|
||
|
||
return report;
|
||
}
|
||
|
||
module.exports = { distributeUpgrades, run };
|
||
|
||
if (require.main === module) {
|
||
run().catch(console.error);
|
||
}
|