Files
xk-admin/apps/desktop/scripts/pack.mjs

197 lines
6.1 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.
/**
* 桌面打包脚本:交互确认版本号 → 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);
});