Files
ele-HeiXiu/build/scripts/generate-update-info.mjs
YoungestSongMo 337d6c1205 feat: 构建系统重构 + VCHSM 架构迁移 + 右键菜单修复 + 尺寸参数统一
## 构建、打包与分发系统重构 (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>
2026-06-29 18:41:07 +08:00

203 lines
6.4 KiB
JavaScript
Raw Permalink 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.
// ============================================================
// 打包后生成 update-info.json含渠道/版本类型/SHA512/blockmap
//
// 用法(由 build.mjs 调用):
// node build/scripts/generate-update-info.mjs <platform> <version> <channel> <edition> <ossPrefix> <releaseDirName>
//
// 也支持手动调用:
// node build/scripts/generate-update-info.mjs <platform> <version> [channel] [edition] [ossPrefix] [releaseDirName] [--url <url>] [--blockmap-url <url>]
//
// 环境变量(可选):
// UPDATE_DOWNLOAD_BASE — OSS 下载基地址
// UPDATE_FORCE — 是否强制更新,默认 false
// UPDATE_MIN_VERSION — 最低兼容版本,默认 0.0.0
// UPDATE_CHANGELOG — 覆盖 CHANGELOG.md 中的日志内容
// ============================================================
import { readFileSync, writeFileSync, readdirSync, statSync } from 'node:fs';
import { join, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
import { createHash } from 'node:crypto';
const __dirname = dirname(fileURLToPath(import.meta.url));
const ROOT = join(__dirname, '..', '..');
// ---------- 参数解析 ----------
const args = process.argv.slice(2);
let urlOverride = '';
let blockmapUrlOverride = '';
const positional = [];
for (let i = 0; i < args.length; i++) {
if (args[i] === '--url' && args[i + 1]) {
urlOverride = args[++i];
} else if (args[i] === '--blockmap-url' && args[i + 1]) {
blockmapUrlOverride = args[++i];
} else if (!args[i].startsWith('--')) {
positional.push(args[i]);
}
}
const platform = positional[0];
const version = positional[1];
const channel = positional[2] || 'stable';
const edition = positional[3] || 'personal';
const ossPrefix = positional[4] || `releases/${version}`;
const releaseDirName = positional[5] || version;
if (!platform || !version) {
console.error('用法: node build/scripts/generate-update-info.mjs <platform> <version> [channel] [edition] [ossPrefix] [releaseDirName]');
process.exit(1);
}
const productName = '船长·HeiXiu';
// ---------- 查找安装包 ----------
const releaseDir = join(ROOT, 'release', releaseDirName);
let installerPath;
try {
const files = readdirSync(releaseDir);
const exts = platform === 'win32' ? ['.exe'] : platform === 'darwin' ? ['.dmg'] : ['.AppImage'];
const match = files.find((f) => exts.some((ext) => f.endsWith(ext)));
if (!match) {
console.error(`未在 ${releaseDir} 中找到安装包文件`);
process.exit(1);
}
installerPath = join(releaseDir, match);
console.log(`找到安装包: ${match}`);
} catch {
console.error(`目录不存在: ${releaseDir}\n请先执行构建(如 npm run build:win`);
process.exit(1);
}
// ---------- 计算文件信息 ----------
const fileBuffer = readFileSync(installerPath);
const stats = statSync(installerPath);
const sha512 = createHash('sha512').update(fileBuffer).digest('base64').replace(/\r?\n/g, '');
const fileSize = stats.size;
console.log(`文件大小: ${(fileSize / 1024 / 1024).toFixed(2)} MB`);
console.log(`SHA512: ${sha512.slice(0, 32)}...`);
// ---------- 查找 blockmap 文件 ----------
let blockMapUrl;
let blockMapSize;
try {
const blockmapFile = readdirSync(releaseDir).find((f) => f.endsWith('.blockmap'));
if (blockmapFile) {
const blockmapPath = join(releaseDir, blockmapFile);
const blockmapStats = statSync(blockmapPath);
blockMapSize = blockmapStats.size;
if (blockmapUrlOverride) {
blockMapUrl = blockmapUrlOverride;
} else {
const downloadBase = process.env.UPDATE_DOWNLOAD_BASE || 'https://heixiu.oss-cn-hangzhou.aliyuncs.com';
blockMapUrl = `${downloadBase.replace(/\/+$/, '')}/${ossPrefix}/${encodeURI(blockmapFile)}`;
}
console.log(`Blockmap: ${blockmapFile} (${(blockMapSize / 1024).toFixed(2)} KB)`);
console.log(`Blockmap URL: ${blockMapUrl}`);
}
} catch {
blockMapUrl = undefined;
blockMapSize = undefined;
}
// ---------- 构建下载 URL ----------
const fileName = installerPath.split(/[/\\]/).pop();
let downloadUrl;
if (urlOverride) {
downloadUrl = urlOverride;
console.log(`下载 URL手动指定: ${downloadUrl}`);
} else {
const downloadBase = process.env.UPDATE_DOWNLOAD_BASE || 'https://heixiu.oss-cn-hangzhou.aliyuncs.com';
downloadUrl = `${downloadBase.replace(/\/+$/, '')}/${ossPrefix}/${encodeURI(fileName)}`;
console.log(`下载 URL自动拼接: ${downloadUrl}`);
}
// ---------- 读取更新日志 ----------
let changelog;
if (process.env.UPDATE_CHANGELOG) {
changelog = process.env.UPDATE_CHANGELOG;
} else {
const changelogPath = join(ROOT, 'CHANGELOG.md');
try {
const changelogContent = readFileSync(changelogPath, 'utf-8');
// 精确匹配当前版本块
const escapedVersion = version.replace(/\./g, '\\.');
const versionSection = changelogContent.match(
new RegExp(`^##\\s+${escapedVersion}[^\\n]*\\n([\\s\\S]*?)(?=^##\\s|\\Z)`, 'm'),
);
if (versionSection) {
changelog = versionSection[0].trim();
console.log(`更新日志: 已从 CHANGELOG.md 提取 v${version} 条目`);
} else {
changelog = `## ${version}\n\n更新内容待补充`;
console.warn(`⚠️ CHANGELOG.md 中未找到 v${version} 条目,使用占位内容`);
}
} catch {
changelog = `## ${version}\n\n更新内容待补充`;
}
}
// ---------- 其他配置 ----------
const forceUpdate = process.env.UPDATE_FORCE === 'true';
const minimumVersion = process.env.UPDATE_MIN_VERSION || '0.0.0';
const releaseDate = new Date().toISOString().slice(0, 10);
// ---------- 生成 JSON ----------
const updateInfo = {
// --- 必填API 返回字段 ---
hasUpdate: true,
latestVersion: version,
downloadUrl,
fileSize,
sha512,
changelog,
releaseDate,
forceUpdate,
minimumVersion,
// --- 渠道与版本类型(灰度测试关键字段)---
channel,
edition,
// --- 增量更新 ---
...(blockMapUrl && { blockMapUrl }),
...(blockMapSize != null && { blockMapSize }),
// --- 附加元数据 ---
_meta: {
platform,
channel,
edition,
fileName,
ossPrefix,
blockmap: blockMapUrl ? { url: blockMapUrl, size: blockMapSize } : null,
generatedAt: new Date().toISOString(),
},
};
const outputPath = join(
releaseDir,
`${productName}-${platform}-v${version}-update-info.json`,
);
writeFileSync(outputPath, JSON.stringify(updateInfo, null, 2), 'utf-8');
console.log(`\n✅ 已生成: ${outputPath}`);
console.log(JSON.stringify(updateInfo, null, 2));