UNPKG

@hoseaguo/deploy

Version:

deploy project to server with node-ssh npm package

278 lines (240 loc) 8.15 kB
/* 部署流程 打包后: 通过ssh连接服务器 对打包文件里的所有文件到文件压缩 -> 压缩文件 上传到服务器,服务器进行解压 删除远程文件 删除本地文件 远程进行package.json依赖安装 pm2启动项目 */ const fs = require('fs') const path = require('path') import DraftLog from 'draftlog'; import archiver from 'archiver'; import { NodeSSH, Config } from "node-ssh"; import { exit } from 'process'; import chalk from 'chalk' DraftLog(console) const ssh = new NodeSSH(); interface Options extends Config { /* 本地打包路径 */ distPath?: string, /* 服务器部署路径 */ serverPath?: string, /* 是否安装npm包 */ installNpmPackage?: boolean, /* 打包文件名 */ zipFileame?: string, /* 清空文件 排除 */ cleanExclude?: string, /* pm2配置文件,假设存在的话,会在部署最后一步,进行 `pm2 startOrReload pm2进程配置文件名`,来重启pm2 服务 */ pm2ConfigFileName?: string, } let defaultOptions = { /* 本地打包路径 */ distPath: "./dist", /* 服务器部署路径 */ serverPath: "/data/www/_test", /* 是否安装npm包 */ installNpmPackage: false, /* 打包文件名 */ zipFileame: '_dist.zip', /* 打包发送到服务器的目录,会清空服务器上文件夹的内容,可以配置清空排除 正则表达式 */ cleanExclude: "node_modules|.*\.json", /* pm2配置文件,假设存在的话,会在部署最后一步,进行 `pm2 startOrReload pm2进程配置文件名`,来重启pm2 服务 */ pm2ConfigFileName: "" } let customOptions: Options = {}; // 压缩文件 async function compressZip() { const output = fs.createWriteStream('./' + customOptions.zipFileame); const archive = archiver('zip', { zlib: { level: 9 } // Sets the compression level. }); archive.pipe(output); archive.directory(customOptions.distPath!, false); archive.finalize(); // let update = console.draft("本地打包文件压缩:开始") let endLoaing = loadingLog('本地打包文件压缩') return new Promise((resolve, reject) => { archive.on('error', function (err) { console.log(err) reject(err) }); output.on('close', function () { endLoaing() resolve(true) }); }) } // 上传压缩文件 async function uploadZip() { let endLoaing = loadingLog('上传打包文件到远程') try { await ssh.putFile(customOptions.zipFileame!, `${customOptions.serverPath}/${customOptions.zipFileame}`) endLoaing() } catch (e) { console.log(e); endLoaing(false) } } // 移除服务器上原有文件 async function cleanServerDir() { let endLoaing = loadingLog('删除远程源文件') try { // 进入远程部署目录 await ssh.execCommand(`cd ${customOptions.serverPath}`, { cwd: customOptions.serverPath }); // 排除删除的文件,除了设置的,还会加上pm2 config 文件,假设存在 let cleanExclude = [customOptions.pm2ConfigFileName, customOptions.cleanExclude]; await ssh.execCommand(`rm \`find ./* |egrep -v '`+ cleanExclude!.join('|') +`'\` -rf`, { cwd: customOptions.serverPath }); } catch (e) { console.log(e) endLoaing(false); } endLoaing() } async function uncompressZip() { let endLoaing = loadingLog('解压远程压缩文件及删除') try { // 进入远程部署目录 await ssh.execCommand(`cd ${customOptions.serverPath}`, { cwd: customOptions.serverPath }); // 解压压缩文件 await ssh.execCommand(`unzip -o ${customOptions.zipFileame} -d .`, { cwd: customOptions.serverPath }); // 删除压缩文件 await ssh.execCommand(`rm ${customOptions.zipFileame}`, { cwd: customOptions.serverPath }); } catch (e) { console.log(e); endLoaing(false); } endLoaing(); } // 删除本地打包文件 const deleteLocalZip = async () => { return new Promise((resolve, reject) => { let endLoaing = loadingLog('删除本地zip包') fs.unlink(customOptions.zipFileame, (err: any) => { if (err) { console.log(err); endLoaing(false) } endLoaing(); resolve(true); }); }); }; async function installPackage() { let endLoaing = loadingLog('远程安装npm包') // 进入远程部署目录 ssh.execCommand(`cd ${customOptions.serverPath}`, { cwd: customOptions.serverPath }); // package npm 安装 try { await ssh.execCommand(`npm i`, { cwd: customOptions.serverPath }); // 试着加1秒延迟 await delay(1000); } catch (e) { console.log(e); endLoaing(false); } endLoaing(); } function loadingLog(str: string) { let startTimestamp = process.hrtime.bigint(); let frames = ['-', '\\', '|', '/'] let index = 0; let update = console.draft(str + frames[index]); let timer = setInterval(() => { index = (index + 1) % frames.length update(`[${frames[index]}] ${str}`); }, 50) return function end(isSuccess: boolean = true) { clearInterval(timer); if (isSuccess) { let endTimestamp = process.hrtime.bigint(); update(chalk.rgb(92, 175, 158)(`[√] ${str} `) + chalk.whiteBright(formatNs(endTimestamp - startTimestamp))); } else { update(chalk.red(`[×] ${str}`)); // 退出程序 exit(1); } } } function formatNs(nsTime: any) { // console.log(nsTime) nsTime = Number(nsTime); // console.log(nsTime) // 1542 928 800 if (nsTime < 1000) { // 100n return `${(nsTime / 1000).toFixed(3)}ms` } else if (nsTime < 1000 ** 2) { // 100 000n return `${(nsTime / 1000).toFixed(3)}ms` } else if (nsTime < 1000 ** 3) { // 100s 000m 000n return `${(nsTime / (1000 ** 3)).toFixed(3)}s` } else { return `${(nsTime / (1000 ** 3)).toFixed(3)}s` } } async function startOrReloadPm2(){ let endLoaing = loadingLog('start or reload pm2 by config file') try { // 进入远程部署目录 await ssh.execCommand(`cd ${customOptions.serverPath}`, { cwd: customOptions.serverPath }); // 重启pm2配置 await ssh.execCommand(`pm2 startOrReload ${customOptions.pm2ConfigFileName}`, { cwd: customOptions.serverPath }); } catch (e) { console.log(e); endLoaing(false); } endLoaing(); } async function delay(time: number){ return new Promise( resolve => { setTimeout( () => { resolve(true) }, time) }) } export default function deploy(options: Options) { let { distPath, serverPath, installNpmPackage, zipFileame, cleanExclude, pm2ConfigFileName, ...sshOptions } = options; customOptions = { distPath: distPath || defaultOptions.distPath, serverPath: serverPath || defaultOptions.serverPath, installNpmPackage: installNpmPackage !== undefined ? installNpmPackage : defaultOptions.installNpmPackage, zipFileame: zipFileame || defaultOptions.zipFileame, cleanExclude: cleanExclude || defaultOptions.cleanExclude, pm2ConfigFileName: pm2ConfigFileName || defaultOptions.pm2ConfigFileName }; let startDeployTimestamp = process.hrtime.bigint(); console.log(chalk.rgb(25, 181, 255)("开始部署>>>")) let sshLoading = loadingLog("ssh连接服务器"); ssh.connect(sshOptions).then(async function () { sshLoading(); await cleanServerDir(); await compressZip(); await uploadZip(); await deleteLocalZip(); await uncompressZip(); if (customOptions.installNpmPackage) { await installPackage(); } if(customOptions.pm2ConfigFileName){ await startOrReloadPm2(); } ssh.dispose(); //断开连接 let endDeployTimestamp = process.hrtime.bigint(); console.log(chalk.rgb(25, 181, 255)("<<<结束部署")); console.log(chalk.rgb(255, 189, 75)('Total time: ' + formatNs(endDeployTimestamp - startDeployTimestamp))) }).catch(e => { console.log(e) sshLoading(false); }) }