#!/usr/bin/env node /** * 发布脚本 * Release script for Claude AI Installer * * 功能: * - 自动更新版本号 * - 构建所有平台的安装包 * - 生成发布说明 * - 可选: 创建 Git tag 并推送 * * 用法 / Usage: * node scripts/release.js [version] [options] * * 示例: * node scripts/release.js patch # 0.0.1 -> 0.0.2 * node scripts/release.js minor # 0.0.1 -> 0.1.0 * node scripts/release.js major # 0.0.1 -> 1.0.0 * node scripts/release.js 1.2.3 # 设置为指定版本 * node scripts/release.js patch --tag # 更新版本并创建 Git tag */ const { execSync } = require('child_process'); const path = require('path'); const fs = require('fs'); // 颜色输出 const colors = { reset: '\x1b[0m', bright: '\x1b[1m', red: '\x1b[31m', green: '\x1b[32m', yellow: '\x1b[33m', blue: '\x1b[34m', cyan: '\x1b[36m', magenta: '\x1b[35m', }; 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 projectRoot = path.resolve(__dirname, '..'); const packageJsonPath = path.join(projectRoot, 'package.json'); // 解析命令行参数 function parseArgs() { const args = process.argv.slice(2); // 每个平台默认只构建对应的版本,避免跨平台构建失败 const platformMap = { win32: 'win', darwin: 'mac', linux: 'linux' }; const currentPlatform = platformMap[process.platform] || 'linux'; const defaultPlatforms = [currentPlatform]; const options = { version: null, tag: false, push: false, platforms: defaultPlatforms, dryRun: false, help: false, }; for (let i = 0; i < args.length; i++) { const arg = args[i]; switch (arg) { case '--tag': case '-t': options.tag = true; break; case '--push': options.push = true; break; case '--platform': case '-p': options.platforms = args[++i].split(','); break; case '--dry-run': options.dryRun = true; break; case '--help': case '-h': options.help = true; break; default: if (!arg.startsWith('-') && !options.version) { options.version = arg; } } } return options; } // 显示帮助信息 function showHelp() { console.log(` ${colors.bright}Claude AI Installer 发布脚本${colors.reset} ${colors.cyan}用法:${colors.reset} node scripts/release.js [version] [options] ${colors.cyan}版本参数:${colors.reset} patch 补丁版本 (0.0.1 -> 0.0.2) minor 次版本 (0.0.1 -> 0.1.0) major 主版本 (0.0.1 -> 1.0.0) x.y.z 指定版本号 ${colors.cyan}选项:${colors.reset} --tag, -t 创建 Git tag --push 推送 tag 到远程仓库 --platform, -p 指定平台 (逗号分隔): win,mac,linux --dry-run 仅显示将要执行的操作,不实际执行 --help, -h 显示此帮助信息 ${colors.cyan}示例:${colors.reset} node scripts/release.js patch # 更新补丁版本并构建 node scripts/release.js 1.0.0 --tag # 设置版本为 1.0.0 并创建 tag node scripts/release.js minor -p win,mac # 更新次版本,仅构建 Win 和 Mac node scripts/release.js patch --tag --push # 更新版本、创建 tag 并推送 `); } // 读取 package.json function readPackageJson() { return JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8')); } // 写入 package.json function writePackageJson(pkg) { fs.writeFileSync(packageJsonPath, JSON.stringify(pkg, null, 2) + '\n'); } // 计算新版本号 function calculateNewVersion(currentVersion, versionArg) { if (!versionArg) { return currentVersion; } // 如果是具体版本号 if (/^\d+\.\d+\.\d+/.test(versionArg)) { return versionArg; } const parts = currentVersion.split('.').map(Number); switch (versionArg) { case 'major': return `${parts[0] + 1}.0.0`; case 'minor': return `${parts[0]}.${parts[1] + 1}.0`; case 'patch': return `${parts[0]}.${parts[1]}.${parts[2] + 1}`; default: throw new Error(`无效的版本参数: ${versionArg}`); } } // 执行命令 function exec(command, options = {}) { log.info(`执行: ${command}`); try { return execSync(command, { stdio: options.silent ? 'pipe' : 'inherit', cwd: projectRoot, encoding: 'utf-8', ...options, }); } catch (error) { if (!options.ignoreError) { throw error; } return null; } } // 检查 Git 状态 function checkGitStatus() { log.step('检查 Git 状态...'); try { const status = execSync('git status --porcelain', { cwd: projectRoot, encoding: 'utf-8', }); if (status.trim()) { log.warn('工作目录有未提交的更改:'); console.log(status); return false; } log.success('Git 工作目录干净'); return true; } catch (error) { log.warn('无法检查 Git 状态'); return true; } } // 更新版本号 function updateVersion(newVersion, dryRun) { log.step(`更新版本号到 ${newVersion}...`); if (dryRun) { log.info('[DRY RUN] 将更新 package.json 版本号'); return; } const pkg = readPackageJson(); pkg.version = newVersion; writePackageJson(pkg); log.success(`版本号已更新: ${newVersion}`); } // 构建所有平台 function buildAllPlatforms(platforms, dryRun) { log.step('构建应用...'); for (const platform of platforms) { log.info(`构建 ${platform} 平台...`); if (dryRun) { log.info(`[DRY RUN] 将构建 ${platform} 平台`); continue; } exec(`node scripts/build.js -p ${platform}`); } log.success('所有平台构建完成'); } // 创建 Git tag function createGitTag(version, dryRun) { log.step(`创建 Git tag v${version}...`); if (dryRun) { log.info(`[DRY RUN] 将创建 tag: v${version}`); return; } // 提交版本更新 exec('git add package.json'); exec(`git commit -m "chore: release v${version}"`); // 创建 tag exec(`git tag -a v${version} -m "Release v${version}"`); log.success(`Git tag v${version} 已创建`); } // 推送到远程 function pushToRemote(version, dryRun) { log.step('推送到远程仓库...'); if (dryRun) { log.info('[DRY RUN] 将推送 commits 和 tags'); return; } exec('git push'); exec('git push --tags'); log.success('已推送到远程仓库'); } // 生成发布说明 function generateReleaseNotes(version) { log.step('生成发布说明...'); const releaseDir = path.join(projectRoot, 'release'); const notesPath = path.join(releaseDir, `RELEASE_NOTES_v${version}.md`); // 获取最近的 commits let commits = ''; try { commits = execSync('git log --oneline -20', { cwd: projectRoot, encoding: 'utf-8', }); } catch (e) { commits = '无法获取提交历史'; } // 获取构建产物列表 let artifacts = []; if (fs.existsSync(releaseDir)) { artifacts = fs.readdirSync(releaseDir).filter(f => { const ext = path.extname(f).toLowerCase(); return ['.exe', '.dmg', '.appimage', '.deb', '.rpm', '.zip'].includes(ext); }); } const notes = `# Claude AI Installer v${version} ## 发布日期 ${new Date().toISOString().split('T')[0]} ## 下载 ${artifacts.map(a => `- ${a}`).join('\n') || '暂无构建产物'} ## 平台支持 | 平台 | 文件格式 | 架构 | |------|----------|------| | Windows | NSIS 安装包 (.exe) | x64 | | macOS | DMG 镜像 (.dmg) | x64, arm64 | | Linux | AppImage (.AppImage) | x64 | ## 更新内容 请查看提交历史了解详细更新内容。 ## 最近提交 \`\`\` ${commits} \`\`\` ## 安装说明 ### Windows 1. 下载 \`.exe\` 安装包 2. 双击运行安装程序 3. 按照向导完成安装 ### macOS 1. 下载 \`.dmg\` 文件 2. 双击打开 DMG 镜像 3. 将应用拖拽到 Applications 文件夹 ### Linux 1. 下载 \`.AppImage\` 文件 2. 添加执行权限: \`chmod +x *.AppImage\` 3. 双击运行或在终端执行 --- 🤖 Generated with Claude AI Installer Build System `; fs.writeFileSync(notesPath, notes); log.success(`发布说明已生成: ${notesPath}`); } // 显示发布摘要 function showSummary(version, platforms) { const releaseDir = path.join(projectRoot, 'release'); console.log(` ${colors.bright}╔════════════════════════════════════════════╗ ║ 发布摘要 - v${version.padEnd(20)}║ ╚════════════════════════════════════════════╝${colors.reset} `); if (fs.existsSync(releaseDir)) { const files = fs.readdirSync(releaseDir).filter(f => { const ext = path.extname(f).toLowerCase(); return ['.exe', '.dmg', '.appimage', '.deb', '.rpm', '.zip'].includes(ext); }); if (files.length > 0) { console.log(`${colors.cyan}构建产物:${colors.reset}`); for (const file of files) { const filePath = path.join(releaseDir, file); const stats = fs.statSync(filePath); const sizeMB = (stats.size / (1024 * 1024)).toFixed(2); console.log(` ${colors.green}•${colors.reset} ${file} (${sizeMB} MB)`); } } } console.log(` ${colors.cyan}输出目录:${colors.reset} ${releaseDir} ${colors.cyan}版本号:${colors.reset} v${version} ${colors.cyan}构建平台:${colors.reset} ${platforms.join(', ')} `); } // 主函数 async function main() { const options = parseArgs(); if (options.help) { showHelp(); process.exit(0); } console.log(` ${colors.bright}${colors.magenta}╔════════════════════════════════════════════╗ ║ Claude AI Installer 发布脚本 ║ ╚════════════════════════════════════════════╝${colors.reset} `); if (options.dryRun) { log.warn('DRY RUN 模式 - 不会执行实际操作'); } const startTime = Date.now(); try { // 读取当前版本 const pkg = readPackageJson(); const currentVersion = pkg.version; log.info(`当前版本: ${currentVersion}`); // 计算新版本 const newVersion = calculateNewVersion(currentVersion, options.version); log.info(`目标版本: ${newVersion}`); // 检查 Git 状态 if (options.tag && !options.dryRun) { const isClean = checkGitStatus(); if (!isClean) { log.warn('建议先提交或暂存更改'); } } // 更新版本号 if (options.version) { updateVersion(newVersion, options.dryRun); } // 构建所有平台 buildAllPlatforms(options.platforms, options.dryRun); // 生成发布说明 if (!options.dryRun) { generateReleaseNotes(newVersion); } // 创建 Git tag if (options.tag) { createGitTag(newVersion, options.dryRun); } // 推送到远程 if (options.push) { pushToRemote(newVersion, options.dryRun); } // 显示摘要 if (!options.dryRun) { showSummary(newVersion, options.platforms); } const duration = ((Date.now() - startTime) / 1000).toFixed(1); log.success(`发布完成! 耗时: ${duration}s`); } catch (error) { log.error(error.message); process.exit(1); } } main();