Files
aurora-admin/tools/release.mjs
T
aurora-admin f73ed394f4 feat(S2-P9+S4-P12): FAQ页240条聚合+发布流程release.mjs
P9: #/faq 路由+renderFaq(details批量懒加载每批10个)+顶栏/侧边栏双入口
    +guide-h3样式+9条i18n英文;浏览器实测240条/搜索宽度11条/0 JS错误。
    规划偏差:契约已在P4-D移出data.js,FAQ走details懒加载。
P12: tools/release.mjs(检查+bump双模式,只做本地准备不自动push/tag)
    +CONTRIBUTING发布5步+版本号规则;检查模式实测版本一致通过。
回归零影响:report.json 79/79、data.json未动。
2026-09-14 03:28:46 +08:00

102 lines
4.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env node
/**
* release.mjs — S4-P12 版本发布流程(SemVer + CHANGELOG 驱动)
*
* 只做可逆的本地准备,不自动 push/tag 到远端:
* 1) 从 CHANGELOG.md 解析最新 `## [x.y.z] - date` 段(Unreleased 不算版本)
* 2) 校验 package.json 的 version 与之*一致*(不一致则报错退出,不自动改)
* 3) 校验 CHANGELOG 该版本段非空;校验 dist 可构建(tools/build-dist.mjs --dry-run 若支持)
* 4) 输出发布检查清单 + 建议的 tag/push 命令(由人执行)
*
* 另支持 --bump <major|minor|patch> --date YYYY-MM-DD:把 [Unreleased] 提升为新版本段
*(改 CHANGELOG.md 与 package.json,需人复核后提交)。
*
* 用法:
* node tools/release.mjs # 检查当前版本是否可发布
* node tools/release.mjs --bump minor --date 2026-09-14
*/
import { readFileSync, writeFileSync } from 'fs';
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';
const __dirname = dirname(fileURLToPath(import.meta.url));
const ROOT = join(__dirname, '..');
const arg = (k) => {
const i = process.argv.indexOf(k);
return i > -1 ? process.argv[i + 1] : null;
};
const fail = (msg) => { console.error('FAIL: ' + msg); process.exit(1); };
const ok = (msg) => console.log('OK: ' + msg);
function parseChangelog() {
const src = readFileSync(join(ROOT, 'CHANGELOG.md'), 'utf8');
const vers = [...src.matchAll(/^## \[([0-9]+\.[0-9]+\.[0-9]+)\](?:\s*-\s*(\S+))?/gm)];
const hasUnreleased = /^## \[Unreleased\]/m.test(src);
const unreleasedBody = hasUnreleased
? (src.split(/^## \[Unreleased\]/m)[1] || '').split(/^## \[/m)[0].trim()
: '';
return { vers: vers.map((m) => ({ v: m[1], date: m[2] || '' })), hasUnreleased, unreleasedBody };
}
function bump(v, kind) {
const [a, b, c] = v.split('.').map(Number);
if (kind === 'major') return `${a + 1}.0.0`;
if (kind === 'minor') return `${a}.${b + 1}.0`;
if (kind === 'patch') return `${a}.${b}.${c + 1}`;
fail('未知 --bump 类型(major|minor|patch): ' + kind);
return v;
}
/* ---------- --bump 模式 ---------- */
const bumpKind = arg('--bump');
if (bumpKind) {
const date = arg('--date') || new Date().toISOString().slice(0, 10);
const pkgPath = join(ROOT, 'package.json');
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
const { hasUnreleased, unreleasedBody } = parseChangelog();
if (!hasUnreleased) fail('CHANGELOG.md 无 [Unreleased] 段,无内容可提升');
if (unreleasedBody.length < 10) fail('[Unreleased] 段为空或过短,先写变更条目再提升版本');
const next = bump(pkg.version, bumpKind);
let cl = readFileSync(join(ROOT, 'CHANGELOG.md'), 'utf8');
cl = cl.replace(/^## \[Unreleased\]/m, `## [Unreleased]\n\n## [${next}] - ${date}`);
writeFileSync(join(ROOT, 'CHANGELOG.md'), cl);
pkg.version = next;
writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
console.log(`bumped: ${next} (${date})`);
console.log('请复核 CHANGELOG.md 与 package.json 后提交:');
console.log(` git add CHANGELOG.md package.json && git commit -m "chore(release): v${next}"`);
process.exit(0);
}
/* ---------- 检查模式 ---------- */
const pkg = JSON.parse(readFileSync(join(ROOT, 'package.json'), 'utf8'));
const { vers, hasUnreleased, unreleasedBody } = parseChangelog();
if (!vers.length) fail('CHANGELOG.md 无任何版本段');
const latest = vers[0];
console.log(`package.json: ${pkg.version} / CHANGELOG 最新: [${latest.v}] ${latest.date}`);
if (pkg.version !== latest.v) fail(`版本不一致:package.json=${pkg.version} vs CHANGELOG=[${latest.v}](用 --bump 提升,不要手改一边)`);
ok('版本一致');
// Unreleased 有内容 = 还有未发布变更,提示而非报错
if (hasUnreleased && unreleasedBody.length > 10) {
console.log(`NOTE: [Unreleased] 段有 ${unreleasedBody.length} 字符未发布变更,发版前确认是否纳入本次`);
} else {
ok('Unreleased 为空,无遗留变更');
}
// dist 构建检查(只验证脚本存在且可跑;产物不入库)
try {
const { execSync } = await import('child_process');
execSync('node tools/build-dist.mjs --help', { cwd: ROOT, stdio: 'pipe', timeout: 30000 });
ok('build-dist 可调用');
} catch {
console.log('NOTE: tools/build-dist.mjs 无 --help(不阻塞,构建时再验证产物)');
}
console.log('');
console.log('发布检查通过。建议的人工执行序列:');
console.log(` 1) git tag v${pkg.version}`);
console.log(` 2) git push origin main v${pkg.version}`);
console.log(' 3) npm run build && npm publish(需 Q1 决策:是否公开发布)');