#!/usr/bin/env node /** * Tauri 构建脚本 * Build script for Claude AI Installer (Tauri) * * 用法 / Usage: * node scripts/build.js [options] * * 选项 / Options: * --debug, -d 调试模式构建 * --skip-frontend 跳过前端构建 * --help, -h 显示帮助信息 */ import { execSync } from 'child_process'; import path from 'path'; import fs from 'fs'; import { fileURLToPath } from 'url'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); // 颜色输出 const colors = { reset: '\x1b[0m', bright: '\x1b[1m', red: '\x1b[31m', green: '\x1b[32m', yellow: '\x1b[33m', blue: '\x1b[34m', cyan: '\x1b[36m', }; const log = { info: (msg) => console.log(`${colors.blue}ℹ${colors.reset} ${msg}`), success: (msg) => console.log(`${colors.green}✔${colors.reset} ${msg}`), warn: (msg) => console.log(`${colors.yellow}⚠${colors.reset} ${msg}`), error: (msg) => console.log(`${colors.red}✖${colors.reset} ${msg}`), step: (msg) => console.log(`\n${colors.cyan}${colors.bright}▶ ${msg}${colors.reset}`), }; // 项目路径 const PROJECT_ROOT = path.resolve(__dirname, '..'); const TAURI_DIR = path.join(PROJECT_ROOT, 'src-tauri'); // 解析命令行参数 function parseArgs() { const args = process.argv.slice(2); const options = { debug: false, skipFrontend: false, help: false, }; for (let i = 0; i < args.length; i++) { const arg = args[i]; switch (arg) { case '--debug': case '-d': options.debug = true; break; case '--skip-frontend': case '-s': options.skipFrontend = true; break; case '--help': case '-h': options.help = true; break; } } return options; } // 显示帮助信息 function showHelp() { console.log(` ${colors.bright}Claude AI Installer 构建脚本 (Tauri)${colors.reset} ${colors.cyan}用法:${colors.reset} node scripts/build.js [options] ${colors.cyan}选项:${colors.reset} --debug, -d 以调试模式构建 --skip-frontend, -s 跳过前端构建 --help, -h 显示此帮助信息 ${colors.cyan}示例:${colors.reset} node scripts/build.js # 完整发布构建 node scripts/build.js --debug # 调试构建 node scripts/build.js -s # 跳过前端,仅构建 Tauri ${colors.cyan}输出 (发布模式):${colors.reset} src-tauri/target/release/bundle/ ├── nsis/*.exe # NSIS 安装程序 └── msi/*.msi # MSI 安装程序 src-tauri/target/release/ └── claude-ai-installer.exe # 可执行文件 ${colors.cyan}输出 (调试模式):${colors.reset} src-tauri/target/debug/ └── claude-ai-installer.exe # 调试版可执行文件 `); } // 执行命令 function exec(command, options = {}) { log.info(`执行: ${command}`); try { execSync(command, { stdio: 'inherit', cwd: PROJECT_ROOT, ...options, }); return true; } catch (error) { log.error(`命令执行失败: ${command}`); return false; } } // 获取版本号 function getVersion() { const tauriConfPath = path.join(TAURI_DIR, 'tauri.conf.json'); const tauriConf = JSON.parse(fs.readFileSync(tauriConfPath, 'utf8')); return tauriConf.version; } // 获取产品名称 function getProductName() { const tauriConfPath = path.join(TAURI_DIR, 'tauri.conf.json'); const tauriConf = JSON.parse(fs.readFileSync(tauriConfPath, 'utf8')); return tauriConf.productName || 'Claude AI Installer'; } // 构建 Tauri 应用 function buildTauri(options) { log.step(`构建 Tauri 应用 (${options.debug ? '调试' : '发布'}模式)...`); let command = 'npm run tauri build'; if (options.debug) { command += ' -- --debug'; } if (!exec(command)) { throw new Error('Tauri 构建失败'); } log.success('Tauri 构建完成'); } // 重命名构建产物,使用与 Electron 版本一致的命名格式 function renameArtifacts(options) { log.step('重命名构建产物...'); const mode = options.debug ? 'debug' : 'release'; const targetDir = path.join(TAURI_DIR, 'target', mode); const bundleDir = path.join(targetDir, 'bundle'); const version = getVersion(); // 创建 portable 版本(复制可执行文件到 bundle 目录) const exeName = 'claude-ai-installer.exe'; const exePath = path.join(targetDir, exeName); if (fs.existsSync(exePath)) { // 确保 bundle 目录存在 if (!fs.existsSync(bundleDir)) { fs.mkdirSync(bundleDir, { recursive: true }); } // 新文件名格式: Claude-AI-Installer-{version}-win-x64-portable.exe const portableName = `Claude-AI-Installer-${version}-win-x64-portable.exe`; const portablePath = path.join(bundleDir, portableName); // 如果目标文件已存在,先删除 if (fs.existsSync(portablePath)) { fs.unlinkSync(portablePath); } fs.copyFileSync(exePath, portablePath); log.success(`创建便携版: ${portableName}`); } // 重命名 NSIS 安装程序 const nsisDir = path.join(bundleDir, 'nsis'); if (fs.existsSync(nsisDir)) { const files = fs.readdirSync(nsisDir).filter(f => f.endsWith('.exe')); for (const file of files) { const oldPath = path.join(nsisDir, file); // 新文件名格式: Claude-AI-Installer-{version}-win-x64-setup.exe const newName = `Claude-AI-Installer-${version}-win-x64-setup.exe`; const newPath = path.join(nsisDir, newName); if (file !== newName) { // 如果目标文件已存在,先删除 if (fs.existsSync(newPath)) { fs.unlinkSync(newPath); } fs.renameSync(oldPath, newPath); log.success(`重命名: ${file} -> ${newName}`); } } } // 重命名 MSI 安装程序 const msiDir = path.join(bundleDir, 'msi'); if (fs.existsSync(msiDir)) { const files = fs.readdirSync(msiDir).filter(f => f.endsWith('.msi')); for (const file of files) { const oldPath = path.join(msiDir, file); // 新文件名格式: Claude-AI-Installer-{version}-win-x64.msi const newName = `Claude-AI-Installer-${version}-win-x64.msi`; const newPath = path.join(msiDir, newName); if (file !== newName) { // 如果目标文件已存在,先删除 if (fs.existsSync(newPath)) { fs.unlinkSync(newPath); } fs.renameSync(oldPath, newPath); log.success(`重命名: ${file} -> ${newName}`); } } } } // 显示构建结果 function showResults(options) { log.step('构建结果:'); const mode = options.debug ? 'debug' : 'release'; const targetDir = path.join(TAURI_DIR, 'target', mode); const bundleDir = path.join(targetDir, 'bundle'); console.log(''); // 显示便携版 const portableFiles = fs.existsSync(bundleDir) ? fs.readdirSync(bundleDir).filter(f => f.endsWith('-portable.exe')) : []; for (const file of portableFiles) { const filePath = path.join(bundleDir, file); const stats = fs.statSync(filePath); const sizeMB = (stats.size / (1024 * 1024)).toFixed(2); console.log(` ${colors.green}•${colors.reset} ${file} (${sizeMB} MB) [便携版]`); } // 显示 NSIS 安装程序 const nsisDir = path.join(bundleDir, 'nsis'); if (fs.existsSync(nsisDir)) { const files = fs.readdirSync(nsisDir).filter(f => f.endsWith('.exe')); for (const file of files) { const filePath = path.join(nsisDir, file); const stats = fs.statSync(filePath); const sizeMB = (stats.size / (1024 * 1024)).toFixed(2); console.log(` ${colors.green}•${colors.reset} nsis/${file} (${sizeMB} MB)`); } } // 显示 MSI 安装程序 const msiDir = path.join(bundleDir, 'msi'); if (fs.existsSync(msiDir)) { const files = fs.readdirSync(msiDir).filter(f => f.endsWith('.msi')); for (const file of files) { const filePath = path.join(msiDir, file); const stats = fs.statSync(filePath); const sizeMB = (stats.size / (1024 * 1024)).toFixed(2); console.log(` ${colors.green}•${colors.reset} msi/${file} (${sizeMB} MB)`); } } console.log(''); log.info(`输出目录: ${targetDir}`); if (fs.existsSync(bundleDir)) { log.info(`安装包目录: ${bundleDir}`); } } // 主函数 async function main() { const options = parseArgs(); if (options.help) { showHelp(); process.exit(0); } console.log(` ${colors.bright}╔════════════════════════════════════════════╗ ║ Claude AI Installer 构建脚本 ║ ║ (Tauri 2.0) ║ ╚════════════════════════════════════════════╝${colors.reset} `); log.info(`构建模式: ${options.debug ? '调试' : '发布'}`); log.info(`版本: ${getVersion()}`); log.info(`产品名称: ${getProductName()}`); const startTime = Date.now(); try { // 构建 Tauri 应用 buildTauri(options); // 重命名构建产物(仅发布模式) if (!options.debug) { renameArtifacts(options); } // 显示结果 showResults(options); const duration = ((Date.now() - startTime) / 1000).toFixed(1); log.success(`构建完成! 耗时: ${duration}s`); } catch (error) { log.error(error.message); process.exit(1); } } main();