| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461 |
- #!/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();
|