- VERSION file as authoritative version source - export.mjs reads VERSION with package.json fallback - bw-ota.ps1 DryRun mode for safe testing - auto-setup.ps1 bumped to v3.2.0 (Phase 8 OTA)
93 lines
4.3 KiB
JavaScript
93 lines
4.3 KiB
JavaScript
#!/usr/bin/env node
|
||
/**
|
||
* Patch C1: stop-dispatcher Batch3 预算控制
|
||
* Batch3 串行 5 阶段累计最高 14800ms,远超 Stop hook 5000ms 预算。
|
||
* 引入 deadline = _perf_start + BUDGET_MS,超时后跳过剩余阶段。
|
||
* 每阶段超时自动截断为 min(原预算, deadline - now)。
|
||
* 幂等: sentinel C1_BATCH3_BUDGET_v1
|
||
*/
|
||
const fs = require('fs');
|
||
const path = require('path');
|
||
|
||
const TARGET = path.join(__dirname, '..', '..', 'hooks', 'stop-dispatcher.js');
|
||
const SENTINEL = 'C1_BATCH3_BUDGET_v1';
|
||
|
||
function main() {
|
||
const src = fs.readFileSync(TARGET, 'utf8');
|
||
if (src.includes(SENTINEL)) { console.error('[patch-c1] already applied'); process.exit(0); }
|
||
|
||
// 查找 Batch3 头部锚点
|
||
const anchor = " // ─── Batch 3 · 尾部串行 (明确依赖顺序) ───\n";
|
||
if (!src.includes(anchor)) { console.error('[patch-c1] batch3 header anchor missing'); process.exit(3); }
|
||
|
||
// 原 Batch3 5 个阶段 (consistency-sentinel/auto-cleanup/log-rotator/auto-backup/auto-git-push)
|
||
const oldBlock =
|
||
" // ─── Batch 3 · 尾部串行 (明确依赖顺序) ───\n" +
|
||
" // sentinel append evolution-log → cleanup 才能 truncate\n" +
|
||
" // auto-backup → auto-git-push (快照先于远端推送)\n" +
|
||
" _stageRecords.push(await race('consistency-sentinel', () => runConsistencySentinel(), 1000));\n" +
|
||
" _stageRecords.push(await race('auto-cleanup', () => {\n" +
|
||
" const ac = require('../scripts/auto-cleanup.js');\n" +
|
||
" if (ac.main) ac.main({ execute: true, ifStale: 86400 });\n" +
|
||
" }, 5000));\n" +
|
||
" _stageRecords.push(await race('log-rotator', () => {\n" +
|
||
" const lr = require('./log-rotator.js');\n" +
|
||
" if (lr.runRotation) lr.runRotation();\n" +
|
||
" }, 800));\n" +
|
||
" _stageRecords.push(await race('auto-backup', () => {\n" +
|
||
" const backup = require('../scripts/auto-backup.js');\n" +
|
||
" backup();\n" +
|
||
" }, 3000));\n" +
|
||
" _stageRecords.push(await race('auto-git-push', () => {\n" +
|
||
" const sync = require('../scripts/auto-git-sync.js');\n" +
|
||
" sync.pushChanges();\n" +
|
||
" }, 5000));\n";
|
||
|
||
if (!src.includes(oldBlock)) { console.error('[patch-c1] batch3 body anchor missing'); process.exit(4); }
|
||
|
||
const newBlock =
|
||
" // ─── Batch 3 · 尾部串行 (明确依赖顺序) ───\n" +
|
||
" // " + SENTINEL + ": 总预算硬截断,防止 Stop hook 整体超过 5000ms 宿主 kill\n" +
|
||
" // sentinel append evolution-log → cleanup 才能 truncate\n" +
|
||
" // auto-backup → auto-git-push (快照先于远端推送)\n" +
|
||
" const _BUDGET_MS = 4200;\n" +
|
||
" const _deadline = _perf_start + _BUDGET_MS;\n" +
|
||
" const _budgetRace = async (name, fn, origMs) => {\n" +
|
||
" const remaining = _deadline - Date.now();\n" +
|
||
" if (remaining <= 100) {\n" +
|
||
" _stageRecords.push({ name, ok: false, ms: 0, skipped: true, reason: 'budget-exhausted' });\n" +
|
||
" return;\n" +
|
||
" }\n" +
|
||
" _stageRecords.push(await race(name, fn, Math.min(origMs, remaining)));\n" +
|
||
" };\n" +
|
||
" await _budgetRace('consistency-sentinel', () => runConsistencySentinel(), 1000);\n" +
|
||
" await _budgetRace('auto-cleanup', () => {\n" +
|
||
" const ac = require('../scripts/auto-cleanup.js');\n" +
|
||
" if (ac.main) ac.main({ execute: true, ifStale: 86400 });\n" +
|
||
" }, 5000);\n" +
|
||
" await _budgetRace('log-rotator', () => {\n" +
|
||
" const lr = require('./log-rotator.js');\n" +
|
||
" if (lr.runRotation) lr.runRotation();\n" +
|
||
" }, 800);\n" +
|
||
" await _budgetRace('auto-backup', () => {\n" +
|
||
" const backup = require('../scripts/auto-backup.js');\n" +
|
||
" backup();\n" +
|
||
" }, 3000);\n" +
|
||
" await _budgetRace('auto-git-push', () => {\n" +
|
||
" const sync = require('../scripts/auto-git-sync.js');\n" +
|
||
" sync.pushChanges();\n" +
|
||
" }, 5000);\n";
|
||
|
||
const patched = src.replace(oldBlock, newBlock);
|
||
if (patched === src) { console.error('[patch-c1] no change'); process.exit(5); }
|
||
|
||
const bakDir = path.join(path.dirname(TARGET), 'archive');
|
||
if (!fs.existsSync(bakDir)) fs.mkdirSync(bakDir, { recursive: true });
|
||
fs.writeFileSync(path.join(bakDir, 'stop-dispatcher.js.bak.c1.' + Date.now()), src);
|
||
const tmp = TARGET + '.tmp.' + process.pid;
|
||
fs.writeFileSync(tmp, patched);
|
||
fs.renameSync(tmp, TARGET);
|
||
console.error('[patch-c1] applied');
|
||
}
|
||
main();
|