starmusic-pc
1#!/usr/bin/env node
2/**
3 * 一键打包 PC 端「安装版」
4 *
5 * 约定:PC 端只分发 NSIS 安装包(build/bin/starmusic-pc-<版本>-Setup.exe),
6 * 不再生成免安装版 zip。
7 *
8 * 这个脚本替 Wails 做了两件它自己不做、但每次都会踩的事:
9 * 1. makensis 不在 PATH 时自动补上 —— Wails 只做 PATH 查找,找不到就
10 * 只打印一句 "Cannot create installer: makensis not found" 然后静默跳过,
11 * 很容易误以为打包成功了
12 * 2. 先给 project.nsi 补 UTF-8 BOM —— 否则 NSIS 会按系统 ANSI 代码页
13 * 解码,安装向导里的中文全是乱码
14 *
15 * 用法(在 pc 目录下):
16 * node scripts/build-installer.cjs
17 * npm run build:installer (若 pc/package.json 里配了同名脚本)
18 */
19const { spawnSync } = require('child_process')
20const fs = require('fs')
21const path = require('path')
22
23const PC_DIR = path.resolve(__dirname, '..')
24const BIN_DIR = path.join(PC_DIR, 'build', 'bin')
25
26/** NSIS 默认安装位置:winget 装完后不会写入 PATH */
27const NSIS_DIRS = ['C:\\Program Files (x86)\\NSIS', 'C:\\Program Files\\NSIS']
28
29/** 定位 makensis.exe:先查 NSIS 安装目录,再查 PATH */
30const findMakensis = () => {
31 for (const dir of NSIS_DIRS) {
32 const exe = path.join(dir, 'makensis.exe')
33 if (fs.existsSync(exe)) return exe
34 }
35 const where = spawnSync('where', ['makensis'], { encoding: 'utf8' })
36 if (where.status === 0 && where.stdout.trim()) return where.stdout.trim().split(/\r?\n/)[0]
37 return ''
38}
39
40const makensis = findMakensis()
41if (!makensis) {
42 console.error('✗ 找不到 makensis,无法生成安装包。')
43 console.error(' 安装:winget install --id NSIS.NSIS -e')
44 process.exit(1)
45}
46console.log(`✓ makensis:${makensis}`)
47
48// Wails 通过 PATH 查找 makensis,这里临时注入,不改动系统环境变量
49const env = { ...process.env, PATH: `${path.dirname(makensis)};${process.env.PATH}` }
50
51// 1. NSIS 脚本预处理(BOM + 编码体检)
52const prepare = spawnSync(process.execPath, [path.join(__dirname, 'prepare-nsis.cjs')], {
53 cwd: PC_DIR,
54 stdio: 'inherit',
55})
56if (prepare.status !== 0) process.exit(prepare.status || 1)
57
58// 2. 构建前端 + Go,并生成安装包
59// 用整条命令字符串而不是「命令 + 参数数组」:shell 模式下数组参数不会被转义,
60// Node 24 会就此发 DEP0190 告警
61const build = spawnSync('wails build -nsis', { cwd: PC_DIR, stdio: 'inherit', env, shell: true })
62if (build.status !== 0) {
63 console.error('\n✗ 打包失败')
64 process.exit(build.status || 1)
65}
66
67// 3. 汇报产物
68console.log('\n=== 打包完成(安装版)===')
69if (!fs.existsSync(BIN_DIR)) {
70 console.error(`✗ 未找到产物目录 ${BIN_DIR}`)
71 process.exit(1)
72}
73const installer = fs.readdirSync(BIN_DIR).filter((f) => /-Setup\.exe$/i.test(f))
74if (!installer.length) {
75 console.error('✗ 没有生成安装包,请检查上面的 Wails 输出')
76 process.exit(1)
77}
78for (const file of fs.readdirSync(BIN_DIR)) {
79 const size = (fs.statSync(path.join(BIN_DIR, file)).size / 1024 / 1024).toFixed(2)
80 console.log(` ${installer.includes(file) ? '★' : ' '} ${file} ${size} MB`)
81}