Procházet zdrojové kódy

设置版本号并自动提交

黄中银 před 2 týdny
rodič
revize
0df7925777
2 změnil soubory, kde provedl 318 přidání a 130 odebrání
  1. 317 0
      scripts/set-version.mjs
  2. 1 130
      set-version.bat

+ 317 - 0
scripts/set-version.mjs

@@ -0,0 +1,317 @@
+import fs from 'fs'
+import path from 'path'
+import { fileURLToPath } from 'url'
+import { execSync } from 'child_process'
+import readline from 'readline'
+
+const __dirname = path.dirname(fileURLToPath(import.meta.url))
+const projectRoot = path.dirname(__dirname)
+process.chdir(projectRoot)
+
+// 读取当前版本
+function getCurrentVersion() {
+  const pkg = JSON.parse(fs.readFileSync('package.json', 'utf-8'))
+  return pkg.version
+}
+
+let currentVersion = getCurrentVersion()
+
+// 清屏并显示标题
+function showHeader() {
+  console.log('')
+  console.log('+--------------------------------------------+')
+  console.log('|         版本号设置工具                     |')
+  console.log('|         Version Bump Tool                  |')
+  console.log('+--------------------------------------------+')
+  console.log('')
+  console.log(`当前版本: ${currentVersion}`)
+  console.log('')
+  console.log('支持的输入格式:')
+  console.log('  - 具体版本号: 1.0.0, 2.1.3')
+  console.log('  - patch: 补丁版本 +1 (如 0.0.6 -> 0.0.7)')
+  console.log('  - minor: 次版本 +1   (如 0.0.6 -> 0.1.0)')
+  console.log('  - major: 主版本 +1   (如 0.0.6 -> 1.0.0)')
+  console.log('  - 按 q 键退出')
+  console.log('')
+}
+
+// 单键读取
+function readKey() {
+  return new Promise((resolve) => {
+    const wasRaw = process.stdin.isRaw
+    readline.emitKeypressEvents(process.stdin)
+    if (process.stdin.isTTY) {
+      process.stdin.setRawMode(true)
+    }
+    process.stdin.once('keypress', (ch, key) => {
+      if (process.stdin.isTTY) {
+        process.stdin.setRawMode(wasRaw)
+      }
+      resolve({ ch, key })
+    })
+  })
+}
+
+// 读取输入,支持单键 q 退出
+async function readInput(prompt) {
+  process.stdout.write(prompt)
+  let input = ''
+
+  readline.emitKeypressEvents(process.stdin)
+  if (process.stdin.isTTY) {
+    process.stdin.setRawMode(true)
+  }
+
+  return new Promise((resolve) => {
+    const onKeypress = (ch, key) => {
+      if (key && key.name === 'return') {
+        process.stdin.removeListener('keypress', onKeypress)
+        if (process.stdin.isTTY) process.stdin.setRawMode(false)
+        console.log('')
+        resolve(input)
+      } else if (key && key.name === 'escape') {
+        process.stdin.removeListener('keypress', onKeypress)
+        if (process.stdin.isTTY) process.stdin.setRawMode(false)
+        console.log('')
+        resolve('__EXIT__')
+      } else if (key && key.name === 'backspace') {
+        if (input.length > 0) {
+          input = input.slice(0, -1)
+          process.stdout.write('\b \b')
+        }
+      } else if (key && key.ctrl && key.name === 'c') {
+        process.stdin.removeListener('keypress', onKeypress)
+        if (process.stdin.isTTY) process.stdin.setRawMode(false)
+        console.log('')
+        process.exit(0)
+      } else if (ch && /[a-zA-Z0-9.\-]/.test(ch)) {
+        input += ch
+        process.stdout.write(ch)
+        // 单独输入 q 立即退出
+        if (input === 'q' || input === 'Q') {
+          process.stdin.removeListener('keypress', onKeypress)
+          if (process.stdin.isTTY) process.stdin.setRawMode(false)
+          console.log('')
+          resolve('__EXIT__')
+        }
+      }
+    }
+    process.stdin.on('keypress', onKeypress)
+  })
+}
+
+// 读取 Y/N
+async function readYesNo(prompt, defaultYes = true) {
+  const hint = defaultYes ? 'Y/n' : 'y/N'
+  process.stdout.write(`${prompt} (${hint}): `)
+
+  readline.emitKeypressEvents(process.stdin)
+  if (process.stdin.isTTY) {
+    process.stdin.setRawMode(true)
+  }
+
+  return new Promise((resolve) => {
+    const onKeypress = (ch, key) => {
+      if (key && key.name === 'return') {
+        process.stdin.removeListener('keypress', onKeypress)
+        if (process.stdin.isTTY) process.stdin.setRawMode(false)
+        console.log(defaultYes ? 'y' : 'n')
+        resolve(defaultYes)
+      } else if (ch === 'y' || ch === 'Y') {
+        process.stdin.removeListener('keypress', onKeypress)
+        if (process.stdin.isTTY) process.stdin.setRawMode(false)
+        console.log('y')
+        resolve(true)
+      } else if (ch === 'n' || ch === 'N') {
+        process.stdin.removeListener('keypress', onKeypress)
+        if (process.stdin.isTTY) process.stdin.setRawMode(false)
+        console.log('n')
+        resolve(false)
+      } else if (key && key.ctrl && key.name === 'c') {
+        process.stdin.removeListener('keypress', onKeypress)
+        if (process.stdin.isTTY) process.stdin.setRawMode(false)
+        console.log('')
+        process.exit(0)
+      }
+    }
+    process.stdin.on('keypress', onKeypress)
+  })
+}
+
+// 等待按键继续或退出
+async function waitForContinueOrExit() {
+  process.stdout.write('按 Enter 继续修改,按 q 退出...')
+
+  readline.emitKeypressEvents(process.stdin)
+  if (process.stdin.isTTY) {
+    process.stdin.setRawMode(true)
+  }
+
+  return new Promise((resolve) => {
+    const onKeypress = (ch, key) => {
+      if (key && key.name === 'return') {
+        process.stdin.removeListener('keypress', onKeypress)
+        if (process.stdin.isTTY) process.stdin.setRawMode(false)
+        console.log('\n')
+        resolve('continue')
+      } else if (ch === 'q' || ch === 'Q') {
+        process.stdin.removeListener('keypress', onKeypress)
+        if (process.stdin.isTTY) process.stdin.setRawMode(false)
+        console.log('\n')
+        resolve('exit')
+      } else if (key && key.ctrl && key.name === 'c') {
+        process.stdin.removeListener('keypress', onKeypress)
+        if (process.stdin.isTTY) process.stdin.setRawMode(false)
+        console.log('')
+        process.exit(0)
+      }
+    }
+    process.stdin.on('keypress', onKeypress)
+  })
+}
+
+// 验证版本号格式
+function validateVersion(input) {
+  const keywords = ['patch', 'minor', 'major']
+  if (keywords.includes(input.toLowerCase())) return true
+  return /^\d+\.\d+\.\d+$/.test(input)
+}
+
+// 获取新版本号(dry-run)
+function getNewVersion(input) {
+  try {
+    const result = execSync(`node scripts/bump-version.mjs ${input} --dry-run`, {
+      encoding: 'utf-8',
+      stdio: ['pipe', 'pipe', 'pipe']
+    })
+    return result.trim()
+  } catch {
+    return null
+  }
+}
+
+// 执行版本更新
+function updateVersion(input) {
+  try {
+    execSync(`node scripts/bump-version.mjs ${input}`, {
+      encoding: 'utf-8',
+      stdio: 'inherit'
+    })
+    return true
+  } catch {
+    return false
+  }
+}
+
+// Git 提交
+function gitCommit(version) {
+  try {
+    execSync('git add package.json src-tauri/tauri.conf.json src-tauri/Cargo.toml src-tauri/Cargo.lock', {
+      encoding: 'utf-8',
+      stdio: 'inherit'
+    })
+    execSync(`git commit -m "v${version}"`, {
+      encoding: 'utf-8',
+      stdio: 'inherit'
+    })
+    return true
+  } catch {
+    return false
+  }
+}
+
+// 主循环
+async function main() {
+  showHeader()
+
+  while (true) {
+    const versionInput = await readInput('请输入新版本号: ')
+
+    if (versionInput === '__EXIT__') {
+      console.log('')
+      console.log('再见!')
+      process.exit(0)
+    }
+
+    if (!versionInput.trim()) {
+      console.log('[错误] 版本号不能为空,请重新输入')
+      console.log('')
+      continue
+    }
+
+    if (!validateVersion(versionInput)) {
+      console.log('[错误] 无效的版本号格式,请输入 x.y.z 格式或 patch/minor/major')
+      console.log('')
+      continue
+    }
+
+    const newVersion = getNewVersion(versionInput)
+    if (!newVersion) {
+      console.log('[错误] 无法计算新版本号,请检查输入')
+      console.log('')
+      continue
+    }
+
+    console.log('')
+    console.log('+--------------------------------------------+')
+    console.log('|  版本变更预览                              |')
+    console.log('+--------------------------------------------+')
+    console.log(`|  当前版本: ${currentVersion}`)
+    console.log(`|  新版本:   ${newVersion}`)
+    console.log('+--------------------------------------------+')
+    console.log('')
+
+    const confirm = await readYesNo('确认更新版本号?', false)
+
+    if (!confirm) {
+      console.log('')
+      console.log('已取消,请重新输入版本号')
+      console.log('')
+      continue
+    }
+
+    console.log('')
+    console.log('正在更新版本号...')
+    console.log('')
+
+    if (!updateVersion(versionInput)) {
+      console.log('')
+      console.log('[错误] 版本号更新失败')
+      continue
+    }
+
+    currentVersion = newVersion
+
+    console.log('')
+    console.log('============================================')
+    console.log(`  版本号已成功更新到 ${newVersion}`)
+    console.log('============================================')
+    console.log('')
+
+    const gitChoice = await readYesNo('是否将修改提交到 Git?', true)
+
+    if (gitChoice) {
+      console.log('')
+      console.log('正在提交到 Git...')
+      if (gitCommit(newVersion)) {
+        console.log('')
+        console.log(`[成功] 已提交到 Git,提交消息: v${newVersion}`)
+      } else {
+        console.log('')
+        console.log('[警告] Git 提交失败,可能没有变更或发生错误')
+      }
+    } else {
+      console.log('')
+      console.log('已跳过 Git 提交')
+    }
+
+    console.log('')
+    const action = await waitForContinueOrExit()
+    if (action === 'exit') {
+      console.log('再见!')
+      process.exit(0)
+    }
+  }
+}
+
+main().catch(console.error)

