UNPKG

yw-auto-deploy-tool

Version:

一个自动打包、上传并部署的脚本工具

142 lines (120 loc) 4.65 kB
// lib/index.js const fs = require('fs'); const path = require('path'); const archiver = require('archiver'); const ClientSFtp = require('ssh2-sftp-client'); const { Client } = require('ssh2'); const ProgressBar = require('progress'); const sftp = new ClientSFtp(); function resolveConfig(configPath) { if (!fs.existsSync(configPath)) { throw new Error(`未找到配置文件: ${configPath}`); } return require(configPath); } async function zipDist(localDistDir, localZipPath) { const zipPath = path.resolve(localZipPath); if (fs.existsSync(zipPath)) { fs.unlinkSync(zipPath); } const output = fs.createWriteStream(zipPath); const archive = archiver('zip', { zlib: { level: 9 } }); archive.pipe(output); archive.directory(localDistDir + '/', false); await archive.finalize(); return new Promise((resolve, reject) => { output.on('close', () => { const sizeMB = (archive.pointer() / 1_000_000).toFixed(2); console.log(`✅ dist.zip 打包完成,大小约 ${sizeMB} MB`); resolve(zipPath); }); archive.on('error', err => reject(err)); }); } async function uploadToSFTP(zipPath, remoteZipPath, sshConfig) { try { console.log(sshConfig) console.log(remoteZipPath, 'remoteZipPath') console.log(zipPath, 'zipPath') await sftp.connect(sshConfig); console.log('📡 已连接 SFTP,开始上传...'); const fileSize = fs.statSync(zipPath).size; const bar = new ProgressBar('📤 上传进度 [:bar] :percent :etas', { complete: '=', incomplete: ' ', width: 40, total: fileSize, }); let lastTransferred = 0; await sftp.fastPut(zipPath, remoteZipPath, { step: (transferred) => { const delta = transferred - lastTransferred; lastTransferred = transferred; bar.tick(delta); }, }); console.log('\n✅ 上传成功!'); } catch (err) { throw new Error('上传失败: ' + err.message); } finally { await sftp.end(); } } function execCommandOnServer(sshConfig, command) { return new Promise((resolve, reject) => { const conn = new Client(); conn.on('ready', () => { conn.exec(command, (err, stream) => { if (err) return reject(err); let stdout = '', stderr = ''; stream.on('close', (code) => { conn.end(); if (code === 0) resolve({ stdout, stderr }); else reject(new Error(`命令失败: ${stderr}`)); }); stream.on('data', data => stdout += data.toString()); stream.stderr.on('data', data => stderr += data.toString()); }); }).on('error', reject).connect(sshConfig); }); } async function backupRemoteDir(sshConfig, remoteDir) { const dateStr = new Date().toISOString().replace(/[:.]/g, '_'); const backupDir = `${remoteDir}_backup_${dateStr}`; try { await execCommandOnServer(sshConfig, `mv ${remoteDir} ${backupDir}`); console.log(`✅ 已备份 ${remoteDir} -> ${backupDir}`); } catch (err) { console.warn('⚠️ 远程目录备份失败(可能原目录不存在):', err.message); } } async function execUnzipOnServer(sshConfig, zipPath, targetDir) { await backupRemoteDir(sshConfig, targetDir); const cmd = `unzip -o ${zipPath} -d ${targetDir}`; await execCommandOnServer(sshConfig, cmd); console.log('✅ 解压完成'); } function cleanLocalBuild(localDistDir, localZipPath) { try { if (fs.existsSync(localZipPath)) fs.unlinkSync(localZipPath); if (fs.existsSync(localDistDir)) fs.rmSync(localDistDir, { recursive: true, force: true }); console.log('🧹 已清理本地构建资源'); } catch (err) { console.warn('❌ 清理失败:', err.message); } } async function deploy({ configPath }) { const config = resolveConfig(configPath); const { sshConfig, paths } = config; const zipPath = await zipDist(paths.localDistDir, paths.localZipPath); await uploadToSFTP(zipPath, paths.remoteZipPath, sshConfig); await execUnzipOnServer(sshConfig, paths.remoteZipPath, paths.remoteUnzipDir); cleanLocalBuild(paths.localDistDir, paths.localZipPath); console.log('🚀 全部流程执行完毕!'); } module.exports = { deploy, zipDist, uploadToSFTP, execUnzipOnServer, cleanLocalBuild, execCommandOnServer, backupRemoteDir, };