#!/usr/bin/env node import 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 = 22 const LOCAL_DIST = 'D:/project/ln-web/dist' // ---------- 处理环境参数 ---------- let env = process.argv[2] // npm run deploy:dev 传入 dev/test/prod if (!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/bash set -e timestamp="${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}] 环境部署完成!`)