+ 1 - 130
set-version.bat

@@ -1,133 +1,4 @@
 @echo off
 chcp 65001 >nul
-setlocal enabledelayedexpansion
-
-:: 切换到脚本所在目录(项目根目录)
 cd /d "%~dp0"
-
-echo.
-echo +--------------------------------------------+
-echo ^|         版本号设置工具                     ^|
-echo ^|         Version Bump Tool                  ^|
-echo +--------------------------------------------+
-echo.
-
-:: 获取当前版本号(从 package.json 读取)
-for /f "tokens=2 delims=:, " %%a in ('findstr /c:"\"version\"" package.json') do (
-    set "current_version=%%~a"
-    goto :got_version
-)
-:got_version
-
-echo 当前版本: %current_version%
-echo.
-echo 支持的输入格式:
-echo   - 具体版本号: 1.0.0, 2.1.3
-echo   - patch: 补丁版本 +1 (如 0.0.6 -^> 0.0.7)
-echo   - minor: 次版本 +1   (如 0.0.6 -^> 0.1.0)
-echo   - major: 主版本 +1   (如 0.0.6 -^> 1.0.0)
-echo.
-
-:input_loop
-set "version_input="
-set /p "version_input=请输入新版本号 (q 退出): "
-
-:: 检查是否退出
-if /i "%version_input%"=="q" goto exit_script
-if /i "%version_input%"=="quit" goto exit_script
-if /i "%version_input%"=="exit" goto exit_script
-
-:: 检查是否为空
-if "%version_input%"=="" (
-    echo [错误] 版本号不能为空,请重新输入
-    echo.
-    goto input_loop
-)
-
-:: 检查是否为 patch/minor/major
-if /i "%version_input%"=="patch" goto validate_version
-if /i "%version_input%"=="minor" goto validate_version
-if /i "%version_input%"=="major" goto validate_version
-
-:: 验证版本号格式 (x.y.z)
-echo %version_input%| findstr /r "^[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*$" >nul
-if errorlevel 1 (
-    echo [错误] 无效的版本号格式,请输入 x.y.z 格式或 patch/minor/major
-    echo.
-    goto input_loop
-)
-
-:validate_version
-:: 调用 bump-version.mjs --dry-run 获取计算后的版本号
-for /f "delims=" %%i in ('node scripts/bump-version.mjs %version_input% --dry-run 2^>nul') do set "new_version=%%i"
-
-if "%new_version%"=="" (
-    echo [错误] 无法计算新版本号,请检查输入
-    echo.
-    goto input_loop
-)
-
-echo.
-echo +--------------------------------------------+
-echo ^|  版本变更预览                              ^|
-echo +--------------------------------------------+
-echo ^|  当前版本: %current_version%
-echo ^|  新版本:   %new_version%
-echo +--------------------------------------------+
-echo.
-
-:confirm_loop
-set "confirm="
-set /p "confirm=确认更新版本号? (y/n): "
-
-if /i "%confirm%"=="y" goto do_update
-if /i "%confirm%"=="yes" goto do_update
-if /i "%confirm%"=="n" goto cancel_update
-if /i "%confirm%"=="no" goto cancel_update
-
-echo [提示] 请输入 y 或 n
-goto confirm_loop
-
-:cancel_update
-echo.
-echo 已取消,请重新输入版本号
-echo.
-goto input_loop
-
-:do_update
-echo.
-echo 正在更新版本号...
-echo.
-node scripts/bump-version.mjs %version_input%
-
-if errorlevel 1 (
-    echo.
-    echo [错误] 版本号更新失败
-    pause
-    exit /b 1
-)
-
-:: 更新当前版本号变量
-set "current_version=%new_version%"
-
-echo.
-echo ============================================
-echo   版本号已成功更新到 %new_version%
-echo ============================================
-echo.
-
-:after_update_loop
-set "after_choice="
-set /p "after_choice=按 Enter 继续修改,输入 q 退出: "
-
-if "%after_choice%"=="" goto input_loop
-if /i "%after_choice%"=="q" goto exit_script
-if /i "%after_choice%"=="quit" goto exit_script
-if /i "%after_choice%"=="exit" goto exit_script
-
-goto input_loop
-
-:exit_script
-echo.
-echo 再见!
-exit /b 0
+node scripts/set-version.mjs