197 lines
6.1 KiB
JavaScript
197 lines
6.1 KiB
JavaScript
/**
|
||
* 桌面打包脚本:交互确认版本号 → electron-builder → 复制友好文件名
|
||
*
|
||
* 交互:
|
||
* 上次打包版本号:x.y.z
|
||
* 1) 使用上次版本号
|
||
* 2) 输入新版本号
|
||
*
|
||
* 产物:
|
||
* - 自动更新用 ASCII 名:xk-desktop-{version}-win-x64.exe(latest.yml 引用)
|
||
* - 人工/后台上传用:萧康医药 {version}.exe
|
||
*
|
||
* 非交互(CI):XK_DESKTOP_VERSION=1.2.3 或 --version=1.2.3
|
||
*/
|
||
import { spawn } from 'node:child_process';
|
||
import { copyFileSync, existsSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';
|
||
import { createInterface } from 'node:readline';
|
||
import { dirname, join } from 'node:path';
|
||
import { fileURLToPath } from 'node:url';
|
||
|
||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||
const desktopRoot = join(__dirname, '..');
|
||
const packageJsonPath = join(desktopRoot, 'package.json');
|
||
const releaseDir = join(desktopRoot, 'release');
|
||
|
||
/** semver 简校验 */
|
||
function isValidVersion(v) {
|
||
return /^\d+\.\d+\.\d+([-+][0-9A-Za-z.-]+)?$/.test(v);
|
||
}
|
||
|
||
function readPkg() {
|
||
return JSON.parse(readFileSync(packageJsonPath, 'utf8'));
|
||
}
|
||
|
||
function writePkgVersion(version) {
|
||
const pkg = readPkg();
|
||
pkg.version = version;
|
||
writeFileSync(packageJsonPath, `${JSON.stringify(pkg, null, 2)}\n`, 'utf8');
|
||
}
|
||
|
||
/** 从 argv / 环境变量取强制版本(跳过交互) */
|
||
function getForcedVersion() {
|
||
const arg = process.argv.find((a) => a.startsWith('--version='));
|
||
if (arg) return arg.slice('--version='.length).trim();
|
||
if (process.env.XK_DESKTOP_VERSION) return String(process.env.XK_DESKTOP_VERSION).trim();
|
||
return '';
|
||
}
|
||
|
||
function ask(question) {
|
||
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
||
return new Promise((resolve) => {
|
||
rl.question(question, (answer) => {
|
||
rl.close();
|
||
resolve(String(answer || '').trim());
|
||
});
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 交互确认本次打包版本
|
||
* @returns {Promise<string>}
|
||
*/
|
||
async function resolveVersion() {
|
||
const forced = getForcedVersion();
|
||
if (forced) {
|
||
if (!isValidVersion(forced)) {
|
||
throw new Error(`无效版本号: ${forced}(请用如 1.0.1)`);
|
||
}
|
||
console.log(`使用指定版本号: ${forced}`);
|
||
return forced;
|
||
}
|
||
|
||
if (!process.stdin.isTTY) {
|
||
const current = readPkg().version || '1.0.0';
|
||
console.log(`非交互环境,沿用 package.json 版本: ${current}`);
|
||
return current;
|
||
}
|
||
|
||
const last = readPkg().version || '1.0.0';
|
||
console.log('');
|
||
console.log('========== 桌面端版本确认 ==========');
|
||
console.log(`上次打包版本号:${last}`);
|
||
console.log('请选择:');
|
||
console.log(` 1) 使用上次版本号(${last})`);
|
||
console.log(' 2) 输入新的版本号');
|
||
console.log('');
|
||
|
||
let choice = await ask('请输入选项 [1/2](默认 1): ');
|
||
if (choice === '' || choice === '1') {
|
||
return last;
|
||
}
|
||
if (choice !== '2') {
|
||
console.log('未识别选项,默认使用上次版本号');
|
||
return last;
|
||
}
|
||
|
||
for (;;) {
|
||
const next = await ask('请输入新版本号(如 1.0.1): ');
|
||
if (isValidVersion(next)) {
|
||
return next;
|
||
}
|
||
console.log('格式不正确,请使用 semver,例如 1.0.1');
|
||
}
|
||
}
|
||
|
||
function runBuilder() {
|
||
// electron-builder 下载 Electron / NSIS 等会打 GitHub;国内网络常超时。
|
||
// 根 .npmrc 有镜像配置,但 spawn 出的进程不一定带上,这里强制写入环境变量。
|
||
const electronMirror =
|
||
process.env.ELECTRON_MIRROR || 'https://npmmirror.com/mirrors/electron/';
|
||
const builderBinariesMirror =
|
||
process.env.ELECTRON_BUILDER_BINARIES_MIRROR ||
|
||
'https://npmmirror.com/mirrors/electron-builder-binaries/';
|
||
const env = {
|
||
...process.env,
|
||
ELECTRON_MIRROR: electronMirror,
|
||
ELECTRON_BUILDER_BINARIES_MIRROR: builderBinariesMirror,
|
||
// 兼容 app-builder-lib 读取的 npm_config_* 形态
|
||
npm_config_electron_mirror: electronMirror,
|
||
npm_config_electron_builder_binaries_mirror: builderBinariesMirror,
|
||
};
|
||
console.log(`Electron 镜像: ${electronMirror}`);
|
||
console.log(`Builder 工具链镜像: ${builderBinariesMirror}`);
|
||
return new Promise((resolve, reject) => {
|
||
const child = spawn(
|
||
'pnpm exec electron-builder --config electron-builder.config.cjs',
|
||
{
|
||
cwd: desktopRoot,
|
||
env,
|
||
shell: true,
|
||
stdio: 'inherit',
|
||
},
|
||
);
|
||
child.on('error', reject);
|
||
child.on('close', (code) => {
|
||
if (code === 0) resolve();
|
||
else reject(new Error(`electron-builder 失败,退出码 ${code}`));
|
||
});
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 复制 Windows 安装包为「萧康医药 {version}.exe」方便后台/人工上传
|
||
* 保留原 ASCII 文件名供 electron-updater 的 latest.yml 使用
|
||
*/
|
||
function copyFriendlyWinExe(version) {
|
||
if (!existsSync(releaseDir)) {
|
||
console.warn(`未找到 release 目录: ${releaseDir}`);
|
||
return;
|
||
}
|
||
const files = readdirSync(releaseDir);
|
||
// 优先精确匹配 builder artifactName
|
||
const exact = `xk-desktop-${version}-win-x64.exe`;
|
||
let src = files.includes(exact) ? exact : '';
|
||
if (!src) {
|
||
src =
|
||
files.find(
|
||
(f) =>
|
||
f.endsWith('.exe') &&
|
||
f.includes(version) &&
|
||
!f.includes('萧康') &&
|
||
!f.toLowerCase().includes('blockmap'),
|
||
) || '';
|
||
}
|
||
if (!src) {
|
||
console.warn('未找到 Windows 安装包,跳过友好文件名复制');
|
||
return;
|
||
}
|
||
const destName = `萧康医药 ${version}.exe`;
|
||
const from = join(releaseDir, src);
|
||
const to = join(releaseDir, destName);
|
||
copyFileSync(from, to);
|
||
console.log(`\n已生成上传用安装包: ${to}`);
|
||
console.log(`(自动更新仍使用: ${from})`);
|
||
}
|
||
|
||
async function main() {
|
||
const version = await resolveVersion();
|
||
const current = readPkg().version;
|
||
if (version !== current) {
|
||
writePkgVersion(version);
|
||
console.log(`已写入 package.json version = ${version}`);
|
||
} else {
|
||
console.log(`版本号保持 ${version}`);
|
||
}
|
||
|
||
console.log('\n开始 electron-builder 打包…\n');
|
||
await runBuilder();
|
||
copyFriendlyWinExe(version);
|
||
console.log('\n打包流程结束');
|
||
}
|
||
|
||
main().catch((err) => {
|
||
console.error(`\n❌ ${err.message || err}`);
|
||
process.exit(1);
|
||
});
|