真相只有一个=>懒!!!css
想直接使用的, github 传送门 后面的能够不用看了- -,记得留个star,笔心
目前主要在作iot项目,因为历史缘由,平台还存在许多react/vue的纯H5子项目,这些项目又必须调用APP暴露的某些api,使得本地开发调试时不得不重复构建手动将build包部署到开发服务器上。几个项目,几轮转测试下来,很有些心累。所以想着能不能写个工具,在执行yarn build
构建打包后自动部署到服务器,这样就能够省去:打开ftp工具->寻找构建目录->寻找服务器上部署目录->备份->粘贴复制文件,这套繁琐手动流程,提升工(mo)做(yu)效率。
而且,有了这个后,jenkins自动构建时,项目配置的shell脚本也能少几行代码。html
首先,咱们先分析下要达到的结果:前端
执行yarn build
vue
我的喜欢用yarn
,用npm
的同窗能够本身替换。
假设项目都是基于create react app
||Vue cli
脚手架搭建的,若是是本身自定义的脚手架的话,请继续往下看。本文以 create react app脚手架为例
build
目录)copy全部文件并上传至远程服务器部署目录其次,分析如何达成结果:node
yarn build
之后自动触发,这里咱们须要用到npm scripts
中的钩子postbuild
,它会在执行build后作一些收尾工做,正好能够用来触发部署操做。对npm scripts
不太了解的同窗,能够参阅《阮一峰 npm scripts 使用指南》 ssh
链接,推荐使用第三方库ssh2 安装 ssh2 、momentreact
yarn add ssh2 moment
为了方便起见,在项目根目录下新建 deploy.js
。这个js文件就是本次编写的自动化部署工具git
deploy.js
不必定要在项目根目录下,若是你对node寻找路径方式比较熟悉,能够放在本身指定的路径下面
打开 package.json
文件,在 scripts
中添加:github
"postbuild": "yarn run deploy", "deploy": "node ./deploy.js",
此时,若是往 deploy.js
中添加 console.log('---deploy test--')
,控制台执行 yarn build
,能够看到,在构建完成后,会继续执行yarn run deploy
,最终控制台输出:shell
---deploy test--
const path = require('path') const moment = require('moment') const util = require('util') const events = require('events') const Client = require('ssh2').Client const fs = require('fs') /*********************************************************************************/ /******************************请手动配置如下内容*********************************/ /*********************************************************************************/ /** * 远程服务器配置 * @type {{password: string, port: number, host: string, username: string}} */ const server = { host: 'xxx.xxx.xxx.xxx', //主机ip port: 22, //SSH 链接端口 username: 'xxxx', //用户名 password: 'xxxxxxx', //用户登陆密码 } const baseDir = 'my_app'//项目目录 const basePath = '/home'//项目部署目录 const bakDirName = baseDir + '.bak' + moment(new Date()).format('YYYY-M-D-HH:mm:ss')//备份文件名 const buildPath = path.resolve('./build')//本地项目编译后的文件目录 /*********************************************************************************/ /**********************************配置结束***************************************/ /*********************************************************************************/ function doConnect(server, then) { const conn = new Client() conn.on('ready', function () { then && then(conn) }).on('error', function (err) { console.error('connect error!', err) }).on('close', function () { conn.end() }).connect(server) } function doShell(server, cmd, then) { doConnect(server, function (conn) { conn.shell(function (err, stream) { if (err) throw err else { let buf = '' stream.on('close', function () { 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) } }) }) } function doGetFileAndDirList(localDir, dirs, files) { const dir = fs.readdirSync(localDir) for (let i = 0; i < dir.length; i++) { const p = path.join(localDir, dir[i]) const stat = fs.statSync(p) if (stat.isDirectory()) { dirs.push(p) doGetFileAndDirList(p, dirs, files) } else { files.push(p) } } } function Control() { events.EventEmitter.call(this) } util.inherits(Control, events.EventEmitter) const control = new Control() control.on('doNext', function (todos, then) { if (todos.length > 0) { const func = todos.shift() func(function (err, result) { if (err) { then(err) throw err } else { control.emit('doNext', todos, then) } }) } else { then(null) } }) function doUploadFile(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) }) } }) }) } function doUploadDir(server, localDir, remoteDir, then) { let dirs = [] let files = [] doGetFileAndDirList(localDir, dirs, files) // 建立远程目录 let todoDir = [] dirs.forEach(function (dir) { todoDir.push(function (done) { const to = path.join(remoteDir, dir.slice(localDir.length + 1)).replace(/[\\]/g, '/') const cmd = 'mkdir -p ' + to + '\r\nexit\r\n' console.log(`cmd::${cmd}`) doShell(server, cmd, done) })// end of push }) // 上传文件 let todoFile = [] files.forEach(function (file) { todoFile.push(function (done) { const to = path.join(remoteDir, file.slice(localDir.length + 1)).replace(/[\\]/g, '/') console.log('upload ' + to) doUploadFile(server, file, to, done) }) }) control.emit('doNext', todoDir, function (err) { if (err) { throw err } else { control.emit('doNext', todoFile, then) } }) } console.log('--------deploy config--------------') console.log(`服务器host: ${server.host}`) console.log(`项目文件夹: ${baseDir}`) console.log(`项目部署以及备份目录: ${basePath}`) console.log(`备份后的文件夹名: ${bakDirName}`) console.log('--------deploy start--------------') doShell(server, `mv ${basePath}/${baseDir} ${basePath}/${bakDirName}\nexit\n`) doUploadDir(server, buildPath, `${basePath}/${baseDir}`, () => console.log('--------deploy end--------------'))
使用scripts
触发时,运行yarn build之后,会自动触发生命周期钩子 postbuild
,进行部署。此过程会先在本地构建打包项目至配置的buildPath
目录,而后在远程服务器xxx.xxx.xxx.xxx
的 /home
中将my_app
备份为my_app.bak2019-10-8-23:06:27
,最后将本地buildPath
目录文件所有上传到/home/my_app
,完成部署。npm
$ yarn run deploy $ node ./deploy.js --------deploy config-------------- 服务器host: xxx.xxx.xxx.xxx 项目文件夹: my_app 项目部署以及备份目录: /home 备份后的文件夹名: my_app.bak2019-10-8-23:06:27 --------deploy start-------------- cmd::mkdir -p /home/my_app/static exit cmd::mkdir -p /home/my_app/static/css exit cmd::mkdir -p /home/my_app/static/js exit cmd::mkdir -p /home/my_app/static/media exit upload /home/my_app/asset-manifest.json upload /home/my_app/favicon.ico upload /home/my_app/index.html upload /home/my_app/logo192.png upload /home/my_app/logo512.png upload /home/my_app/manifest.json upload /home/my_app/precache-manifest.20dc8cb74286fd491ca0a9fc9b07234a.js upload /home/my_app/robots.txt upload /home/my_app/service-worker.js upload /home/my_app/static/css/main.2cce8147.chunk.css upload /home/my_app/static/css/main.2cce8147.chunk.css.map upload /home/my_app/static/js/2.222d1515.chunk.js upload /home/my_app/static/js/2.222d1515.chunk.js.map upload /home/my_app/static/js/main.0782b2ff.chunk.js upload /home/my_app/static/js/main.0782b2ff.chunk.js.map upload /home/my_app/static/js/runtime~main.077bb605.js upload /home/my_app/static/js/runtime~main.077bb605.js.map upload /home/my_app/static/media/logo.5d5d9eef.svg --------deploy end-------------- Done in 16.58s.
项目GitHub地址: https://github.com/hello-jun/deploy此工具也能够单独使用,稍加改造后,也能够用来自动部署react native项目,有兴趣的能够本身尝试~欢迎star、留言、issue。但愿本文对各位有所帮助,祝工做生活愉快!