#!/usr/bin/env node import fs from 'fs' import path from 'path' import { fileURLToPath } from 'url' import { execSync } from 'child_process' import readline from 'readline' // 获取当前脚本所在目录 const __filename = fileURLToPath(import.meta.url) const __dirname = path.dirname(__filename) // 读取package.json const packageJson = JSON.parse(fs.readFileSync(path.join(__dirname, 'package.json'), 'utf8')) const currentVersion = packageJson.version // 获取当前分支名 function getCurrentBranch() { try { return execSync('git rev-parse --abbrev-ref HEAD', { encoding: 'utf8' }).trim() } catch (error) { console.error('❌❌❌ getCurrentBranch Failed:', error.message) return 'unknown-branch' } } // 获取当前时间戳 function getTimestamp() { const now = new Date() // 精确到秒 + 3位随机数(1/1000概率),确保同一秒内多次构建也能生成唯一的tag return ( now.getFullYear() + String(now.getMonth() + 1).padStart(2, '0') + String(now.getDate()).padStart(2, '0') + String(now.getHours()).padStart(2, '0') + String(now.getMinutes()).padStart(2, '0') + String(now.getSeconds()).padStart(2, '0') + Math.floor(Math.random() * 1000) .toString() .padStart(3, '0') ) } // 验证版本号格式 function isValidVersion(version) { const versionRegex = /^(\d+)(\.(\d+))?(\.(\d+))?$/ return versionRegex.test(version) } // 比较版本号 function compareVersions(version1, version2) { const v1 = version1.split('.').map(Number) const v2 = version2.split('.').map(Number) for (let i = 0; i < Math.max(v1.length, v2.length); i++) { const num1 = v1[i] || 0 const num2 = v2[i] || 0 if (num1 > num2) return 1 if (num1 < num2) return -1 } return 0 } // 格式化日期为YYYY-MM-DD HH:mm:ss function formatDateTime(date) { const year = date.getFullYear() const month = String(date.getMonth() + 1).padStart(2, '0') const day = String(date.getDate()).padStart(2, '0') const hours = String(date.getHours()).padStart(2, '0') const minutes = String(date.getMinutes()).padStart(2, '0') const seconds = String(date.getSeconds()).padStart(2, '0') return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}` } // 创建版本信息文件 function createVersionInfoFile(version, type, branch, tag) { const versionInfo = { version: version, branch: branch, tag: tag, buildType: type, buildTime: formatDateTime(new Date()), } const distDir = path.join(__dirname, 'dist') // 确保dist目录存在 if (!fs.existsSync(distDir)) { fs.mkdirSync(distDir, { recursive: true }) } // 写入version.json文件 fs.writeFileSync(path.join(distDir, 'version.json'), JSON.stringify(versionInfo, null, 2)) console.log(`✅✅✅ version.json has been generated: ${path.join(distDir, 'version.json')}`) } // 处理测试构建 function handleTestBuild() { const branch = getCurrentBranch() const timestamp = getTimestamp() const tag = `test-${timestamp}` console.log( `🚀🚀🚀 A test version is being created... branchName:${branch},productionVersion:${currentVersion}` ) try { // 执行构建命令 execSync('vite build --mode test', { stdio: 'inherit' }) // 创建tag execSync(`git tag ${tag}`, { stdio: 'inherit' }) console.log(`✅✅✅ Test Version Build Success! Test Version: ${tag}`) // 生成版本信息文件 createVersionInfoFile(currentVersion, 'test', branch, tag) } catch (error) { console.error('❌❌❌ Test Version Build Failed:', error.message) process.exit(1) } } // 处理发布构建 function handlePublishBuild() { const rl = readline.createInterface({ input: process.stdin, output: process.stdout, }) rl.question(`请输入需要发布的新版本号(当前生产版本号: ${currentVersion}): `, (newVersion) => { newVersion = newVersion.trim() if (!isValidVersion(newVersion)) { console.error('无效的发布版本号格式,请使用x.x.x格式') rl.close() process.exit(1) } if (compareVersions(newVersion, currentVersion) <= 0) { console.error(`发布版本号必须大于当前版本(${currentVersion})`) rl.close() process.exit(1) } const branch = getCurrentBranch() const tag = `publish-v${newVersion}` console.log( `🚀🚀🚀 A publish version is being created... branchName:${branch}、productionVersion: ${newVersion}` ) try { // 更新package.json中的版本 packageJson.version = newVersion fs.writeFileSync(path.join(__dirname, 'package.json'), JSON.stringify(packageJson, null, 2)) // 执行构建命令 execSync('vite build', { stdio: 'inherit' }) // 创建tag execSync(`git tag ${tag}`, { stdio: 'inherit' }) console.log(`✅✅✅ Publish version ${tag} build success! Version number: ${newVersion}`) // 生成版本信息文件 createVersionInfoFile(newVersion, 'publish', branch, tag) } catch (error) { console.error('❌❌❌ Publish version build failed:', error.message) process.exit(1) } finally { rl.close() } }) } // 基于特定tag构建 测试与生产环境 版本 function handleBuildByTag(modeType) { if (modeType !== 'test' && modeType !== 'pro') { console.error('无效的构建类型,请确认构建类型为 test 或 pro') process.exit(1) } console.log(`🚀🚀🚀 正在基于${modeType}构建...`) const rl = readline.createInterface({ input: process.stdin, output: process.stdout, }) // 保存原始分支 const originalBranch = execSync('git rev-parse --abbrev-ref HEAD', { encoding: 'utf8' }).trim() rl.question('请输入要构建的tag名称(test-xxxx 或 publish-xxxx):', (tag) => { tag = tag.trim() try { // 检查tag是否存在 execSync(`git rev-parse ${tag}`, { stdio: 'ignore' }) console.log(`🚀🚀🚀 正在基于tag ${tag} 构建...`) // 切换到该tag execSync(`git checkout ${tag}`, { stdio: 'inherit' }) // 读取当前分支名 const branch = getCurrentBranch() // 读取package.json中的版本号 const pkgJson = JSON.parse(fs.readFileSync(path.join(__dirname, 'package.json'), 'utf8')) const version = pkgJson.version // 执行构建命令 if (modeType === 'test') { execSync('vite build --mode test', { stdio: 'inherit' }) } else if (modeType === 'pro') { execSync('vite build', { stdio: 'inherit' }) } // 生成版本信息文件 const buildType = tag.startsWith('test-') ? 'test' : 'publish' createVersionInfoFile(version, buildType, branch, tag) console.log(`✅✅✅ 基于tag ${tag} 构建成功!`) // 切换回原分支 execSync(`git checkout ${originalBranch}`, { stdio: 'inherit' }) console.log(`✅✅✅ 已切换回原分支: ${originalBranch}`) } catch (error) { console.error(`❌❌❌ 基于tag ${tag} 构建失败: ${error.message}`) // 即使构建失败也尝试切换回原分支 try { execSync(`git checkout ${originalBranch}`, { stdio: 'inherit' }) console.log(`✅✅✅ 已切换回原分支: ${originalBranch}`) } catch (checkoutError) { console.error(`❌❌❌ 切换回原分支失败,请手动处理: ${checkoutError.message}`) } process.exit(1) } finally { rl.close() } }) } // 主函数 function main() { const command = process.argv[2] switch (command) { case 'test': handleTestBuild() break case 'publish': handlePublishBuild() break case 'test-by-tag': handleBuildByTag('test') break case 'pro-by-tag': handleBuildByTag('pro') break default: console.log('用法: node version-script.js [test|publish|build-by-tag]') process.exit(1) } } main()