为什么要写这个?只有一个道理=>懒惰!!!想直接用的就不用看后面的github传送门了--记得留个star,比信目前在做IOT项目,由于历史原因,react还是很多的/vue平台上的纯H5子项目,这些项目还需要调用一些APP暴露的API,导致本地开发调试时需要重复构建,手动部署构建包到开发服务器。几个项目,几轮测试下来,还是挺累的。于是就想能不能写一个工具,执行yarnbuild构建包后会自动部署到服务器上,这样就可以保存了:打开ftp工具->找到构建目录->找到服务器上的部署目录->备份->粘贴复制的文件,这一套繁琐的手工流程提高了工作(mo)工作(yu)的效率。而且,有了这个,在jenkins自动构建的时候,项目配置的shell脚本也可以省下几行代码。如何实现?思路分析首先分析一下要实现的结果:我个人比较喜欢用yarn来执行yarnbuild,用npm的同学可以自己替换。假设项目都是基于createreactapp||vue-cli脚手架,如果是自己自定义的脚手架,请继续往下看。本文以创建ReactApp脚手架为例。工具自动触发登录远程服务器,建立连接,备份原项目。从本地build目录(本文为项目根目录下的build目录)复制所有文件,上传到远程服务器部署目录。部署完成。输出提示,关闭远程连接二、分析如何实现结果:在前端项目中编写工具,首选node.js工具需要在执行yarnbuild后自动触发,这里需要使用hookpostbuildinnpmscripts,它会执行build做一些收尾工作后,就可以用来触发部署操作了。对npmscripts不是很了解的同学,可以参考《阮一峰 npm scripts 使用指南》与服务器建立连接并进行操作。您需要建立ssh连接。推荐使用第三方库ssh2进行备份。最好在文件名中加上具体的时间。例如:my_app.bak2019-10-8-14:36:36。为了方便,直接使用一个轻量级的时间库moment来安装ssh2和momentyarnaddssh2moment为了方便,在项目根目录下新建一个deploy.js。这个js文件就是本次编写的自动化部署工具deploy.js。它不必位于项目的根目录中。如果你熟悉node查找路径的方式,可以将它放在你指定的路径下打开package.json文件,在scripts中添加:"postbuild":"yarnrundeploy","deploy":"node./deploy.js”,此时如果在deploy.js中加入console.log('---deploytest--'),控制台执行yarnbuild,可以看到构建完成后,yarnrundeploy会继续执行,最后控制台输出:---deploytest--根据ssh2文档,编写连接服务器、备份文件、上传文件的代码:constpath=require('path')constmoment=require('moment')constutil=require('util')constevents=require('events')constClient=require('ssh2').Clientconstfs=require('fs')/***************************************************************************************//********************************请手动配置以下***********************************//*******************************************************************************************//***远程服务器配置*@type{{password:string,port:number,host:string,username:string}}*/constserver={host:'xxx.xxx.xxx.xxx',//hostipport:22,//SSH连接端口username:'xxxx',//用户名password:'xxxxxxx',//用户登录密码}constbaseDir='my_app'//项目目录constbasePath='/home'//项目部署目录constbakDirName=baseDir+'.bak'+moment(newDate().format('YYYY-M-D-HH:mm:ss')//备份文件名constbuildPath=path.resolve('./build')//本地工程编译文件目录/*******************************************************************************************//****************************************配置结束******************************************//***************************************************************************************/函数doConnect(服务器,then){constconn=newClient()conn.on('ready',function(){then&&then(conn)}).on('error',function(err){console.error('连接错误!',err)}).on('close',function(){conn.end()}).connect(server)}functiondoShell(server,cmd,then){doConnect(server,function(conn){conn.shell(function(err,stream){if(err)throwerrelse{letbuf=''流。on('关闭',函数(){conn.end()then&&then(err,buf)}).on('data',function(data){buf=buf+data}).stderr.on('data',function(data){console.log('stderr:'+data)})stream.end(cmd)}})})}functiondoGetFileAndDirList(localDir,dirs,files){constdir=fs.readdirSync(localDir)for(leti=0;i
0){constfunc=todos.shift()func(function(err,result){if(err){then(err)throwerr}else{control.emit('doNext',todos,然后)}})}else{then(null)}})functiondoUploadFile(server,localPath,remotePath,then){doConnect(server,function(conn){conn.sftp(function(err,sftp){if(err){then(err)}else{sftp.fastPut(localPath,remotePath,function(err,result){conn.end()then(err,result)})}})})}functiondoUploadDir(server,localDir,remoteDir,then){letdirs=[]letfiles=[]doGetFileAndDirList(localDir,dirs,files)//创建远程目录lettodoDir=[]dirs.forEach(function(dir){todoDir.push(function(done){constto=path.join(remoteDir,dir.slice(localDir.length+1)).replace(/[\\]/g,'/')constcmd='mkdir-p'+to+'\r\nexit\r\n'console.log(`cmd::${cmd}`)doShell(server,cmd,done)})//推送结束})//上传文件lettodoFile=[]files.forEach(function(file){todoFile.push(function(done){constto=path.join(remoteDir,file.slice(localDir.length+1)).replace(/[\\]/g,'/')console.log('上传'+to)doUploadFile(server,file,to,done)})})控制。emit('doNext',todoDir,function(err){if(err){throwerr}else{control.emit('doNext',todoFile,then)}})}控制台。log('--------部署配置------------')console.log(`serverhost:${server.host}`)console.log(`项目文件夹:${baseDir}`)console.log(`项目部署及备份目录:${basePath}`)console.log(`备份文件夹名称:${bakDirName}`)console.log('--------deploystart------------')doShell(server,`mv${basePath}/${baseDir}${basePath}/${bakDirName}\nexit\n`)doUploadDir(server,buildPath,`${basePath}/${baseDir}`,()=>console.log('------部署结束--------------'))运行结果示例脚本触发时,运行yarnbuild后,会自动触发lifecyclehookpostbuild进行部署。这个过程会先将打包好的项目在本地构建到配置的buildPath目录下,然后在远程服务器上运行xxx.xxx.xxx.xxx的/home中,备份my_app为my_app.bak2019-10-8-23:06:27,最后将本地的buildPath目录文件全部上传到/home/my_app,完成部署。$yarnrundeploy$node./deploy.js------deployconfig----------------服务器主机:xxx.xxx.xxx.xxx项目文件夹:my_app项目部署及备份目录:/home备份后的文件夹名称:my_app.bak2019-10-8-23:06:27------deploystart--------------cmd::mkdir-p/home/my_app/staticexitcmd::mkdir-p/home/my_app/static/cssexitcmd::mkdir-p/home/my_app/static/jsexitcmd::mkdir-p/home/my_app/static/mediaexitupload/home/my_app/asset-manifest.jsonupload/home/my_app/favicon.icoupload/home/my_app/index.htmlupload/home/my_app/logo192.pngupload/home/my_app/logo512.pngupload/home/my_app/manifest.jsonupload/home/my_app/precache-manifest.20dc8cb74286fd491ca0a9fc9b07234a.jupload/home/my_app/robots.txtupload/home/my_app/service-worker.jupload/home/my_app/static/css/main.2cce8147.chunk.cssupload/home/my_app/static/css/main.2cce8147.chunk.css.mapupload/home/my_app/static/js/2.222d1515.chunk.jsupload/home/my_app/static/js/2.222d1515.chunk.js.mapupload/home/my_app/统计ic/js/main.0782b2ff.chunk.jsupload/home/my_app/static/js/main.0782b2ff.chunk.js.mapupload/home/my_app/static/js/runtime~main.077bb605.jsupload/home/my_app/static/js/runtime~main.077bb605.js.mapupload/home/my_app/static/media/logo.5d5d9eef.svg------部署结束--------------在16.58秒内完成。Epilogue项目GitHub地址:https://github.com/hello-jun/deploy这个工具也可以单独使用。稍微修改一下,也可以用来自动部署reactnative项目。有兴趣的可以自己试试~欢迎star、留言、issue。希望这篇文章对您有所帮助。祝您工作生活愉快!