deploy.js 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. #!/usr/bin/env node
  2. import inquirer from 'inquirer'
  3. import { execSync } from 'child_process'
  4. import fs from 'fs'
  5. import { join } from 'path'
  6. import { tmpdir } from 'os'
  7. import archiver from 'archiver'
  8. import dayjs from 'dayjs'
  9. // ---------- 环境配置 ----------
  10. const ENV_CONFIG = {
  11. dev: { remoteUser: 'root', remoteIP: '192.168.2.140', remoteAppDir: '/work/web/dist' },
  12. test: { remoteUser: 'liujia', remoteIP: '43.137.10.199', remoteAppDir: '/work/web/dist' },
  13. prod: { remoteUser: 'root', remoteIP: '192.168.2.140', remoteAppDir: '/work/web/dist-prod' },
  14. }
  15. const SSH_PORT = 22
  16. const LOCAL_DIST = 'D:/project/ln-web/dist'
  17. // ---------- 处理环境参数 ----------
  18. let env = process.argv[2] // npm run deploy:dev 传入 dev/test/prod
  19. if (!env || !ENV_CONFIG[env]) {
  20. const answer = await inquirer.prompt([
  21. {
  22. type: 'list',
  23. name: 'env',
  24. message: '请选择要部署的环境:',
  25. choices: Object.keys(ENV_CONFIG),
  26. default: 'dev',
  27. },
  28. ])
  29. env = answer.env
  30. }
  31. const { remoteUser, remoteIP, remoteAppDir } = ENV_CONFIG[env]
  32. console.log(`\n你选择的环境是: ${env}, 远程目录: ${remoteAppDir}\n`)
  33. // ---------- 压缩 dist ----------
  34. const timestamp = dayjs().format('YYYYMMDDHHmmss')
  35. const zipFileName = `dist-${timestamp}.zip`
  36. const zipPath = join(tmpdir(), zipFileName)
  37. console.log('📦 正在压缩 dist...')
  38. if (!fs.existsSync(LOCAL_DIST)) {
  39. console.error(`❌ 本地 dist 目录不存在: ${LOCAL_DIST}`)
  40. process.exit(1)
  41. }
  42. await new Promise((resolve, reject) => {
  43. const output = fs.createWriteStream(zipPath)
  44. const archive = archiver('zip', { zlib: { level: 9 } })
  45. output.on('close', () => resolve())
  46. archive.on('error', (err) => reject(err))
  47. archive.pipe(output)
  48. archive.directory(LOCAL_DIST, false)
  49. archive.finalize()
  50. })
  51. const zipSizeMB = (fs.statSync(zipPath).size / 1024 / 1024).toFixed(2)
  52. console.log(`✅ 压缩完成: ${zipSizeMB} MB, 文件: ${zipPath}`)
  53. // ---------- 上传压缩包 ----------
  54. const remoteZip = `/tmp/dist-latest-${timestamp}.zip`
  55. execSync(`scp -P ${SSH_PORT} "${zipPath}" ${remoteUser}@${remoteIP}:${remoteZip}`, {
  56. stdio: 'inherit',
  57. })
  58. // ---------- 生成远程部署脚本 ----------
  59. const useSudo = remoteUser !== 'root' ? 'sudo ' : ''
  60. const remoteScriptContent = `#!/bin/bash
  61. set -e
  62. timestamp="${timestamp}"
  63. tmpdir="/tmp/dist-temp-$timestamp"
  64. target="${remoteAppDir}"
  65. zipfile="${remoteZip}"
  66. echo "📦 正在服务器上原子部署 ..."
  67. echo "📁 创建临时目录 $tmpdir"
  68. mkdir -p "$tmpdir"
  69. echo "📦 解压上传文件"
  70. unzip -qo "$zipfile" -d "$tmpdir"
  71. echo "🔒 设置权限"
  72. ${useSudo}chown -R root:root "$tmpdir"
  73. echo "🚀 替换新目录"
  74. ${useSudo}rm -rf "$target"
  75. ${useSudo}mv "$tmpdir" "$target"
  76. echo "🧹 删除压缩包"
  77. rm -f "$zipfile"
  78. echo "✅ 远程部署完成: $target 更新成功"
  79. `
  80. const localScriptPath = join(tmpdir(), `deploy-temp-${timestamp}.sh`)
  81. fs.writeFileSync(localScriptPath, remoteScriptContent, { encoding: 'utf8' })
  82. // ---------- 上传远程脚本 ----------
  83. const remoteScriptPath = `/tmp/deploy-temp-${timestamp}.sh`
  84. execSync(`scp -P ${SSH_PORT} "${localScriptPath}" ${remoteUser}@${remoteIP}:${remoteScriptPath}`, {
  85. stdio: 'inherit',
  86. })
  87. // ---------- 执行远程脚本 ----------
  88. console.log('\n🚀 执行远程部署脚本...')
  89. try {
  90. execSync(
  91. `ssh -p ${SSH_PORT} ${remoteUser}@${remoteIP} "chmod +x ${remoteScriptPath} && bash ${remoteScriptPath}"`,
  92. {
  93. stdio: 'inherit',
  94. }
  95. )
  96. } catch (err) {
  97. console.error('❌ 远程部署失败,请检查远程日志和权限', err)
  98. process.exit(1)
  99. }
  100. // ---------- 清理本地/远程临时文件 ----------
  101. console.log('\n🧹 清理本地/远程临时文件...')
  102. fs.unlinkSync(zipPath)
  103. fs.unlinkSync(localScriptPath)
  104. execSync(`ssh -p ${SSH_PORT} ${remoteUser}@${remoteIP} "rm -f ${remoteScriptPath} ${remoteZip}"`, {
  105. stdio: 'inherit',
  106. })
  107. console.log(`\n✅ [${env}] 环境部署完成!`)