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