## 构建、打包与分发系统重构 (build/) - 构建系统收敛至 build/ 目录:build.mjs + electron-builder.js + 6 个脚本 - electron-builder 配置从 package.json 提取为 build/electron-builder.js - 新增 --edition (personal/enterprise) 和 --channel (stable/beta/alpha) 参数 - OSS 路径按渠道隔离 - 新增 pre-build-check.mjs:Git/CHANGELOG/版本/环境变量校验 - 新增 bump-version.mjs:版本号管理 + CHANGELOG 自动生成 - updater.ts 发送 channel/edition/deviceFingerprint - 新增 src/shared/utils/rollout.ts 灰度测试工具 - 新增 10 条 NPM 脚本 ## VCHSM 五层架构迁移 - 7 个业务模块 + ~500 处导入路径同步 ## 修复 - MediaCardContextMenu 改用共享 ContextMenu 组件 - ComboBox 尺寸参数显示统一 (size-format.ts) ## 文档 - UpdateA.md / build/README.md / README.md / .env.example 更新 Co-Authored-By: Claude <noreply@anthropic.com>
154 lines
5.0 KiB
JavaScript
154 lines
5.0 KiB
JavaScript
// ============================================================
|
||
// bump-version.mjs — 版本号管理与 CHANGELOG 模板生成
|
||
//
|
||
// 用法:
|
||
// node build/scripts/bump-version.mjs <major|minor|patch> [--no-changelog]
|
||
// node build/scripts/bump-version.mjs set <x.y.z>
|
||
// node build/scripts/bump-version.mjs pre <beta|alpha>
|
||
//
|
||
// 示例:
|
||
// node build/scripts/bump-version.mjs patch # 0.0.28 → 0.0.29
|
||
// node build/scripts/bump-version.mjs minor # 0.0.28 → 0.1.0
|
||
// node build/scripts/bump-version.mjs set 1.0.0 # 直接设置版本
|
||
// node build/scripts/bump-version.mjs pre beta # 0.0.29 → 0.0.29-beta.1
|
||
// ============================================================
|
||
|
||
import { readFileSync, writeFileSync } from 'node:fs';
|
||
import { join, dirname } from 'node:path';
|
||
import { fileURLToPath } from 'node:url';
|
||
|
||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||
const ROOT = join(__dirname, '..', '..');
|
||
|
||
const args = process.argv.slice(2);
|
||
const command = args[0];
|
||
const arg1 = args[1];
|
||
const noChangelog = args.includes('--no-changelog');
|
||
|
||
if (!command) {
|
||
console.error('用法: node build/scripts/bump-version.mjs <major|minor|patch|set|pre> [value]');
|
||
console.error('示例: node build/scripts/bump-version.mjs patch');
|
||
console.error(' node build/scripts/bump-version.mjs set 1.0.0');
|
||
console.error(' node build/scripts/bump-version.mjs pre beta');
|
||
process.exit(1);
|
||
}
|
||
|
||
const pkgPath = join(ROOT, 'package.json');
|
||
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
||
const oldVersion = pkg.version;
|
||
|
||
// ---------- 版本计算 ----------
|
||
|
||
function parseSemver(v) {
|
||
const match = v.match(/^(\d+)\.(\d+)\.(\d+)(?:-(.+))?$/);
|
||
if (!match) throw new Error(`无效的版本号格式: ${v}`);
|
||
return {
|
||
major: parseInt(match[1]),
|
||
minor: parseInt(match[2]),
|
||
patch: parseInt(match[3]),
|
||
pre: match[4] || null,
|
||
};
|
||
}
|
||
|
||
let newVersion;
|
||
|
||
switch (command) {
|
||
case 'major':
|
||
case 'minor':
|
||
case 'patch': {
|
||
const sv = parseSemver(oldVersion);
|
||
if (command === 'major') {
|
||
sv.major++;
|
||
sv.minor = 0;
|
||
sv.patch = 0;
|
||
} else if (command === 'minor') {
|
||
sv.minor++;
|
||
sv.patch = 0;
|
||
} else {
|
||
sv.patch++;
|
||
}
|
||
sv.pre = null;
|
||
newVersion = `${sv.major}.${sv.minor}.${sv.patch}`;
|
||
break;
|
||
}
|
||
|
||
case 'set': {
|
||
if (!arg1) {
|
||
console.error('用法: node build/scripts/bump-version.mjs set <x.y.z>');
|
||
process.exit(1);
|
||
}
|
||
// 基本格式校验
|
||
if (!/^\d+\.\d+\.\d+(-.+)?$/.test(arg1)) {
|
||
console.error(`无效的版本号: ${arg1}`);
|
||
process.exit(1);
|
||
}
|
||
newVersion = arg1;
|
||
break;
|
||
}
|
||
|
||
case 'pre': {
|
||
if (!arg1 || !['beta', 'alpha'].includes(arg1)) {
|
||
console.error('用法: node build/scripts/bump-version.mjs pre <beta|alpha>');
|
||
process.exit(1);
|
||
}
|
||
const sv = parseSemver(oldVersion);
|
||
|
||
// 如果当前版本已有预发布后缀,递增进位号
|
||
if (sv.pre) {
|
||
const match = sv.pre.match(new RegExp(`^${arg1}\\.(\\d+)$`));
|
||
if (match) {
|
||
const num = parseInt(match[1]) + 1;
|
||
newVersion = `${sv.major}.${sv.minor}.${sv.patch}-${arg1}.${num}`;
|
||
} else {
|
||
// 不同预发布类型,重新开始
|
||
newVersion = `${sv.major}.${sv.minor}.${sv.patch}-${arg1}.1`;
|
||
}
|
||
} else {
|
||
// 纯 semver → 首次预发布:自动升 patch 再加后缀
|
||
sv.patch++;
|
||
newVersion = `${sv.major}.${sv.minor}.${sv.patch}-${arg1}.1`;
|
||
}
|
||
break;
|
||
}
|
||
|
||
default:
|
||
console.error(`未知命令: ${command}。支持: major | minor | patch | set <ver> | pre <beta|alpha>`);
|
||
process.exit(1);
|
||
}
|
||
|
||
// ---------- 更新 package.json ----------
|
||
|
||
pkg.version = newVersion;
|
||
writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n', 'utf-8');
|
||
console.log(`✅ package.json: ${oldVersion} → ${newVersion}`);
|
||
|
||
// ---------- 更新 shared/constants/version.ts(由 import pkg 动态读取,无需修改) ----------
|
||
console.log(' shared/constants/version.ts 自动读取 package.json,无需手动同步');
|
||
|
||
// ---------- 更新 CHANGELOG.md ----------
|
||
|
||
if (!noChangelog) {
|
||
const changelogPath = join(ROOT, 'CHANGELOG.md');
|
||
try {
|
||
let changelog = readFileSync(changelogPath, 'utf-8');
|
||
|
||
const today = new Date().toISOString().slice(0, 10);
|
||
const entry = `## ${newVersion}(${today})\n\n**更新内容待补充**\n\n---\n\n`;
|
||
|
||
// 在第一个 ## 之前插入新版本条目
|
||
const firstHeaderMatch = changelog.match(/^##\s/m);
|
||
if (firstHeaderMatch) {
|
||
const insertAt = firstHeaderMatch.index;
|
||
changelog = changelog.slice(0, insertAt) + entry + changelog.slice(insertAt);
|
||
writeFileSync(changelogPath, changelog, 'utf-8');
|
||
console.log(`✅ CHANGELOG.md: 已添加 v${newVersion} 模板条目`);
|
||
} else {
|
||
console.warn('⚠️ CHANGELOG.md 格式异常,未找到 ## 版本标题');
|
||
}
|
||
} catch {
|
||
console.warn('⚠️ CHANGELOG.md 不存在,跳过更新');
|
||
}
|
||
}
|
||
|
||
console.log(`\n版本已更新: ${oldVersion} → ${newVersion}`);
|