| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126 | #!/usr/bin/env nodeimport inquirer from 'inquirer'import { execSync } from 'child_process'import fs from 'fs'import { join } from 'path'import { tmpdir } from 'os'import archiver from 'archiver'import dayjs from 'dayjs'// ---------- 环境配置 ----------const ENV_CONFIG = {  dev: { remoteUser: 'root', remoteIP: '192.168.2.140', remoteAppDir: '/work/web/dist' },  test: { remoteUser: 'liujia', remoteIP: '43.137.10.199', remoteAppDir: '/work/web/dist' },  prod: { remoteUser: 'root', remoteIP: '192.168.2.140', remoteAppDir: '/work/web/dist-prod' },}const SSH_PORT = 22const LOCAL_DIST = 'D:/project/ln-web/dist'// ---------- 处理环境参数 ----------let env = process.argv[2] // npm run deploy:dev 传入 dev/test/prodif (!env || !ENV_CONFIG[env]) {  const answer = await inquirer.prompt([    {      type: 'list',      name: 'env',      message: '请选择要部署的环境:',      choices: Object.keys(ENV_CONFIG),      default: 'dev',    },  ])  env = answer.env}const { remoteUser, remoteIP, remoteAppDir } = ENV_CONFIG[env]console.log(`\n你选择的环境是: ${env}, 远程目录: ${remoteAppDir}\n`)// ---------- 压缩 dist ----------const timestamp = dayjs().format('YYYYMMDDHHmmss')const zipFileName = `dist-${timestamp}.zip`const zipPath = join(tmpdir(), zipFileName)console.log('📦 正在压缩 dist...')if (!fs.existsSync(LOCAL_DIST)) {  console.error(`❌ 本地 dist 目录不存在: ${LOCAL_DIST}`)  process.exit(1)}await new Promise((resolve, reject) => {  const output = fs.createWriteStream(zipPath)  const archive = archiver('zip', { zlib: { level: 9 } })  output.on('close', () => resolve())  archive.on('error', (err) => reject(err))  archive.pipe(output)  archive.directory(LOCAL_DIST, false)  archive.finalize()})const zipSizeMB = (fs.statSync(zipPath).size / 1024 / 1024).toFixed(2)console.log(`✅ 压缩完成: ${zipSizeMB} MB, 文件: ${zipPath}`)// ---------- 上传压缩包 ----------const remoteZip = `/tmp/dist-latest-${timestamp}.zip`execSync(`scp -P ${SSH_PORT} "${zipPath}" ${remoteUser}@${remoteIP}:${remoteZip}`, {  stdio: 'inherit',})// ---------- 生成远程部署脚本 ----------const useSudo = remoteUser !== 'root' ? 'sudo ' : ''const remoteScriptContent = `#!/bin/bashset -etimestamp="${timestamp}"tmpdir="/tmp/dist-temp-$timestamp"target="${remoteAppDir}"zipfile="${remoteZip}"echo "📦 正在服务器上原子部署 ..."echo "📁 创建临时目录 $tmpdir"mkdir -p "$tmpdir"echo "📦 解压上传文件"unzip -qo "$zipfile" -d "$tmpdir"echo "🔒 设置权限"${useSudo}chown -R root:root "$tmpdir"echo "🚀 替换新目录"${useSudo}rm -rf "$target"${useSudo}mv "$tmpdir" "$target"echo "🧹 删除压缩包"rm -f "$zipfile"echo "✅ 远程部署完成: $target 更新成功"`const localScriptPath = join(tmpdir(), `deploy-temp-${timestamp}.sh`)fs.writeFileSync(localScriptPath, remoteScriptContent, { encoding: 'utf8' })// ---------- 上传远程脚本 ----------const remoteScriptPath = `/tmp/deploy-temp-${timestamp}.sh`execSync(`scp -P ${SSH_PORT} "${localScriptPath}" ${remoteUser}@${remoteIP}:${remoteScriptPath}`, {  stdio: 'inherit',})// ---------- 执行远程脚本 ----------console.log('\n🚀 执行远程部署脚本...')try {  execSync(    `ssh -p ${SSH_PORT} ${remoteUser}@${remoteIP} "chmod +x ${remoteScriptPath} && bash ${remoteScriptPath}"`,    {      stdio: 'inherit',    }  )} catch (err) {  console.error('❌ 远程部署失败,请检查远程日志和权限', err)  process.exit(1)}// ---------- 清理本地/远程临时文件 ----------console.log('\n🧹 清理本地/远程临时文件...')fs.unlinkSync(zipPath)fs.unlinkSync(localScriptPath)execSync(`ssh -p ${SSH_PORT} ${remoteUser}@${remoteIP} "rm -f ${remoteScriptPath} ${remoteZip}"`, {  stdio: 'inherit',})console.log(`\n✅ [${env}] 环境部署完成!`)
 |