GPU_GUARD_MONOREPO/packages/backend/scripts/build-windows-installer.js
2026-05-20 21:39:12 +08:00

122 lines
4.2 KiB
JavaScript

const cp = require('node:child_process');
const fs = require('node:fs');
const path = require('node:path');
const yaml = require('js-yaml');
const backendDir = path.resolve(__dirname, '..');
const repoDir = path.resolve(backendDir, '..', '..');
const outputDir = path.join(backendDir, 'build', 'pkg-output');
const trayProject = path.join(repoDir, 'packages', 'windows-tray', 'Neta.Tray', 'Neta.Tray.csproj');
const trayOutputDir = path.join(backendDir, 'build', 'tray-output');
const nodeRuntimeDir = path.join(backendDir, 'build', 'node-runtime');
const skillsSourceDir = path.join(backendDir, 'skills');
const skillsOutputDir = path.join(backendDir, 'build', 'skills-output');
const toolsSourceDir = path.join(backendDir, 'tools', 'win32');
const toolsOutputDir = path.join(backendDir, 'build', 'tools-output', 'win32');
const issPath = path.join(backendDir, 'installer', 'setup.iss');
const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm';
function copyBundledNodeRuntime() {
if (process.platform !== 'win32') {
console.log('Skipping bundled node.exe copy on non-Windows host.');
return;
}
fs.rmSync(nodeRuntimeDir, { recursive: true, force: true });
fs.mkdirSync(nodeRuntimeDir, { recursive: true });
fs.copyFileSync(process.execPath, path.join(nodeRuntimeDir, 'node.exe'));
}
function readSkillConfig(skillDir) {
const configPath = path.join(skillDir, 'skill.config.yaml');
if (!fs.existsSync(configPath)) return null;
return yaml.load(fs.readFileSync(configPath, 'utf8')) || null;
}
function prepareSkillsOutput() {
fs.rmSync(skillsOutputDir, { recursive: true, force: true });
fs.cpSync(skillsSourceDir, skillsOutputDir, {
recursive: true,
filter: source => path.basename(source) !== 'node_modules',
});
for (const entry of fs.readdirSync(skillsOutputDir, { withFileTypes: true })) {
if (!entry.isDirectory()) continue;
const skillDir = path.join(skillsOutputDir, entry.name);
const config = readSkillConfig(skillDir);
const nodePackages = config?.runtime === 'node' && config?.entrypoint
? config?.dependencies?.node?.packages
: null;
if (!Array.isArray(nodePackages) || nodePackages.length === 0) continue;
console.log(`Installing Node dependencies for skill ${entry.name}...`);
cp.execFileSync(
npmCmd,
[
'install',
'--omit=dev',
'--no-audit',
'--no-fund',
'--package-lock=false',
...nodePackages,
],
{ cwd: skillDir, stdio: 'inherit' }
);
}
}
if (!fs.existsSync(issPath)) {
throw new Error(`缺少安装器脚本: ${issPath}`);
}
if (!fs.existsSync(path.join(outputDir, 'backend.exe'))) {
console.log('backend.exe not found, running pkg build first...');
cp.execFileSync('node', ['scripts/pkg-build.js'], { cwd: backendDir, stdio: 'inherit' });
}
copyBundledNodeRuntime();
prepareSkillsOutput();
fs.rmSync(trayOutputDir, { recursive: true, force: true });
cp.execFileSync(
'dotnet',
[
'publish',
trayProject,
'-c', 'Release',
'-r', 'win-x64',
'--self-contained', 'true',
'/p:PublishSingleFile=true',
'/p:PublishTrimmed=false',
'-o', trayOutputDir,
],
{ cwd: repoDir, stdio: 'inherit' }
);
const publishedTrayExePath = path.join(trayOutputDir, 'Neta.Tray.exe');
if (!fs.existsSync(publishedTrayExePath)) {
throw new Error(`缺少托盘产物: ${publishedTrayExePath}`);
}
// 架构 C: 把 tools/win32/*.ps1 拷贝到 installer stage 目录(weixin-db 的 key 抽取脚本)
fs.rmSync(toolsOutputDir, { recursive: true, force: true });
fs.mkdirSync(toolsOutputDir, { recursive: true });
if (fs.existsSync(toolsSourceDir)) {
fs.cpSync(toolsSourceDir, toolsOutputDir, { recursive: true });
console.log(`[installer] copied tools/win32 → ${toolsOutputDir}`);
} else {
console.warn(`[installer] tools/win32 not found at ${toolsSourceDir}, skip`);
}
const isccCandidates = [
path.join(process.env.LOCALAPPDATA || '', 'Programs', 'Inno Setup 6', 'ISCC.exe'),
'C:\\Program Files (x86)\\Inno Setup 6\\ISCC.exe',
'C:\\Program Files\\Inno Setup 6\\ISCC.exe',
];
const iscc = isccCandidates.find(p => fs.existsSync(p)) || 'iscc';
console.log('Building installer with Inno Setup...');
cp.execFileSync(iscc, [issPath], { cwd: backendDir, stdio: 'inherit' });
console.log('Installer build complete.');